Hướng dẫn tạo app django - P3
Bài đăng này đã không được cập nhật trong 5 năm
Phần 3 - Tạo views, templates. Chuyển đổi view sang class-based View
- Note lại từ tài liệu học django 3.0 https://docs.djangoproject.com/en/3.0
Lifecycle
Thêm views
views.py:
from django.http import HttpResponse
from django.template import loader
from django.shortcuts import get_object_or_404, render
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
template = loader.get_template('polls/index.html')
context = {
'latest_question_list': latest_question_list,
}
return HttpResponse(template.render(context, request))
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):
return HttpResponse("You're voting on question %s." % question_id)
Thêm routes
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('<int:question_id>/', views.detail, name='detail'),
path('<int:question_id>/results/', views.results, name='results'),
path('<int:question_id>/vote/', views.vote, name='vote')
]
Thêm template
Mặc định template đặt trong polls/templates/polls
polls/templates/polls/index.html:
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
polls/templates/polls/detail.html:
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
Muốn thay đổi đường dẫn template => mysite/settings.py Chưa xác định ????
=> 3 kiểu truyền url:
- Kiểu cục súc:
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
- Kiểu truyền tên của url: trong file
polls/urls.pycó đặt tênname='detail'=> tên làdetail:
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
- Kiểu truyền cả namespace
pollsvà tên urldetail:
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
urls.py: thêm app_name = 'polls'
get_object_or_404 và render đều là shortcut. Cách viết khác :
views.py
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
template = loader.get_template('polls/index.html')
context = {'latest_question_list': latest_question_list}
return HttpResponse(template.render(context, request))
def detail(request, question_id):
try:
question = Question.objects.get(pk=question_id)
except Question.DoesNotExist:
raise Http404("Question does not exist")
return render(request, 'polls/detail.html', {'question': question})
Series django first project
Thêm form chức năng Vote
polls/templates/polls/detail.html:
<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>
polls/views.py:
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
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,))
Chức năng view result
polls/views.py:
def results(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/results.html', {'question': question})
polls/templates/polls/results.html:
<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>
Nếu có 2 người cùng submit chính xác cùng 1 thời điểm => kết quả vote chỉ tăng lên 1 đơn vị (expected là 2) => Tìm hiểu https://docs.djangoproject.com/en/3.0/ref/models/expressions/#avoiding-race-conditions-using-f
class-based view
Các views flow ở trên đều hoạt động theo spec rất cơ bản, chung chung đó là nhận request với tham số, sau đó truy vấn và lấy template trả về response. Django giúp chuyển đổi flow này(Function Based Views) thành 1 dạng khác(Class Base View):
- chuyển đổi URLconf.
- xóa bớt các views không cần thiết.
- tạo các views dựa trên Django’s generic views.
Chỉnh URLconf polls/urls.py:
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'),
]
Chỉnh views: polls/views.py
from django.http import 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
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
"""Return the last five 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):
... # same as above, no changes needed.
generic.ListView,generic.DetailViewlà những class trừu tượng hóa quá trình xử lý đối tượng cụ thể, thực hiện hầu hết các xử lý cơ bản như gọi chung là Class Base Viewtemplate_name = polls/details.html: chỉ định tên template thay vì mặc định ("polls/question_detail.html")context_object_name = 'latest_question_list'chỉ định tên biến trả về cho template, nếu không trong ngũ cảnh này tên biến mặc định sẽ làquestion_listget_queryset: có sẵn 1 đống các methods trợ giúp truy vấn gọi làqueryset, trong trường hợp này là ghi đè method để lấy ra 5 question theo ngày pub_date.
All rights reserved