./20170819-1303-cet-6-1.png
- This tutorial focuses on how to do simple form processing and cutting down the current codes.
./20170819-1303-cet-6-2.png
./20170819-1303-cet-6-3.png
{% csrf_token %}
is used to prevent the Cross Site Request Forgeries.
- Cross Site Request Forgeries is a common hacker method to forge form in a website.
./20170819-1303-cet-6-4.png
./20170819-1303-cet-6-5.png
./20170819-1303-cet-6-6.png
- The
{{ forloop.counter }}
refer to how many loop has been passed through.
./20170819-1303-cet-6-7.png
./20170819-1303-cet-6-8.png
- This is the overall codes for the newly updated view for the detail page.
./20170819-1303-cet-6-9.png
- Example to loop after foreign key list.
./20170819-1303-cet-6-10.png
- The most common method for request is
GET
and POST
.
- In form usually the request when submitting the form is
POST
.
- For every codes that can alter data in server - side should all go with
POST
request.
- This practice is not specific to Django but to general web development.
./20170819-1303-cet-6-11.png
- What is Cross Site Request Forgeries?
- This is the definition I can find in the Wikipedia page about Cross Site Request Forgeries, "Cross-site request forgery, also known as one-click attack or session riding and abbreviated as CSRF (sometimes pronounced sea-surf) or XSRF, is a type of malicious exploit of a website where unauthorized commands are transmitted from a user that the web application trusts. Unlike cross-site scripting (XSS), which exploits the trust a user has for a particular site, CSRF exploits the trust that a site has in a user's browser.".
- I thought this is an another name for XSS (Cross Site Scripting). However, based from the Wikipedia page XSS and Cross Site Request Forgeries are different.
- XSS is between a user and a website.
- Cross Site Request Forgeries is between a web browser and a website.
- Here is the Wikipedia page about Cross Site Request Forgeries, https://en.wikipedia.org/wiki/Cross-site_request_forgery.
./20170819-1303-cet-6-12.png
./20170819-1303-cet-6-13.png
- The
"choice"
from _request.POST["choice"]
is the name
of the radio input button.
- The value of
_request.POST["choice"]
is the value
attribute from the HTML tag of the corresponding radio button.
./20170819-1303-cet-6-14.png
- There was an error of which my returned value is
"on"
instead of the id of the choice.
- I forgot to put
value
attribute there.
./20170819-1303-cet-6-15.png
- For example the
request.POST["choice"]
will return the value
(HTML attribute) from an input component with name
(also HTML attribute) of "choice"
.
./20170819-1303-cet-6-16.png
- The
request.POST
values are always string.
./20170819-1303-cet-6-17.png
- Based from the screenshot above it seems that using
request.GET
will also works fine.
- However, the safety net is there when using
request.POST
to make sure the data will only be altered via a POST
call.
./20170819-1303-cet-6-18.png
./20170819-1303-cet-6-19.png
- The
request.POST["choice"]
will raise KeyError
(default error when specific key is not found in a Python's dictionary) if the choice is not provided from the POST data.
- The codes will check for
KeyError
and return back the question's detail page.
./20170819-1303-cet-6-20.png
- In this case the
HttpResponseRedirect
is used rather than HttpResponse
.
- This is the best practice in web development, to redirect everything after a form has been processed.
./20170819-1303-cet-6-21.png
./20170819-1303-cet-6-22.png
reverse()
here is to construct valid URL from the list in the urls.py.
- I think in most Django related works the
reverse()
will always be used to refer to a routing instead of hard - coded the link.
- Hence, if the link changed, it can still be accessed with the same variable but with different routing.
./20170819-1303-cet-6-23.png
./20170819-1303-cet-6-24.png
./20170819-1303-cet-6-25.png
- Example of
HttpRedirect
with reverse()
.
- The
reverse()
shown here is wrong because reverse()
actually takes 2 parameters.
- Here is the codes for the right use of
reverse()
.
return HttpResponseRedirect(reverse("polls:results", args=(question.id,)))
- The
args
is an iterable tuple
hence the ending comma (",") is a necessary.
- Because in this case, if there is no comma ending, the
args
will be filled with an int
.
./20170819-1303-cet-6-26.png
./20170819-1303-cet-6-27.png
- There is this nice functionality of DjangoTemplates that will give automatic suffix "s" for every list with more than one elements.
{{ choice.votes|pluralize }}
./20170819-1303-cet-6-28.png
- The small problem is that, if there are 2 people submit vote at the exact same time, for any people there are, the vote will only be counted as 1 vote.
./20170819-1303-cet-6-29.png
- I always write
Choice.DoesNotExists
and it returns error. I took me a while to notice it is actually Choice.DoesNotExist
.
./20170819-1303-cet-6-30.png
./20170819-1303-cet-6-31.png
- There was an error as well when I forget to put
value
attribute in <input type="radio" ...
.
- On the other hand, input radio button is grouped with the HTML attribute
name
.
./20170819-1303-cet-6-32.png
./20170819-1303-cet-6-33.png
- There is this generic views that will make less code for the same principle to display object or a list of object.
./20170819-1303-cet-6-34.png
- The three steps to add generic views system to the current views.
./20170819-1303-cet-6-35.png
- The
DetailView
needs to have pk
as an argument listed as in the URL routings.
- The screenshot above shows the example codes.
./20170819-1303-cet-6-36.png
./20170819-1303-cet-6-37.png
- The code to use generic views system in Django.
- Usually it is
views.results
now it changed into views.ResultsView.as_view()
.
- The
ResultView
itself is a class and not a function like it is in results()
.
ResultView
is a class inherited from generic.DetailView
.
- The
generic
is imported from from django.views import generic
.
./20170819-1303-cet-6-38.png
- By default the
DetailView
will look for a template named after app_name/model_name_detail.html
.
- However it can be altered with explicitly mention the name of the template the generic views will use.
- For such,
template_name
variable can be overwritten with the relative location of the template.
./20170819-1303-cet-6-39.png
- In all, each generic views in Django already know which cell/document/list that needs to be called when the document loads.
- Here is the final codes for polls/urls.py.
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views import generic
from .models import Choice, Question
# Create your views here.
class IndexView(generic.ListView):
template_name = "polls/index.html"
context_object_name = "latest_question_list"
def get_queryset(self):
""" Return the last 5 published questions. """
return Question.objects.order_by("-pub_date")[:5]
class DetailView(generic.DetailView):
model = Question
template_name = "polls/detail.html"
class ResultsView(generic.DetailView):
model = Question
template_name = "polls/results.html"
def vote(_request, _question_id):
question = get_object_or_404(Question, pk=_question_id)
print("="*50)
print("="*50)
print(_request.POST)
print(_request.POST["choice"])
print(question.choice_set.all())
print(question.choice_set.all()[0].id)
print(question.choice_set.all()[0]._meta.pk.name)
print(Question.objects.get(pk=2))
print(Choice.objects.all())
print(Choice.objects.all()[0]._meta.pk.name)
print(Choice.objects.all()[0].id)
print(Choice.objects.all()[1].id)
print(Choice.objects.get(pk=2))
print(Choice.objects.get(pk=3))
print("*"*50)
print(question.choice_set.all())
print(question.choice_set.all()[0])
print(question.choice_set.all()[1])
print(question.choice_set.all()[0].id)
print(question.choice_set.all()[1].id)
print("*"*50)
#print(question.choice_set.get(pk=1))
#print(question.choice_set.get(pk=2))
print("="*50)
print("="*50)
try:
selected_choice = question.choice_set.get(pk=_request.POST["choice"])
except (KeyError, Choice.DoesNotExist):
# Redisplay the question voting form.
return render(_request, "polls/detail.html", {
"question": question,
"error_message": "you did not select a choice"
})
else:
selected_choice.votes += 1
selected_choice.save()
"""
Always return an HttpResponseRedirect after successfully dealing with POST
data. This prevents data from being posted twice if the user hits back
button.
"""
return HttpResponseRedirect(reverse("polls:results", args=(question.id,)))
./20170819-1303-cet-6-41.png