Sử dụng Generic View trong Python Django

Nội dung

Bài này sẽ giới thiệu những nội dung sau:

  • Generic View là gì?
  • Sử dụng ListView và DetailView
  • Cách gọi Generic View tối giản hơn

Generic View là gì?

Viết những ứng dụng web có thể có những thứ không thay đổi, và từ đó ta sẽ lặp lại những pattern liên tục… Django cố gắng xử lý những đơn điệu đó tại pattern model và template (view), nhưng lập trình viên website vẫn phải mệt nhọc xử lý tạo tầng view (controller).
Generic View được phát triển để giải quyết vấn đề đó. Người ta đã thu gom những cái chung của những kiến thức và mô hình ở view (controller) để phát triển và abstract chúng sao cho chúng ta có thể viết xử lý data trong views mà không cần viết code quá nhiều.
Chúng ta sẽ phát hiện ra có những task khá chung chung với nhau như: hiển thị list trong object, hiển thị thông tin chi tiết,…


Sử dụng ListView và DetailView

Bây giờ tại views của app blog, Kteam sẽ thay thế hàm list bằng class ListView, hàm post bằng class DetailView.
Ta sửa lại views.py như sau:

from django.shortcuts import render
from .models import Post
from django.views.generic import ListView, DetailView
# Create your views here.

class PostListView(ListView):
    queryset = Post.objects.all().order_by('-date')
    template_name = 'blog/blog.html'
    context_object_name = 'Posts'
    paginate_by = 10

class PostDetailView(DetailView):
    model = Post
    template_name = 'blog/post.html'
  • import class ListView và DetailView.
  • khai báo class PostListView kế thừa ListView. Ta dùng queryset để lưu data truy vấn thay vì model (vì model sẽ thấy data từ trên xuống trong khi Kteam muốn lấy data theo thời gian từ cũ đến mới). Khai báo template_namekey của querysetpaginate_by là để phân trang, nghĩa là hiển thị ở template 10 bài đầu, rồi người dùng nhấn next thì mới xuất hiện 10 bài cũ hơn.
  • khai báo PostDetailView kế thừa DetailView. Vì DetailView sẽ truy vấn bằng primary key của model nên ta chỉ cần khai báo model để Django biết để truy vấn. Ngoài ra khai báo template_name.

Tiếp theo, ta sẽ sửa lại ở urls.py:

from django.urls import path
from . import views

urlpatterns = [
   path('', views.PostListView.as_view(), name='blog'),
   path('<int:pk>/', views.PostDetailView.as_view(), name='post'),
]
  • vì là class nên không thể đưa class thẳng vào url, ta sẽ gọi function as_view của class.
  • ở PostDetailView thì class sẽ truy vấn bằng primary key, nên thay url id thành pk để path hợp lệ.

Vì ở ListView ta có phân trang, nên ta chỉnh template thêm phần pagination:

{% extends "pages/base.html" %}
 
{% block title %}Blog{% endblock %}
 
{% block content %}
{% for post in Posts %}
    <h4>{{post.date|date:"d-m-Y"}}<a href="/redirect?Id=bPkSKQqMZ9erlLi6p21Q4A%3d%3d" url 'post' post.id %}">{{post.title}}</a></h4>
    <img src="{{post.image.url}}" width="500px" height="300px" />
{% endfor %}
<br/>
{% if page_obj.has_next %}
    <a href="/redirect?Id=5X7PNpoYSAf8ixnSnRj01TqsLDVRgREOSN1%2f%2fTHd3cqOH%2fnBsmGomt6wNekyuxMZtv4DKMfpRucdayOMARv5iK897ba1V6oesyqoyKTuvIU%3d" theo</a>
{% endif %}
{% endblock %}

  • Ta thêm điều kiện là nếu còn tiếp thì sẽ hiển thị thêm tag a tiếp theo với đường dẫn hiện tại kèm theo tham số page bằng số trang tiếp theo

Giờ ta thử kiểm tra, (Kteam sẽ cố tình chuyển paginate_by xuống bằng 1 để có thể kiểm tra link nhấn bài tiếp theo):


Cách gọi Generic View tối giản hơn

Nếu các bạn nhớ bài trước Kteam đã hướng dẫn dùng thư viện để login và logout, bản chất nó cũng là generic view. Nếu các bạn nhớ cách viết thì ta có thể tối giản dòng code trên bằng cách viết như sau, tại urls.py app blog:

from django.urls import path
from .models import Post
from django.views.generic import ListView, DetailView

urlpatterns = [
   path('', ListView.as_view(
      queryset = Post.objects.all().order_by('-date'),
      template_name = 'blog/blog.html',
      context_object_name = 'Posts',
      paginate_by = 10)
      , name='blog'),
   path('<int:pk>/', DetailView.as_view(
      model = Post,
      template_name = 'blog/post.html'), 
      name='post'),
]

Leave a Reply