Django for Designers/Whats next: Difference between revisions

POST to index view
imported>Paulproteus
imported>Paulproteus
(POST to index view)
 
(14 intermediate revisions by 2 users not shown)
Line 1:
== Part 67: Exercises for the reader ==
 
=== Updating and deleting bookmarks (the last two parts of CRUD) ===
 
Let's make it possible to delete a bookmark. The changes we will make to support that are to add a delete button next to each bookmark, add a a view that handles that delete button, and hook all that up to urls.py.
 
To make the delete button appear, edit ''bookmark.html'' so that it has the following text, top to bottom. The only change we are making is the addition of a new div toward the bottom.
 
<source lang="html4strict">
<li>
<a class="bookmark-link" href="{{ bookmark.url }}">{% if bookmark.title %}{{ bookmark.title }}{% else %}{{ bookmark.url }}{% endif %}</a>
<div class="metadata"><span class="author">Posted by {{ bookmark.author }}</span> | <span class="timestamp">{{ bookmark.timestamp|date:"Y-m-d" }}</span>
{% if bookmark.tag_set.all %}| <span class="tags">
{% for tag in bookmark.tag_set.all %}
<a href="{% url 'bookmarks.views.tag' tag.slug %}">{{ tag.slug }}</a></span>
{% endfor %}
{% endif %}
</div>
{% if request.user.is_authenticated %}
<div class="actions"><form method="POST" action="{% url bookmarks.views.delete bookmark.id %}>
{% csrf_token %}
<input type="submit" value="Delete">
</form></div>{% endif %}
</source>
 
We added a very dense block there. Here is an explanation, piece-by-piece:
 
* It only renders if there is a user logged-in.
* It looks for a URL corresponding to a view (that we have not yet written) called bookmarks.delete.
* It passes the bookmark ID to that view as a URL parameter.
* It provides a CSRF token, and a submit button labeled ''Delete''.
 
To make that work, the next step to to take is to write our view. Open up ''bookmarks/views.py'' and add the following to the end of the file:
 
<source lang="python">
def delete(request, bookmark_id):
if request.method == 'POST':
b = get_object_or_404(Bookmark, pk=int(bookmark_id))
b.delete()
return redirect(index)
</source>
 
Namely, if the request is a POST, we try to find the corresponding bookmark and delete it. No matter what, we redirect back to the start.
 
Finally, we need to adjust urls.py to be aware of this view. To do that, open up ''urls.py'' in your editor, and add this line anywhere within the urlpatterns sequence (for example, underneath the final current URL pattern):
 
<source lang="python">
url(r'^delete/(\d+)/$', 'bookmarks.views.delete'),
</source>
 
(The (\d+) means, "any sequence of digits, 1 or longer, captured." Capturing it permits the variable to be passed to the Python view function.)
 
Save all of that, and now try loading up the home page. If you see a bookmark you want to delete, click the ''delete'' button. What should appear to happen is that the page reloads itself, and that bookmark is now gone.
 
Note that, at the moment, any user can delete any other user's bookmarks. If you want things to work differently, you'll need to add a check to the ''delete'' function in ''views.py''.
 
To be able to '''update''' bookmarks, we will need to create add an ''update'' link, make it go to a page where bookmarks can be edited, and then let that page be saved. (If we wanted to make this slicker, the first step would be to put the edit page in a form using e.g. [http://jqueryui.com/resources/demos/dialog/modal-form.html jQuery UI].)
 
We already have a form that lets us enter bookmarks, and we can re-use it as the editing area. As a reminder, open ''index.html'' and notice the following fragment.
 
<source lang="html4strict">
{% block bookmark_widget %}
{% if request.user %}
<div id="new-bookmark-widget">
<form method="post">
{% csrf_token %}
<h3>Bookmark</h3>
{{ form.as_p }}
<p><button id="new-bookmark-submit">Submit</button>Submit</button>
</form>
</div>
{% endif %}
{% endblock %}
</source>
 
Within that fragment, make ''one'' change. Change this:
 
<source lang="html4strict">
<form method="post">
</source>
 
to this:
 
<source lang="html4strict">
<form method="post" action="{% url bookmarks.views.index %}">
</source>
 
That way, it POSTs to the index view.
 
We will want the same widget in our new page. So we will extend ''index.html'' rather than extending the base. Create a new file called ''edit.html'' with the following contents:
 
<source lang="html4strict">
{% extends 'index.html' %}
 
{% block subheader %}Edit bookmark{% endblock %}
 
{% block content %}{% endblock %}
</source>
 
This way, we empty out the content section, but keep the rest of the page layout intact. (It would probably be even better to extract the bookmark_widget to a separate block, and then have edit.html extend base.html instead of index. But hey, this works!)
 
Make sure to inform git you'll want that file to be part of the next commit:
 
<source lang="bash">
# in django-for-designers/myproject
$ git add bookmarks/templates/edit.html
</source>
 
We'll need a view to render that form and to accept changes. So, open up ''views.py'' and add a new function to the end:
 
<source lang="python">
def edit(request, bookmark_id):
b = get_object_or_404(Bookmark, pk=int(bookmark_id))
context = {
'form': BookmarkForm(instance=b),
}
return render(request, 'edit.html', context)
</source>
 
In this snippet, we grab a bookmark by ID number instantiate a Django form based on, and pass that to the template.
 
Intriguingly, we do not need any special code to handle the submitted form. If it gets submitted to the index view, the existing machinery will handle it properly!
 
We still need to inform ''urls.py'' about the new view. To do that, open ''urls.py'' and add this line as the final line in the urlpatterns sequence:
 
<source lang="python">
url(r'^edit/(\d+)/$', 'bookmarks.views.edit'),
</source>
 
We do also need to change the base template so that it links to our edit functionality. Open ''base.html'' and find the following:
 
<source lang="html4strict">
{% if request.user.is_authenticated %}
<div class="actions"><form method="POST" action="{% url bookmarks.views.delete bookmark.id %}>
{% csrf_token %}
<input type="submit" value="Delete">
</form></div>{% endif %}
</source>
 
and replace it with:
 
<source lang="html4strict">
{% if request.user.is_authenticated %}
<div class="actions"><form method="POST" action="{% url bookmarks.views.delete bookmark.id %}>
{% csrf_token %}
<input type="submit" value="Delete">
</form> | <a href="{% url bookmarks.views.edit bookmark.id %}">edit</a></div>{% endif %}
</source>
 
Congratulations! You can now edit your bookmarks.
 
You'll do well to ''commit'' at this point.
 
<source lang="bash">
# in django-for-designers/myproject
$ git commit -a -m 'Update and delete functionality'
</source>
 
<!--=== Enabling the built-in admin ===
 
...needs to be written-->
 
=== Handling user-uploaded media ===
Line 285 ⟶ 443:
Before going forward, we should decide what kind of information we will export, and how we will export it.
 
For this example, since all the bookmarks on the site are public, we will provide a list of all bookmarks. We will let users of the API filter the bookmarks by the user, if they like. To keep things simple at first, we won't provide the bookmarks' tags.
 
==== Adding Tastypie as a dependency ====
Line 335 ⟶ 493:
Tastypie is based on "Resources"; you can read more about it in its [http://django-tastypie.readthedocs.org/en/latest/tutorial.html own tutorial]. In this example, we do just a handful of things:
 
* We limit users of the API to only be able to ''get'' from you, rather than use other HTTP verbs like PUT and DELETE to upload or modify data. (You can enable PUT and similar modification features yourself, but you will likely want to learn about authorization within Tastypie first. The simplest such mode is to enable [http://django-tastypie.readthedocs.org/en/latest/cookbook.html#using-sessionauthentication SessionAuthentication].)
 
* It will make available the ''queryset'' provided, which is all the bookmarks.
Line 375 ⟶ 533:
</source>
 
==== AddFiltering, theand abilityfurther to filter by userdirections ====
 
Our API lets us filter by any of the fields it returns. So, for example, you can filter the results by user by visiting:
 
http://localhost:8000/api/bookmark/?user__username=yourself&format=json
* Update Python code to export data via Tastypie
 
Doing this requires no changes to your code. Cool, huh?
* Demo that it works, in a browser
 
One further direction would be to summarize the tags as a list of strings, rather than as a list of tag URIs. You could look into the [http://django-tastypie.readthedocs.org/en/latest/cookbook.html#adding-custom-values dehydrate] method that Tastypie lets you override.
* git commit
 
==== Further directions ====
Anonymous user