Due: Wednesday Jan 28, 23:59pm.
This homework builds on homework 1, and asks you to improve the for-sale site we built. If you were not able to get homework 1 to work, hopefully the sample solutions you saw via CrowdGrader helped you.
Additional features
Users should be able to do the following:
The default view should return all unsold items. The SQLFORM.grid will let users sort them according to date posted, price, select for category.
At the bottom, there should be a button that:
The listing page should contain only the titles, prices, category, posting date, sold status (if you are viewing all items). A page with the listing details should contain all the rest of the information (chiefly the post content).
The fields variable of SQLFORM.grid lets you select which fields of the db.forsale table are visible in the grid. If you select details=True, the user will be able to go to a more detailed view where the user can see all details of a given listing (including contact information for the seller, image if any, etc).
The best way to implement this functionality consists in using an optional argument for the index page, which if present, instructs to display all items. The URL can look simply as /default/index/all
rather than /default/index
.
This is how you can retrieve the parameter:
show_all = request.args(0) == 'all'
Then, you need to build a query, which normally asks for the sold status to be False, except if the show_all flag is set. This is easy enough:
q = (db.forsale) if show_all else (db.forsale.sold == False)
We need to then pass this query to the SQLFORM.grid. We also need to tell SQLFORM.grid that, if show_all is set, the first argument of the URL should not be passed to SQLFORM.grid. This can be done as follows:
start_idx = 1 if show_all else 0
form = SQLFORM.grid(q, args=request.args[:start_idx], ...)
You can easily make the button via:
if show_all:
button = A('See unsold', _class='btn', _href=('default', 'index'))
else:
button = A('See all', _class='btn', _href=('default', 'index', args=['all']))
Implement this via the links feature of SQLFORM.grid. When you see listings, check if the logged in user is the author of the listing. If so, add buttons to delete, edit, and toggle the sold/unsold status.
These buttons should direct to URLs that are protected with user_signature=True, as in:
URL('default', 'toggle_sold', args=[r.id], user_signature=True)
In this way, the toggling can be performed without further checks, as in:
@require_signature
@auth.requires_login()
def toggle_sold():
item = db.forsale(request.vars(0)) or redirect(URL('default', 'index'))
item.update_record(sold = not item.sold)
redirect(URL('default', 'index')) # Assuming this is where you want to go
where update_record is documented here.
For editing an item, you can use SQLFORM, (examples have been given in chapter 3 and in the bulletin board app).
Deleting an item requires a bit more care, as it is best done by first asking confirmation that you really want to delete it. The simplest way is to use a confirmation form, and do:
@require_signature
@auth.requires_login
def delete_item():
item = db.forsale(request.vars(0)) or redirect(URL('default', 'index'))
form = FORM.confirm('Are you sure?')
if form.accepted:
db(db.forsale.id == item.id).delete()
redirect(...)
return dict(form=form)
You can of course improve on this code. It would be best to display not only the confirmation prompt, but also the full listing being deleted.
default/index
. This will make testing easier.If you cannot login, it is most likely because you are trying to access CrowdGrader using some other account (e.g. a @gmail.com account). Select login with another account. You might have to use an incognito window if Google insists in logging you in with the wrong account. Sorry.
As part of this assignment, I am asking you to review 4 other submissions by next Wednesday. A detailed grading rubric is available on CrowdGrader. Go to the assignment URL to do the reviews.