In this assignment, you will expand your classified ads web site to add functionality equivalent to:
You can log into this site using an account: facebook and a password of Marketnn! where "nn" is the two-digit number of Dr. Chuck's race car or the numeric value for asterisk in the ASCII character set.
We will add a favoriting capability to your previous milestone by borrowing more parts and pieces from the code that runs:
At this point all of the challenges should be working - not all will be tested by the autograder - but we will separately check them.
(1) Make yourself a gravatar at https://en.gravatar.com/ - it is super easy and you will see your
avatar when you log in to your application and elsewhere with gravatar enabled apps. The gravatar can be
anything you like - it does not have to be a picture of you. The gravatar is associated with an email address
so make sure to give an email address to the user you create with createsuperuser.
(2) Change your home/static/favicon.ico to a favicon of your own making. I made my favicon
at https://favicon.io/favicon-generator/ - it might not change instantly after you update the favicon
because they are cached extensively. Probably the best way to test is to go right to the favicon url
after you update the file and press 'Refresh' and/or switch browsers. Sometimes the browser caching
is "too effective" on a favicon so to force a real reload to check if the new favicon is really being served
you can add a GET parameter to the URL to force it to be re-retrieved:
https://market.dj4e.com/favicon.ico?x=42
Change the x value to something else if you want to test over and over.
(3) Make social login work. Take a look at
github_settings-dist.py, copy it into
market/config/github_settings.py and go through the process on github to get your client ID and
secret. The documentation is in comments of market/config/github_settings.py.
To get your key and secret from github, go to: https://github.com/settings/developers and add a new OAuth2 application. Here are some sample settings:
Application name: ChuckList PythonAnywhere
Homepage Url: https://drchuck.pythonanywhere.com
Application Description: Some pithy words...
Authorization callback URL: https://drchuck.pythonanywhere.com/oauth/complete/github/
You can register two applications with github - one on localhost and one on PythonAnywhere. If you are
using github login on localhost - make sure that you register http://127.0.0.1:8000/ instead
of http://localhost:8000/ and use that in your browser to test your site. If you
use localhost, you probably will get an error message when you login like:
The redirect_uri MUST match the registered callback URL for this application.
In this section, you will pull bits and pieces of the favwc sample application
into your mkt application to add support for logged in users to "favorite" and "un-favorite"
ads. We will also create a custom web component to implement this favorites feature.
(1) Add this to your mkt/models.py, taking inspiration from dj4e-samples/favwc/models.py
class Ad(models.Model) :
...
# Favorites
favorites = models.ManyToManyField(settings.AUTH_USER_MODEL,
through='Fav', related_name='favorite_ads')
...
class Fav(models.Model) :
ad = models.ForeignKey(Ad, on_delete=models.CASCADE)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
# https://docs.djangoproject.com/en/4.2/ref/models/options/#unique-together
class Meta:
unique_together = ('ad', 'user')
def __str__(self) :
return '%s likes %s'%(self.user.username, self.ad.title[:10])
(2) Do python manage.py makemigrations and python manage.py migrate once
you have modified the model.
(3) Edit your mkt/admin.py and add the Fav model to the /admin UI so you can
look at Favs in the Admin user interface.
(4) Look at how ThingListView from dj4e-samples/favwc/views.py
retrieves the list of favorites for the current user and add code
to your AdListView to retrieve the favorites for the current logged in user.
(5) If the market/site/wc/dj4e-favstar.js file does not exist, create the market/site/ and market/site/wc folders and the
file market/site/wc/dj4e-favstar.js and add the following Javascript code for our custom web component:
import { html, LitElement } from "https://cdn.jsdelivr.net/npm/lit@3.2.1/+esm";
export class DJ4EFavoriteStar extends LitElement {
static properties = {
fav: { type: Boolean },
};
// Don't use Shadow-DOM
createRenderRoot() { return this; }
render() {
return html`
<span class="fa-stack" style="vertical-align: middle;">
<i class="fa fa-star fa-stack-1x"
style="${this.fav ? "" : "display: none;"} color: orange;">
</i>
<i class="fa fa-star-o fa-stack-1x"></i>
</span>
`
}
}
customElements.define('dj4e-favstar', DJ4EFavoriteStar);
(6) Alter your list.html by looking through favwc/templates/favwc/list.html. Make sure to add the
dj4e-favstar web component in the list of ads, to show the stars based on the list of favorites for the current logged in user
and the favToggle() function and web component module include JavaScript (as shown below):
{% extends "base_menu.html" %}
{% block content %}
...
<script> <!-- add this -->
function favToggle(element, url) {
console.log('POSTing to', url);
fetch(url, { method: 'POST', body: '{}' } )
.then((response) => {
if (!response.ok) { // Check if the response status is in the 2xx range (success)
console.log("If you see this error, it is likely in ToggleFavoriteView()");
console.log("Note: It is easier to diagnose this error FireFox.");
throw new Error(`AJAX/fetch error status: ${response.status}, see developer console.`)
}
return response.text();
})
.then((response) => {
console.log(url, response);
element.toggleAttribute('fav');
}).catch((error) => {
alert(`Url ${url} failed ${error}`);
});
}
</script>
<script type="module" src="/site/wc/dj4e-favstar.js"></script>
{% endblock %}
...
(6) At this point, you can reload your application and go to the main page with all of your
ads listed, each ad should have a blank star and if you click on the star, it will change
color, but if you refresh the page, the favorites will not be "sticky" because you are missing the server code to store the favorite when the key is pressed. The JavaScript is running
favPost() but the server code is failing and returning an error to your application.
You can view developer console in your browser and you will see a 404 not found
on a url that looks like ad/42/toggle. We need to add a route and a view for that url
pattern so that the favorites can be stored.
(7) Add the following route to your urls.py for the favorites feature:
...
path('ad/<int:pk>/toggle', views.ToggleFavoriteView.as_view(), name='ad_toggle'),
...
(8) Pull in and adapt ToggleFavoriteView
from dj4e-samples/favwc/views.py into your views.py. This view will be called using AJAX in the favToggle() function
above. Edit this view carefully - if you make a mistake in this method, it will not be shown on the screen like
your other views. The best way
to debug it is to (a) run your application, (b) open the developer console, (c) press the favorite star, (d) check the
console output for any errors, (e) check the network traffic tab in the developer console for a URL that returns an error,
and then (f) you look a the detail of the error in the http response in the network tab of the developer console.
Here is an example screen shot of a JavaScript Error in the developer console.
Here is an example screen shot of a View Error Traceback in the developer console.
By this point in the course, you should already have the earlier assignments working correctly. While it’s fine to use AI to help diagnose specific errors, one of the worst approaches is to take a file you’ve been developing for weeks, hand it to an AI, and paste back a completely rewritten version without reviewing it carefully.
AI-generated solutions often “work” in the sense that they run without crashing, but they may remove or alter details the autograder expects. This can cause confusing autograder failures that are very hard to untangle.
There are many ways to solve these assignments that are roughly equivalent, but the autograder is not checking for just any equivalent solution. It's verifying that you’ve adapted the provided sample code in the expected ways.
If AI produces a solution that is only “approximately” like the samples, it probably won’t pass. And if AI overwrites working code you already built and have working perfectly, you may find yourself needing to start over from the beginning.
It is always a good idea to manually test your application before submitting it for grading. Here are a set of manual test steps:
The most common problem is that when you click on the star it looks good on the screen but the fact that this is not a favorites (or not) did not get recorded in the server. Often you will need to check the developer network console in your browser to find errors in the AJAX code.
This is the first time you are using AJAX so some of the errors will only be seen in the developer
console. If your favoriting code breaks - you won't see the errors on the main screen. Go into the
developer console, under the network tab and watch for the AJAX (also known as XHR) calls. Some will
fail with errors like 404 or 500 and - if you select the request that is in error and look at
the Response tab you will usually see what is going wrong in the server.
Sometimes the AJAX errors are a little difficult to see when using the Chrome browser. The developer console in FireFox renders the actual HTML to make it easier to read.
Make sure to turn off your ad blocker. Take a look at your web developer console if the AJAX part of favorites seem to fail. You might see a message like:
POST .. net::ERR_BLOCKED_BY_CLIENT
Or a similar message - this means your JavaScript tried to do an AJAX request and was stopped by the browser.