+1

Hướng dẫn tạo app django - P3

Phần 3 - Tạo views, templates. Chuyển đổi view sang class-based View


Lifecycle

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

  1. 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 %}
  1. 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.py có đặt tên name='detail' => tên là detail:
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
  • Kiểu truyền cả namespace polls và tên url detail:
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

urls.py: thêm app_name = 'polls'


get_object_or_404render đề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):

  1. chuyển đổi URLconf.
  2. xóa bớt các views không cần thiết.
  3. 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.DetailView là 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 View
  • template_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_list
  • get_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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí