We'll continue with the Django tutorial from the last time (https://qiita.com/shitikakei/items/753ba603b5bca272c69c). This time around, class-based views are an important part of using Django.
<h1>{{ question.question_text }}</h1>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
{% for choice in question.choice_set.all %}
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">
<label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
{% endfor %}
<input type="submit" value="Vote">
</form>
For example, suppose you want to create a form like this.
It is a form that prepares an answer with a radio button for a certain question, checks it, and sends it by POST.
{{forloop.counter}}
is the number of loops for {% for choice in question.choice_set.all%}
.
The actual notation would be, for example, <input type =" radio "name =" choice "id =" choice1 "value =" 1 ">
.
Let's take a look at the view for this.
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from .models import Choice, Question
# ...
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
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 didn't select a choice.",
})
else:
# selected_choice.votes += 1
#The above code causes conflict problems, so F(Column name)Use the method.
selected_choice.votes = F('votes') + 1
selected_choice.save()
# Always return an HttpResponseRedirect after successfully dealing
# with POST data. This prevents data from being posted twice if a
# user hits the Back button.
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
What has changed compared to the previous time is that the HttpResposeRedirect
and reverse
objects have been imported.
The former is a method for redirecting, and the latter reverse ()
method is a method that returns a URL and calls the URL from the named path set in urls.py. For example, in the above case
The URL / polls / results
is returned, but you can return the URL / polls / 1 / results
by specifying question.id as an argument.
If you understand the contents of the vote function itself up to the last time, it is not so difficult to understand what you are doing.
First, assign an instance to the variable. Since it is a get_object_or_404
method, it is an instance of the corresponding primary key data from the Question
model.
Exception handling it with the try-catch-else
pattern.
First, based on the POST data sent from the form, get the data from the database with the get ()
method.
request.POST
allows you to access the data sent with the specified key.
For example, in this case request.POST ['choice']
, it means to access the data whose name
attribute is choice
.
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">
It will be this part.
So, this time it is a form with a radio button, so there are some choices in the for
loop, but since there is only one data to be sent, the value of the choice's value
is taken out and made into pk
. Become.
By the way, if there are multiple same name
attributes in checkboxes etc., please get them withgetlist ()
instead ofget ()
.
Quiet talk, for example, question.choice_set.get (pk = request.POST ['choice'])
becomes question.choice_set.get (pk = 1)
, that is, data with primary key 1 is choice
It will be searched from the model and assigned to the variable as an instance.
If it can be obtained safely, it will jump to ʻelse, if it cannot be obtained, it will be treated as an exception of
KeyError, and the error message will be returned by the
render () method and returned to the form screen. The process of ʻelse
is to increase the number of votes, update it to the database, and redirect to the vote result screen.
Now let's create a template for voting results.
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>
<a href="{% url 'polls:detail' question.id %}">Vote again?</a>
The vote {{choice.votes | pluralize}}
part is the expression that if the value to the left of pluiralize
is plural, it returns a suffix that represents the plural.
For example, in this case, if choice.votes
is 2, then vote
becomes votes
.
By the way, I've written various views (controllers)
so far, but Django
has a built-in so-called general-purpose view called a class-based view.
view
has the role of bridging data and ʻURL` to the template, but even though it has come so far
-Create and display a list with filter ()
-Obtain data from the database and reflect it in the template
· Display one of many pages like polls / 1 / results
I think that such a process came out many times and I wrote view
each time.
A general-purpose view is a system that sets appropriate default values according to the model and form in advance so that you do not have to write such processing many times.
By the way, as you can see from the fact that it is class-based, the generic view in Django
is provided by the class.
It seems that there is also a function base.
Let's take a look at the previous view
.
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
# Create your views here.
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
context = {
'latest_question_list': latest_question_list,
}
return render(request, 'polls/index.html', context)
def detail(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/detail.html', {'question': question})
def results(request, question_id):
response = "You`re looking at the results of question %s."
return HttpResponse(response % question_id)
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
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 didn't select a choice.",
})
else:
selected_choice.votes = F('votes') + 1
selected_choice.save()
# Always return an HttpResponseRedirect after successfully dealing
# with POST data. This prevents data from being posted twice if a
# user hits the Back button.
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
Next, here is the new view
to write
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.views import generic
from .models import Question, Choice
# Create your views here.
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
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)
try:
#Access POST data. This time, the ID of the option selected by the radio button of the form is returned as a character string and assigned to the variable.
selected_choice = question.choice_set.get(pk=request.POST['choice'])
#Specify the Key error in the exception specification. Exception handling if there is no choice.
except(KeyError, Choice.DoesNotExist):
return render(request, 'polls/detail.html', { 'question':question, 'error_message':"You didn`t select a choice.",})
else:
selected_choice.votes = F('votes') + 1
selected_choice.save()
#Redirect processing in Django. reverse()In urls.Call the URL from the named path set in py. You can specify a specific page by specifying the argument.
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
You can see that it is quite different.
First, import the generic
object from the django.views
module.
It can also be imported and used for each class-based view, such as from django.views.generic import ListView
.
In that case, the argument would be ListView
instead of generic.ListView
.
And as mentioned above, since it is a class-based view, we will rewrite what was written as a function into a class and inherit each class-based view and use it.
For example, it's a part like class IndexView (generic.ListView):
. Since it inherits, specify the general-purpose view you want to use as an argument.
Of course, inheriting can also be overridden.
Let's look at each one.
Listview
Listview
is a view for displaying a list of objects. In other words, it can be used when creating a list page.
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
return Question.objects.order_by('-pub_date')[:5]
When you actually use it, it will be written as above.
Normally, it is essential to specify the model when using a class-based view, but you can also specify the method of queryset ()
.
For example, in the above, instead of simply displaying the list of models, we want to display the list in increments of 5, so we are specifying with query set ()
instead of specifying the model.
After that, specify the template of the bridging destination with template_name
, and decide the variable name of the object at the bridging destination with context_object_name
.
Aside from the former, I have to decide the latter
{% for question in latest_question_list %}
Where I was writing like this
{% for question in object_list %}
If you do not write it, it will not work.
It goes without saying which one is easier to understand at first glance.
By the way, by using a mechanism called Context Processor
, you can define a template variable in a separate file and import it so that you do not have to specify it individually here.
It can also be used like a global variable in a template.
Reference: Introducing Google Analytics to Django
DetailView
DetailView
is a class-based view that can be used for individual detail pages.
The view you want to use for pages like polls / 1 / detail
.
class DetailView(generic.DetailView):
model = Question
template_name = 'polls/detail.html'
You can see that we are specifying the model and the template.
In addition to this, you have to specify pk
or slug
in ʻurls.py, but this time, specify
pk` after this.
These are the two class-based views used in this tutorial, but there are several others, so this time we'll just take a quick look at what they have.
generic.base.View
This is the main class of class-based view. Each class base inherits this, and by inheriting it, we are using the class base.
Example of use
from django.http import HttpResponse
from django.views import View
class MyView(View):
def get(self, request, *args, **kwargs):
return HttpResponse('Hello, World!')
Both can be used for forms, resulting in views with update processing.
The role of FormView
is to display the form when it receives the HTTP method GET
, that is, to display the form when the ʻURL set in
pathis hit. Then, when you receive the
HTTP method POST, you can execute the process based on the
requestreceived from the form. It seems to be used for forms that perform redirect processing without additional database processing, For example, the login form given as an example has a separate view called the authentication view called
LoginView`, so it seems that the latter, which will be described later, is often used.
Example of use
from django import forms
class ContactForm(forms.Form):
name = forms.CharField()
message = forms.CharField(widget=forms.Textarea)
def send_email(self):
# send email using the self.cleaned_data dictionary
pass
Regarding CreateView
, the ʻinsert process is executed for the specified model based on the
requestreceived from the form when the
HTTP method POST` is received.
In other words, it can be used for forms that add new records (data).
Example of use
from django.views.generic.edit import CreateView
from myapp.models import Author
class AuthorCreate(CreateView):
model = Author
fields = ['name']
ʻUpdateView` is not an addition, but a view used for forms that involve updating existing records.
Example of use
from django.views.generic.edit import UpdateView
from myapp.models import Author
class AuthorUpdate(UpdateView):
model = Author
fields = ['name']
template_name_suffix = '_update_form'
Now you can see that the fields
variables are defined in ʻUpdateView and
CreateView. This is an item that must be set when using both views, and data input from the form will be ignored except for the specified fields. In other words, in the above example, no matter how many input items in the form correspond to, for example, ʻaddress
or Tel
, only the name
field will be added / updated.
This specification can be set in a list or tuple, so you can specify more than one.
Now that we've briefly covered the roles for each class base and confirmed how to define them, the next step is to modify ʻurls.py` so that it can use a generic view.
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
Earlier, you had to specify pk
to use DetailView
.
Make sure that <int: question_id>
is now <int: pk>
.
Finally, modify views.py
as described at the beginning of this item.
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.views import generic
from .models import Question, Choice
# Create your views here.
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
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)
try:
#Access POST data. This time, the ID of the option selected by the radio button of the form is returned as a character string and assigned to the variable.
selected_choice = question.choice_set.get(pk=request.POST['choice'])
#Specify the Key error in the exception specification. Exception handling if there is no choice.
except(KeyError, Choice.DoesNotExist):
return render(request, 'polls/detail.html', { 'question':question, 'error_message':"You didn't select a choice.",})
else:
selected_choice.votes = F('votes') + 1
selected_choice.save()
#Redirect processing in Django. reverse()In urls.Call the URL from the named path set in py. You can specify a specific page by specifying the argument.
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
I felt that understanding here is inevitable when dealing with Django.
Introducing Google Analytics to Django Introduction to class-based generic views in Django and sample usage Collect Django's generic class views and mention the implementation
Recommended Posts