× Giới thiệu Lịch khai giảng Tin tức Sản phẩm học viên

Xây dựng một blog từ đầu với Django

18/10/2023 01:21

Có rất nhiều nền tảng viết blog mà bạn có thể sử dụng ngay. Tuy nhiên, xây dựng blog của riêng bạn từ đầu bằng Django là một cách tuyệt vời để giữ quyền kiểm soát nội dung của bạn

Có rất nhiều nền tảng viết blog mà bạn có thể sử dụng ngay. Tuy nhiên, xây dựng blog của riêng bạn từ đầu bằng Django là một cách tuyệt vời để giữ quyền kiểm soát nội dung của bạn. Ngay cả khi bạn mới bắt đầu sử dụng Django, các tính năng thân thiện với người dùng của nó sẽ cho phép bạn tập trung vào việc thiết kế và đăng nội dung của mình.

Trong hướng dẫn này, bạn sẽ học cách:

  • Thiết lập dự án Django mới
  • Tạo và chỉnh sửa bài đăng trên blog
  • Hiển thị bài viết cho người dùng
  • Chỉ định danh mục cho bài viết
  • Cho phép người dùng bình luận về bài viết

Đồng thời, bạn sẽ tận dụng trang quản trị Django và khám phá cách làm việc với các biểu mẫu trong Django. Đây là một dự án lý tưởng để bạn bước chân vào thế giới Django, nhưng bạn cần có nền tảng vững chắc về Python cơ bản .

Khi kết thúc hướng dẫn này, bạn sẽ có thể chia sẻ kiến ​​thức của mình thông qua một blog đang hoạt động mà bạn đã xây dựng từ đầu. Nếu bạn tò mò về mã nguồn cuối cùng trông như thế nào thì bạn có thể nhấp vào liên kết bên dưới:

Thiết lập môi trường phát triển

Bất cứ khi nào bạn bắt đầu một dự án phát triển web mới, trước tiên bạn nên thiết lập môi trường phát triển của mình. Tạo một thư mục mới để dự án của bạn tồn tại và cdvào đó:

$ mkdir django-blog
$ cd django-blog

Khi đã vào trong django-blog/thư mục, bạn nên tạo một môi trường ảo để quản lý các phần phụ thuộc. Chọn hệ điều hành của bạn bên dưới và sử dụng lệnh dành riêng cho nền tảng của bạn để thiết lập môi trường ảo:

PS> python -m venv venv

Lệnh này sẽ tạo một venv/thư mục trong thư mục làm việc của bạn. Trong thư mục này, bạn sẽ tìm thấy một số tệp, bao gồm bản sao của thư viện chuẩn Python. Sau này, khi bạn cài đặt các phần phụ thuộc mới, thư mục này cũng sẽ lưu trữ chúng. Tiếp theo, bạn cần kích hoạt môi trường ảo bằng cách chạy lệnh sau:

PS> .\venv\Scripts\activate
(venv) PS>

Với các lệnh trên, bạn tạo và kích hoạt một môi trường ảo được đặt tên venvbằng cách sử dụng mô-đun tích hợp sẵn của Python venv. Dấu ngoặc (venv)đơn phía trước lời nhắc cho biết bạn đã kích hoạt thành công môi trường ảo.

Bây giờ bạn đã tạo và kích hoạt môi trường ảo, đã đến lúc cài đặt Django. Bạn có thể làm điều này bằng cách sử dụng pip:

(venv) $ python -m pip install Django

Sau khi thiết lập môi trường ảo và cài đặt Django, bây giờ bạn có thể bắt đầu tạo dự án Django của mình.

Bắt đầu dự án Django của bạn

Ứng dụng web Django được tạo thành từ một dự án và các ứng dụng . Dự án Django chứa một số cấu hình áp dụng cho toàn bộ ứng dụng web, chẳng hạn như cài đặt dự án, URL, mẫu chia sẻ và tệp tĩnh. Mỗi ứng dụng Django có thể có URL riêng cũng như các mẫu HTML và tệp tĩnh riêng, chẳng hạn như JavaScript và CSS .

Để tạo dự án Django, hãy đảm bảo bạn đang ở trong django-blog/thư mục có môi trường ảo được kích hoạt. Sau đó, chạy lệnh sau để tạo personal_blogdự án:

(venv) $ django-admin startproject personal_blog .

Đừng quên thêm dấu chấm ( .) vào cuối lệnh trên. Dấu chấm ngăn Django tạo thư mục dự án lồng nhau cho dự án danh mục đầu tư của bạn. Nếu không, bạn sẽ có một personal_blog/thư mục chứa personal_blog/thư mục con.

Bằng cách chạy startprojectlệnh như trên, bạn đã yêu cầu Django tạo một personal_blog/thư mục trong django-blog/thư mục. Cấu trúc thư mục của bạn sẽ trông giống như thế này:

django-blog/
│
├── personal_blog/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
│
├── venv/
│
└── manage.py

Sau khi cấu trúc tệp của bạn được thiết lập, bây giờ bạn có thể khởi động máy chủ phát triển Django và kiểm tra xem thiết lập của bạn có thành công hay không. Trong bảng điều khiển, chạy lệnh sau:

(venv) $ python manage.py runserver

Sau đó, trong trình duyệt của bạn, hãy truy cập http://localhost:8000và bạn sẽ thấy thông tin sau:

Trang bắt đầu Django

Xin chúc mừng, bạn đã tạo được một trang web Django! Bước tiếp theo là tạo ứng dụng blog để bạn có thể thêm chế độ xem và chức năng cho trang web của mình.

Thêm ứng dụng blog mà bạn sẽ xây dựng từ đầu

Trước khi bạn bắt đầu xây dựng chức năng của phần này trên trang web của mình, hãy tạo một ứng dụng Django mới có tên blog:

(venv) $ python manage.py startapp blog

Sau khi tạo xong ứng dụng, bạn cần cài đặt ứng dụng đó vào dự án của mình. Trong django-blog/personal_blog/settings.py, thêm dòng mã sau vào INSTALLED_APPS:

# personal_blog/settings.py

# ...

INSTALLED_APPS = [
    "blog.apps.BlogConfig",
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
]

# ...

Để đưa một ứng dụng vào dự án Django, bạn cần thêm một tham chiếu đến lớp cấu hình của nó ở đầu danh INSTALLED_APPSsách trong settings.py.

Bằng cách thêm blog.apps.BlogConfig, bạn cho Django biết rằng blogứng dụng bạn vừa tạo tồn tại. Bạn đang tiến một bước lớn gần hơn tới việc xây dựng blog thành công ngay từ đầu! Bước tiếp theo là tạo các mô hình để xác định logic về cách lưu trữ nội dung blog của bạn trong cơ sở dữ liệu.

Xác định các mô hình để thể hiện các bảng cơ sở dữ liệu

Nếu bạn muốn lưu trữ dữ liệu để hiển thị trên trang web thì bạn sẽ cần có cơ sở dữ liệu. Thông thường, nếu bạn muốn tạo cơ sở dữ liệu với các bảng và cột trong các bảng đó thì bạn sẽ cần sử dụng SQL để quản lý cơ sở dữ liệu. Nhưng khi sử dụng Django, bạn không cần phải học ngôn ngữ mới vì nó đã tích hợp sẵn trình ánh xạ quan hệ đối tượng (ORM) .

ORM là một chương trình cho phép bạn tạo các lớp tương ứng với các bảng cơ sở dữ liệu. Thuộc tính lớp tương ứng với các cột và các thể hiện của lớp tương ứng với các hàng trong cơ sở dữ liệu. Vì vậy, thay vì học một ngôn ngữ hoàn toàn mới để tạo cơ sở dữ liệu và các bảng của nó, bạn chỉ cần viết một số lớp Python .

Khi bạn đang sử dụng ORM, mô hình là các lớp bạn xây dựng đại diện cho các bảng cơ sở dữ liệu. Ở Django, chúng nằm trong models.pymô-đun của từng ứng dụng Django.

Bạn sẽ cần ba mô hình riêng biệt cho blog:

  1. Post
  2. Category
  3. Comment

Bắt đầu bằng mã cho Categoryvà Postmô hình trong models.pytệp ứng dụng của bạn blog:

 1# blog/models.py
 2
 3from django.db import models
 4
 5class Category(models.Model):
 6    name = models.CharField(max_length=30)
 7
 8class Post(models.Model):
 9    title = models.CharField(max_length=255)
10    body = models.TextField()
11    created_on = models.DateTimeField(auto_now_add=True)
12    last_modified = models.DateTimeField(auto_now=True)
13    categories = models.ManyToManyField("Category", related_name="posts")

Mô hình này Categoryrất cơ bản. Tất cả những gì nó cần là một đĩa đơn CharFieldtrong đó bạn lưu trữ tên của một danh mục cho các bài đăng trên blog của mình. Để giữ cho tên danh mục của bạn ngắn gọn, bạn đặt độ dài tối đa là ba mươi ký tự.

Các trường titlevà bodytrên Postmô hình cũng chứa văn bản. Bạn cần một cái CharFieldđể titlelưu trữ một chuỗi ngắn cho tiêu đề bài viết. Nội dung của bài đăng cần phải là một đoạn văn bản dài, vì vậy bạn sử dụng phần mở rộng TextField.

Hai trường tiếp theo created_onvà last_modified, là Django DateTimeFields. Chúng lưu trữ một datetimeđối tượng chứa ngày và giờ tương ứng khi bài viết được tạo và sửa đổi.

Đối với created_onDateTimeFieldcó đối số auto_now_add=True. Việc này chỉ định ngày và giờ hiện tại cho trường này bất cứ khi nào bạn tạo một phiên bản của lớp này.

Đối với last_modifiedDateTimeFieldcó đối số auto_now=True. Việc này chỉ định ngày và giờ hiện tại cho trường này bất cứ khi nào một phiên bản của lớp này được lưu. Điều đó có nghĩa là bất cứ khi nào bạn chỉnh sửa một thể hiện của lớp này date_modifiedđều được cập nhật.

Trường cuối cùng trên Postmô hình tạo mối quan hệ giữa bài đăng và danh mục. Tại đây, bạn liên kết các mô hình của mình cho các danh mục và bài đăng theo cách mà bạn có thể chỉ định nhiều danh mục cho nhiều bài đăng. Django cung cấp ManytoManyFieldloại trường cho loại mối quan hệ này.

Có ManyToManyFieldhai đối số. Đầu tiên là mô hình mà nó áp dụng—trong trường hợp này là Category. Trường thứ hai cho phép bạn truy cập vào mối quan hệ từ một Categoryđối tượng, ngay cả khi bạn chưa thêm trường vào đó. Bằng cách thêm related_nameof posts, bạn có thể truy cập category.postsđể lấy danh sách các bài đăng thuộc danh mục đó. Bạn sẽ thấy nó hoạt động như thế nào sau này trong phần hướng dẫn.

Mô hình thứ ba và cuối cùng mà bạn cần thêm có tên Comment:

 1# blog/models.py
 2
 3# ...
 4
 5class Comment(models.Model):
 6    author = models.CharField(max_length=60)
 7    body = models.TextField()
 8    created_on = models.DateTimeField(auto_now_add=True)
 9    post = models.ForeignKey("Post", on_delete=models.CASCADE)

Ba trường đầu tiên trong mô hình này trông quen thuộc. Có một authortrường để người dùng thêm tên hoặc bí danh, một bodytrường dành cho nội dung nhận xét và một created_ontrường giống hệt với created_ontrường trên Postmô hình.

Ở dòng 9, bạn sử dụng một trường quan hệ khác, ForeignKeytrường. Điều này tương tự ManyToManyFieldnhưng thay vào đó nó xác định mối quan hệ nhiều-một . Lý do đằng sau điều này là nhiều bình luận có thể được gán cho một bài đăng. Nhưng bạn không thể có một bình luận tương ứng với nhiều bài viết.

Trường này ForeignKeycó hai đối số. Đầu tiên là mô hình khác trong mối quan hệ—trong trường hợp này, Post. Lệnh thứ hai cho Django biết phải làm gì khi bài viết bị xóa. Nếu một bài đăng bị xóa thì bạn không muốn những bình luận liên quan đến bài viết đó bị treo lại. Thay vào đó, bạn cũng xóa các bình luận. Đó là những gì on_delete=models.CASCADEdành cho.

Bây giờ bạn đã tạo Projectlớp của mình, bạn cần Django để tạo cơ sở dữ liệu. Theo mặc định, Django ORM tạo cơ sở dữ liệu bằng SQLite , nhưng bạn có thể sử dụng các cơ sở dữ liệu khác sử dụng ngôn ngữ SQL, chẳng hạn như PostgreSQL hoặc MySQL , với Django ORM.

Để bắt đầu quá trình tạo cơ sở dữ liệu, bạn cần tạo tệp di chuyển . Di chuyển là một tệp chứa một Migrationlớp với các quy tắc cho Django biết những thay đổi cần thực hiện đối với cơ sở dữ liệu.

Để tạo di chuyển, hãy nhập lệnh sau trong bảng điều khiển, đảm bảo rằng bạn đang ở trong django-blog/thư mục:

(venv) $ python manage.py makemigrations blog
Migrations for 'blog':
  blog/migrations/0001_initial.py
    - Create model Category
    - Create model Post
    - Create model Comment

Bạn sẽ thấy blog/bây giờ có chứa migrations/, bao gồm một tệp có tên 0001_initial.py. Tệp này chứa các hướng dẫn mà Django nên thực hiện trên cơ sở dữ liệu.

Bây giờ bạn đã tạo một tệp di chuyển, bạn cần áp dụng các di chuyển được nêu trong tệp đó và tạo cơ sở dữ liệu của mình bằng lệnh migrate:

(venv) $ python manage.py migrate blog
Operations to perform:
  Apply all migrations: blog
Running migrations:
  Applying blog.0001_initial... OK

Bây giờ bạn đã tạo mô hình, bạn có thể bắt đầu thêm một số bài đăng và danh mục vào blog của mình. Để làm như vậy, bạn sẽ học cách sử dụng trang quản trị Django, trang này sẽ cho phép bạn tạo các phiên bản của các lớp mô hình của mình trong một giao diện web đẹp mắt.

Tận dụng trang quản trị Django

Trang quản trị Django là một công cụ tuyệt vời và là một trong những lợi ích tuyệt vời khi sử dụng Django. Trang quản trị Django cho phép bạn với tư cách là quản trị viên blog tạo, cập nhật và xóa các phiên bản của các lớp mô hình của bạn một cách thoải mái nhờ giao diện web đẹp mắt.

Để truy cập trang quản trị Django, bạn cần thêm chính mình làm siêu người dùng. Django đi kèm với các mô hình người dùng tích hợp và hệ thống quản lý người dùng cho phép bạn đăng nhập vào quản trị viên.

Trước khi có thể tạo siêu người dùng, bạn cần áp dụng một số di chuyển hiện có mà Django đã chuẩn bị cho bạn:

(venv) $ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, blog, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK

Nhìn vào danh sách các lần di chuyển được áp dụng sẽ cho bạn ấn tượng về các tính năng mà Django đã tích hợp sẵn. Ví dụ: Django cung cấp ủy quyền người dùng và trang quản trị.

Sau khi áp dụng tất cả các lần di chuyển, bạn có thể thêm chính mình làm siêu người dùng bằng lệnh sau:

(venv) $ python manage.py createsuperuser
Username (leave blank to use 'root'): admin
Email address: admin@example.com
Password: RealPyth0n
Password (again): RealPyth0n
Superuser created successfully.

Khi bạn chạy createsuperuserlệnh quản lý, Django sẽ nhắc bạn chọn tên người dùng, cung cấp địa chỉ email và đặt mật khẩu. Sử dụng dữ liệu của riêng bạn cho các trường này và đảm bảo ghi nhớ chúng.

Điều hướng đến http://localhost:8000/adminvà đăng nhập bằng thông tin đăng nhập mà bạn vừa sử dụng để tạo siêu người dùng. Bạn sẽ thấy một trang tương tự như trang bên dưới:

Trang quản trị mặc định của Django

Các mô hình Uservà Groupssẽ xuất hiện, nhưng bạn sẽ nhận thấy rằng không có tham chiếu nào đến các mô hình mà bạn đã tự tạo. Đó là bởi vì trước tiên bạn cần đăng ký mô hình của mình bên trong trang quản trị Django.CategoryPostComment

Trong blogthư mục, mở admin.pytệp và thêm các dòng mã sau:

 1# blog/admin.py
 2
 3from django.contrib import admin
 4from blog.models import Category, Comment, Post
 5
 6class CategoryAdmin(admin.ModelAdmin):
 7    pass
 8
 9class PostAdmin(admin.ModelAdmin):
10    pass
11
12class CommentAdmin(admin.ModelAdmin):
13    pass
14
15admin.site.register(Category, CategoryAdmin)
16admin.site.register(Post, PostAdmin)
17admin.site.register(Comment, CommentAdmin)

Ở dòng 4, bạn nhập các model mà bạn muốn đăng ký trên trang quản trị.

Từ dòng 6 đến dòng 13, bạn xác định các lớp trống CategoryAdminPostAdmin, và CommentAdmin. Vì mục đích của hướng dẫn này, bạn không cần thêm bất kỳ thuộc tính hoặc phương thức nào vào các lớp này. Mục đích của họ là tùy chỉnh những gì trang quản trị hiển thị. Đối với hướng dẫn này, cấu hình mặc định là đủ.

Trong ba dòng cuối cùng, bạn đăng ký mô hình với các lớp quản trị viên. Nếu bạn truy cập http://localhost:8000/admin, bạn sẽ thấy các mô hình Postvà Categorybây giờ hiển thị:

Trang web quản trị Django với các mô hình được hiển thị

Nếu bạn nhấp vào Bài đăng thì bạn có thể thêm bài đăng mới cho blog mà bạn đang xây dựng từ đầu. Nếu bạn không muốn nghĩ ra tiêu đề và văn bản ngay bây giờ, bạn có thể tạo các bài đăng giữ chỗ bằng cách sử dụng một số văn bản giả lorem ipsum :

Tạo một vài bài đăng giả mạo và gán cho chúng các danh mục giả mạo trước khi chuyển sang phần tiếp theo. Bằng cách đó, bạn sẽ có các bài đăng và danh mục mà bạn có thể làm việc trong phần tiếp theo.

Kiểm soát việc biểu diễn mô hình

Hiện tại, có hai thiếu sót trong trang quản trị Django mà bạn sẽ giải quyết trong phần này. Cả hai đều liên quan đến việc thể hiện mô hình của bạn. Hãy nhảy qua http://localhost:8000/adminvà xem liệu bạn có thể tìm thấy chúng không. Ngoài ra, bạn có thể nhấp vào phần thu gọn bên dưới:

Để cải thiện khả năng trình bày mô hình của bạn, hãy mở models.pyvà thêm mã được đánh dấu bên dưới:

 1# blog/models.py
 2
 3from django.db import models
 4
 5class Category(models.Model):
 6    name = models.CharField(max_length=30)
 7
 8    class Meta:
 9        verbose_name_plural = "categories"
10
11    def __str__(self):
12        return self.name
13
14class Post(models.Model):
15    title = models.CharField(max_length=255)
16    body = models.TextField()
17    created_on = models.DateTimeField(auto_now_add=True)
18    last_modified = models.DateTimeField(auto_now=True)
19    categories = models.ManyToManyField("Category", related_name="posts")
20
21    def __str__(self):
22        return self.title
23
24class Comment(models.Model):
25    author = models.CharField(max_length=60)
26    body = models.TextField()
27    created_on = models.DateTimeField(auto_now_add=True)
28    post = models.ForeignKey("Post", on_delete=models.CASCADE)
29
30    def __str__(self):
31        return f"{self.author} on '{self.post}'"

Giống như với các lớp Python thông thường , bạn có thể thêm một .__str()__phương thức vào các lớp mô hình hóa để cung cấp cách trình bày chuỗi tốt hơn cho các đối tượng của bạn. Đối với danh mục, bạn muốn hiển thị tên. Đối với bài viết, bạn muốn có tiêu đề. Đối với nhận xét, hãy hiển thị tên của người nhận xét và bài đăng mà họ đang nhận xét.

Để sửa dạng số nhiều không chính xác của Categorylớp, bạn thêm một Metalớp để kiểm soát tên số nhiều của lớp. Theo mặc định, Django chỉ thêm chữ thường vào cuối tên model. Đối với số nhiều của post , điều này hoạt động hoàn hảo. Đối với danh mục , bạn cần xác định rõ ràng verbose_name_plural, viết đúng chính tả.

Để xác minh rằng các thay đổi của bạn có hiệu quả, hãy truy cập http://localhost:8000/adminlại:

Trang web quản trị Django với sự thể hiện chính xác của các mô hình

Làm tốt! Bạn đã khắc phục cả hai thiếu sót và các mô hình được thể hiện theo cách dễ đọc của con người. Tiếp theo, bạn sẽ chuyển từ khu vực quản trị sang phần hướng tới người dùng trên blog của mình.

Xử lý logic với lượt xem

Chế độ xem trong Django là tập hợp các hàm hoặc lớp bên trong views.pytệp trong thư mục của ứng dụng. Mỗi hàm hoặc lớp xử lý logic được xử lý mỗi khi người dùng của bạn truy cập một URL khác.

Bạn sẽ cần tạo ba chức năng xem cho blog của mình trong views.pytệp trong blog/thư mục:

  • blog_index()sẽ hiển thị một danh sách tất cả các bài viết của bạn.
  • blog_detail()sẽ hiển thị toàn bộ bài viết. Sau đó, chế độ xem này cũng sẽ hiển thị các nhận xét hiện có và một biểu mẫu cho phép người dùng tạo nhận xét mới.
  • blog_category()sẽ tương tự như blog_index, nhưng các bài đăng hiển thị sẽ chỉ thuộc một danh mục cụ thể mà người dùng chọn.

Bắt đầu bằng cách thêm các mục nhập cần thiết và blog_index()hàm vào views.py:

 1# blog/views.py
 2
 3from django.shortcuts import render
 4from blog.models import Post, Comment
 5
 6def blog_index(request):
 7    posts = Post.objects.all().order_by("-created_on")
 8    context = {
 9        "posts": posts,
10    }
11    return render(request, "blog/index.html", context)

Trên dòng 4, bạn nhập Postcác Commentmô hình. Ở dòng 7, bên trong hàm xem, bạn nhận được Bộ truy vấn chứa tất cả các bài đăng trong cơ sở dữ liệu. Bộ truy vấn là tập hợp tất cả các đối tượng trong cơ sở dữ liệu phù hợp với truy vấn.

Việc thêm .order_by()phương thức vào Bộ truy vấn sẽ sắp xếp các đối tượng theo đối số đã cho. Dấu trừ ( -) yêu cầu Django bắt đầu với giá trị lớn nhất thay vì giá trị nhỏ nhất. Bằng cách đó, bạn sẽ nhận được các bài viết được tạo gần đây trước tiên.

Cuối cùng, bạn xác định một context từ điển và hiển thị một mẫu có tên index.html. Đừng lo lắng về các mẫu Django. Bạn sẽ có thể tạo những thứ đó trong phần sắp tới.

Tiếp theo, bạn có thể tạo chế blog_category()độ xem. Hàm xem này sẽ cần lấy tên danh mục làm đối số và truy vấn Postcơ sở dữ liệu để tìm tất cả các bài đăng trong danh mục nhất định:

 1# blog/views.py
 2
 3# ...
 4
 5def blog_category(request, category):
 6    posts = Post.objects.filter(
 7        categories__name__contains=category
 8    ).order_by("-created_on")
 9    context = {
10        "category": category,
11        "posts": posts,
12    }
13    return render(request, "blog/category.html", context)

Trên dòng 6, bạn sử dụng bộ lọc Django Queryset . Đối số của bộ lọc cho Django biết những điều kiện nào cần phải đúng để truy xuất một đối tượng. Trong trường hợp này, bạn chỉ muốn các bài đăng có danh mục chứa danh mục có tên tương ứng với nội dung được đưa ra trong đối số của hàm xem. Một lần nữa, bạn đang sử dụng .order_by()dòng 8 để sắp xếp các bài đăng bắt đầu bằng bài gần đây nhất.

Sau đó, bạn thêm những bài đăng này và danh mục vào contexttừ điển và hiển thị một category.htmlmẫu.

Chức năng xem cuối cùng cần thêm là blog_detail():

 1# blog/views.py
 2
 3# ...
 4
 5def blog_detail(request, pk):
 6    post = Post.objects.get(pk=pk)
 7    comments = Comment.objects.filter(post=post)
 8    context = {
 9        "post": post,
10        "comments": comments,
11    }
12
13    return render(request, "blog/detail.html", context)

Hàm blog_detail()xem lấy một giá trị khóa chính, pk, làm đối số và ở dòng 6, truy xuất đối tượng với pk. Khóa chính là mã định danh duy nhất của mục nhập cơ sở dữ liệu. Điều đó có nghĩa là bạn đang yêu cầu một bài đăng có khóa chính cụ thể mà bạn cung cấp.

Trên dòng 7, bạn truy xuất tất cả các nhận xét được gán cho bài đăng nhất định bằng cách sử dụng lại bộ lọc Django. Nếu bạn chưa tạo bất kỳ nhận xét nào trong trang quản trị Django thì Bộ truy vấn sẽ trống. Bây giờ thì ổn rồi.

Cuối cùng, bạn thêm cả hai postvà commentsvào contexttừ điển và hiển thị một detail.htmlmẫu. Giống như các mẫu khác mà bạn tham chiếu trong chế độ xem của mình, mẫu này chưa tồn tại. Trong phần tiếp theo, bạn sẽ tạo các mẫu còn thiếu.

Xây dựng các mẫu

Mẫu là các tệp HTML có khả năng hiển thị nội dung động được gửi từ chế độ xem Django của bạn. Có các công cụ tạo mẫu phổ biến như Jinja . Nhưng nếu bạn không định làm bất cứ điều gì cầu kỳ trong các mẫu của mình thì bạn có thể sử dụng ngôn ngữ mẫu tích hợp sẵn của Django .

Chức render()năng của chế độ xem của bạn sẽ tìm kiếm các mẫu HTML trong thư mục có tên bên templates/trong thư mục ứng dụng của bạn. Vì mẫu của các ứng dụng khác nhau có thể có cùng tên nên cách tốt nhất là thêm thư mục con có tên ứng dụng bên trong thư templates/mục đó.

Tạo template/thư mục cũng như thư mục con có tên blog/, sau đó là các tệp mẫu bên trong nó:

(venv) $ mkdir -p blog/templates/blog
(venv) $ touch blog/templates/blog/index.html
(venv) $ touch blog/templates/blog/category.html
(venv) $ touch blog/templates/blog/detail.html

Mẫu đầu tiên bạn sẽ làm việc là index.html. Bạn sẽ sử dụng một forvòng lặp để lặp qua tất cả các bài viết. Đối với mỗi bài đăng, bạn sẽ hiển thị tiêu đề và một đoạn nội dung:

 1<!-- blog/templates/blog/index.html -->
 2
 3{% block page_title %}
 4    <h2>Blog Posts</h2>
 5{% endblock page_title %}
 6
 7{% block page_content %}
 8    {% block posts %}
 9        {% for post in posts %}
10            <h3><a href="{% url 'blog_detail' post.pk %}">{{ post.title }}</a></h3>
11            <small>
12                {{ post.created_on.date }} | Categories:
13                {% for category in post.categories.all %}
14                    <a href="{% url 'blog_category' category.name %}">
15                        {{ category.name }}
16                    </a>
17                {% endfor %}
18            </small>
19            <p>{{ post.body | slice:":400" }}...</p>
20        {% endfor %}
21    {% endblock posts %}
22{% endblock page_content %}

Bạn có thể nhận thấy rằng mẫu sẽ không hiển thị dưới dạng trang HTML hợp lệ. Đối với điều này, các phần tử HTML bắt buộc như <html>và <head>bị thiếu. Bạn sẽ lo việc này sau. Hiện tại, hãy tập trung vào cách bạn làm việc với contexttừ điển trong mẫu Django.

Trên dòng 9, bạn đang lặp qua các postsgiá trị. Điều đó có nghĩa là bạn có thể truy cập trực tiếp các khóa contexttrong mẫu Django vì từ điển này đã được giải nén cho bạn.

Bên trong forvòng lặp, bạn có thể truy cập các thuộc tính của post, như .titletrên dòng 10. Bạn bọc tiêu đề của bài đăng trong một siêu liên kết trỏ đến URL có tên blog_detail, URL này lấy một số nguyên làm đối số. Số nguyên này là giá trị khóa chính duy nhất pkcủa bài đăng.

Bên dưới tiêu đề, bạn hiển thị .created_onthuộc tính của bài đăng cũng như các danh mục của nó. Ở dòng 13, bạn sử dụng một forvòng lặp khác để lặp lại tất cả các danh mục được chỉ định cho bài đăng.

Ở dòng 19, bạn sử dụng bộ lọc mẫu slice để cắt phần nội dung bài viết còn 400 ký tự để chỉ mục blog dễ đọc hơn. Để tìm hiểu thêm về bộ lọc mẫu, hãy xem hướng dẫn về thẻ và bộ lọc tích hợp .

Một điểm thú vị khác trong index.htmlmẫu này là việc sử dụng {% block %} các thẻ mẫu . Với thẻ mẫu này, bạn có thể xác định các khối nội dung mà bạn có thể sử dụng hoặc ghi đè trong các mẫu con mở rộng mẫu gốc.

Mẫu con của index.htmllà category.html. Mẫu này sẽ trông gần giống nhau. Bất kỳ bài đăng nào mà mẫu nhận được từ chế độ xem đều phải được liệt kê. Điều đó có nghĩa là sự khác biệt duy nhất ở category.htmldòng tiêu đề:

<!-- blog/templates/blog/category.html -->

{% extends "blog/index.html" %}

{% block page_title %}
<h2>{{ category }}</h2>
{% endblock page_title %}

Để mở rộng mẫu gốc, bạn phải sử dụng {% extends %}thẻ ở đầu mẫu con. Bên trong {% extends %}thẻ, bạn xác định mẫu mà bạn muốn mở rộng.

Sau khi tham chiếu mẫu gốc, bạn có thể quyết định khối nào bạn muốn kế thừa hoặc ghi đè. Nếu bạn không tham chiếu một khối trong mẫu con thì bạn sẽ kế thừa khối của mẫu mẹ. Ngoài ra, bạn có thể tham chiếu khối của phụ huynh với nội dung mới.

Ở đây, bạn chỉ ghi đè titlekhối chứ không ghi postsđè khối index.html. Vì bạn không tham chiếu postskhối của mẫu gốc nên bạn kế thừa nội dung từ index.htmlkhối này. Đó chính xác là những gì bạn muốn hiển thị tất cả các bài đăng mà chế blog_category()độ xem cung cấp.

Mẫu cuối cùng là detail.htmlmẫu. Trong mẫu này, bạn sẽ hiển thị tiêu đề và toàn bộ nội dung của bài đăng.

Giữa tiêu đề và nội dung bài đăng, bạn sẽ hiển thị ngày tạo bài đăng và mọi danh mục:

 1<!--  blog/templates/blog/detail.html -->
 2
 3{% block page_title %}
 4    <h2>{{ post.title }}</h2>
 5{% endblock page_title %}
 6
 7{% block page_content %}
 8    <small>
 9        {{ post.created_on.date }} | Categories:
10        {% for category in post.categories.all %}
11            <a href="{% url 'blog_category' category.name %}">
12                {{ category.name }}
13            </a>
14        {% endfor %}
15    </small>
16    <p>{{ post.body | linebreaks }}</p>
17{% endblock page_content %}

Một vài dòng đầu tiên của mẫu—trong đó bạn hiển thị tiêu đề, ngày tháng và danh mục bài đăng—có logic tương tự như các mẫu trước đó. Lần này, khi hiển thị nội dung bài đăng, bạn sử dụng linebreaksbộ lọc mẫu. Thẻ này đăng ký hai ngắt dòng liên tiếp dưới dạng đoạn văn mới, do đó phần nội dung không xuất hiện dưới dạng một khối văn bản dài.

Với các mẫu đã có sẵn, bạn chỉ còn thiếu một mảnh ghép để thực sự có thể xem blog mà bạn đang xây dựng từ đầu: các tuyến đường. Trong phần tiếp theo, bạn sẽ tạo lộ trình để lượt xem có thể truy cập blog của bạn trong trình duyệt.

Bao gồm các tuyến đường cho URL

Để thực sự thấy blog của bạn hoạt động và hoạt động, bạn cần kết nối các tuyến đường cho chúng trong dự án Django của mình. Nói chung, tuyến đường là URL mà bạn sẽ nhập vào thanh địa chỉ của trình duyệt.

Trong Django, bạn tạo các tuyến đường có mẫu . Thay vì tạo URL cho mỗi bài đăng blog theo cách thủ công, bạn có thể tạo quy tắc để truy cập bất kỳ bài đăng blog hiện có nào.

Để làm như vậy, bạn cần tạo một urls.pytệp bên trong blog/và thêm URL cho ba chế độ xem:

# blog/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path("", views.blog_index, name="blog_index"),
    path("post/<int:pk>/", views.blog_detail, name="blog_detail"),
    path("category/<category>/", views.blog_category, name="blog_category"),
]

Khi đã có các URL dành riêng cho blog, bạn cần thêm chúng vào cấu hình URL của dự án bằng cách personal_blog/urls.pysử dụng include():

# personal_blog/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path("admin/", admin.site.urls),
    path("", include("blog.urls")),
]

Với thiết lập này, bạn sẽ có logic lộ trình sau:

Mẫu tuyến đường URL mẫu Sự miêu tả
"" http://localhost:8000/ chỉ mục blog
"post/<int:pk>/" http://localhost:8000/post/1 Chế độ xem chi tiết blog của một bài đăng vớipk=1
"category/<category>/" http://localhost:8000/category/python Chế độ xem chỉ mục blog của tất cả các bài đăng có danh mụcpython

Hãy tiếp tục và ghé thăm http://localhost:8000/. Sau đó, nhấp xung quanh và chú ý các URL khác nhau trong thanh địa chỉ và các mẫu bạn tải:

Cho đến nay, blog trông khá cơ bản. Trong phần tiếp theo, bạn sẽ nâng cao giao diện blog của mình bằng cách thêm một số phong cách cho blog của mình.

Làm cho blog của bạn trông đẹp hơn

Trước khi bắt đầu tạo kiểu cho dự án của mình, bạn sẽ tạo một mẫu cơ sở mà bạn sẽ mở rộng trong các mẫu con của mình từ trước. Bằng cách đó, bạn có thể cấu trúc các mẫu HTML của mình ở một nơi và để các mẫu khác kế thừa nội dung.

Bắt đầu bằng cách tạo một thư mục có tên templates/trong django-blog/thư mục và một tệp có tên base.htmlbên trong thư mục mới:

(venv) $ mkdir templates/
(venv) $ touch templates/base.html

Như bạn đã thấy trước đây, mỗi dự án Django có thể bao gồm nhiều ứng dụng xử lý logic riêng biệt và mỗi ứng dụng chứa templates/thư mục riêng để lưu trữ các mẫu HTML liên quan đến ứng dụng. Đối với các mẫu mà toàn bộ dự án chia sẻ, bạn nên tạo một templates/thư mục trong thư mục gốc.

Bên trong base.html, thêm các dòng mã sau:

 1<!-- templates/base.html -->
 2
 3<!DOCTYPE html>
 4<html lang="en">
 5<head>
 6    <meta charset="utf-8">
 7    <title>My Personal Blog</title>
 8</head>
 9<body>
10<h1>My Personal Blog</h1>
11<a href="{% url "blog_index" %}">Home</a>
12<hr>
13{% block page_title %}{% endblock page_title %}
14{% block page_content %}{% endblock page_content %}
15</body>
16</html>

Với đoạn mã trên, bạn tạo khung của một tài liệu HTML hợp lệ. Bạn cũng xác định tiêu đề và tiêu đề Blog cá nhân của tôi mà bất kỳ mẫu con nào cũng sẽ kế thừa.

Như bạn đã học trước đây, bạn cần thêm {% extends %}thẻ vào đầu mẫu con. Mở index.htmlvà thêm dòng được đánh dấu bên dưới:

<!-- blog/templates/blog/index.html -->

{% extends "base.html" %}

{% block page_title %}
    <h2>Blog Posts</h2>
{% endblock page_title %}

{% block posts %}
    <!-- ... -->
{% endblock posts %}

Bằng cách thêm {% extends "base.html" %}vào index.html, mẫu sẽ kế thừa cấu trúc của base.html.

Tiếp tục detail.htmlvà biến nó thành mẫu con của base.html:

<!--  blog/templates/blog/detail.html -->

{% extends "base.html" %}

{% block page_title %}
    <h2>{{ post.title }}</h2>
{% endblock page_title %}

{% block page_content %}
    <!-- ... -->
{% endblock page_content %}

Với kế thừa mẫu , bạn không phải lặp lại đánh dấu trong mẫu của mình. Thay vào đó, bạn mở rộng các mẫu con của mình. Django sau đó sẽ tự động hợp nhất chúng lại với nhau khi phục vụ chúng trong chế độ xem.

Hãy nhớ rằng categories.htmlđã mở rộng index.html. Vì các mẫu được chuyển giao quyền kế thừa nên bạn không cần thêm bất kỳ thẻ bổ sung nào vào mẫu này.

Trước khi bạn có thể xem mẫu cơ sở đang hoạt động, bạn cần cho biết dự án ví dụ Django của bạn base.htmlđã tồn tại. Cài đặt mặc định đăng ký templates/các thư mục trong mỗi ứng dụng chứ không phải trong chính thư mục gốc. Trong personal_blog/settings.py, cập nhật TEMPLATES:

# personal_blog/settings.py

# ...

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [
            BASE_DIR / "templates/",
        ],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ]
        },
    }
]

# ...

Bạn đã xác định hằng số BASE_DIRin settings.pyvà nó trỏ đến thư mục gốc của dự án. Tiếp theo, bạn nối đường dẫn với toán tử dấu gạch chéo xuôi ( /) từ pathlibđể trỏ đến templates/thư mục và thêm nó vào "DIRS"danh sách.

Hãy truy cập trình duyệt của bạn và truy cập http://localhost:8000/:

Blog Django không có kiểu dáng

Khi bạn truy cập blog của mình, dòng tiêu đề chính sẽ hiển thị trên mọi trang. Điều đó có nghĩa là sự kế thừa hoạt động. Nhưng không có bất kỳ kiểu dáng nào, blog của bạn trông vẫn rất cơ bản.

Thay vì đi sâu vào tạo kiểu CSS với hướng dẫn này, bạn sẽ thêm khung CSS bên ngoài vào dự án của mình. Sử dụng các khung CSS bên ngoài có thể giúp bạn tiết kiệm rất nhiều công việc trong quá trình phát triển web. Điều đó có nghĩa là, nếu bạn không biết CSS, bạn nên học những điều cơ bản nếu bạn đang phát triển web.

Mở base.htmllại và thêm link vào framework Simple.css :

 1<!-- templates/base.html -->
 2
 3<!DOCTYPE html>
 4<html lang="en">
 5<head>
 6    <meta charset="utf-8">
 7    <title>My Personal Blog</title>
 8    <link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
 9</head>
10<body>
11<!-- ... -->
12</body>
13</html>

Tương tự như kỹ thuật nhập Python , bạn có thể nhập thư viện CSS bên ngoài vào trang web của mình. Ở dòng 8, bạn đang tải một tệp CSS bên ngoài. Tệp CSS bên ngoài này cung cấp kiểu dáng mà bạn không cần thêm bất kỳ lớp nào vào các thành phần HTML của mình.

Mở http://localhost:8000/và truy cập trang danh mục để xác minh rằng các mẫu con của bạn kế thừa kiểu này:

Blog Django với sự kế thừa phong cách

Tải biểu định kiểu vào mẫu cơ sở là tất cả những gì bạn cần để thêm các kiểu CSS bên ngoài vào dự án của mình. Tất cả các mẫu đều mở rộng base.htmlvà sẽ tự động kế thừa kiểu dáng.

Blog của bạn gần như đã hoàn thành. Tính năng cuối cùng mà bạn sẽ triển khai là khả năng thêm nhận xét vào bài đăng trên blog của mình.

Làm việc với các biểu mẫu để nhận xét của người dùng

Để bình luận có thể hoạt động trên blog của bạn, bạn cần thêm biểu mẫu vào trang bài đăng. Trước khi làm như vậy, bạn cần tạo biểu mẫu Django . Các hình thức Django rất giống với các mô hình. Một biểu mẫu bao gồm một lớp trong đó các thuộc tính của lớp là các trường biểu mẫu. Django đi kèm với một số trường biểu mẫu tích hợp mà bạn có thể sử dụng để nhanh chóng tạo biểu mẫu mình cần.

Tạo một file mới có tên forms.pytrong blog/thư mục:

(venv) $ touch blog/forms.py

Bên trong forms.py, thêm CommentFormlớp có hai trường authorvà body:

# blog/forms.py

from django import forms

class CommentForm(forms.Form):
    author = forms.CharField(
        max_length=60,
        widget=forms.TextInput(
            attrs={"class": "form-control", "placeholder": "Your Name"}
        ),
    )
    body = forms.CharField(
        widget=forms.Textarea(
            attrs={"class": "form-control", "placeholder": "Leave a comment!"}
        )
    )

Đối với cả hai authorvà body, bạn sử dụng một CharFieldlớp. Để kiểm soát cách hiển thị phần tử biểu mẫu trên trang, bạn chuyển vào một widgetđối số.

Trường này authorcó forms.TextInputtiện ích. Điều này yêu cầu Django tải trường này dưới dạng phần tử nhập văn bản HTML trong các mẫu. Thay vào đó, trường bodysử dụng một forms.TextAreatiện ích nên trường được hiển thị dưới dạng phần tử vùng văn bản HTML.

Các tiện ích này cũng lấy đối số attrs, đây là một từ điển cho phép bạn chỉ định một số lớp CSS. Điều này sẽ giúp định dạng mẫu cho chế độ xem này sau này. Nó cũng cho phép bạn thêm một số văn bản giữ chỗ.

Khi bạn đã tạo biểu mẫu Django để nhận xét, hãy xem cách biểu mẫu di chuyển qua các yêu cầu:

  1. Khi người dùng truy cập một trang có chứa biểu mẫu, họ sẽ gửi GETyêu cầu đến máy chủ. Trong trường hợp này, không có dữ liệu nào được nhập vào biểu mẫu nên bạn chỉ muốn hiển thị biểu mẫu và hiển thị nó.
  2. Khi người dùng nhập thông tin và nhấp vào nút Gửi , họ sẽ gửi POSTyêu cầu , chứa dữ liệu được gửi cùng với biểu mẫu, tới máy chủ. Tại thời điểm này, dữ liệu sẽ được xử lý và có hai điều có thể xảy ra:
    • Biểu mẫu hợp lệ và người dùng được chuyển hướng đến trang tiếp theo.
    • Biểu mẫu không hợp lệ và biểu mẫu trống sẽ xuất hiện lại. Người dùng quay lại bước 1 và quá trình lặp lại.

Chức năng xem cần giống với hành vi này là chế blog_detail()độ xem. Cập nhật blog_detail()với mã được đánh dấu bên dưới:

 1# blog/views.py
 2
 3from django.http import HttpResponseRedirect
 4from django.shortcuts import render
 5from blog.models import Post, Comment
 6from blog.forms import CommentForm
 7
 8# ...
 9
10def blog_detail(request, pk):
11    post = Post.objects.get(pk=pk)
12    form = CommentForm()
13    if request.method == "POST":
14        form = CommentForm(request.POST)
15        if form.is_valid():
16            comment = Comment(
17                author=form.cleaned_data["author"],
18                body=form.cleaned_data["body"],
19                post=post,
20            )
21            comment.save()
22            return HttpResponseRedirect(request.path_info)
23
24    comments = Comment.objects.filter(post=post)
25    context = {
26        "post": post,
27        "comments": comments,
28        "form": CommentForm(),
29    }
30    return render(request, "blog/detail.html", context)

Ở dòng 3, bạn import HttpResponseRedirect, điều này giúp bạn chuyển hướng yêu cầu ở dòng 22. Bạn sẽ xem xét kỹ hơn dòng 22 ngay sau đây. Đầu tiên, hãy làm theo yêu cầu thông qua phần thân của blog_detail().

Bất kể loại yêu cầu của bạn là gì, bạn lấy CommentForm(), loại mà bạn đã nhập ở dòng 6 và tạo một phiên bản của nó ở dòng 12. Bằng cách đó, bạn đảm bảo rằng luôn có một biểu mẫu trống trong chế độ xem của bạn.

Sau đó ở dòng 13, bạn kiểm tra xem mình đã nhận được POSTyêu cầu chưa. Nếu vậy bạn cập nhật lại formdữ liệu của POSTrequest ở dòng 14. Đó là dữ liệu mà người dùng đã nhập vào biểu mẫu.

Sau đó, bạn xác thực biểu mẫu bằng cách sử dụng .is_valid()dòng 15 để kiểm tra xem người dùng đã nhập chính xác tất cả các trường chưa.

Nếu biểu mẫu hợp lệ thì bạn tạo một phiên bản mới của Commentcác dòng từ 16 đến 20. Bạn có thể truy cập dữ liệu từ biểu mẫu bằng cách sử dụng form.cleaned_data, đây là một từ điển. Trước khi chuyển dữ liệu do người dùng gửi vào các truy vấn cơ sở dữ liệu của bạn, bạn nên dọn sạch dữ liệu biểu mẫu . Bằng cách đó, bạn đảm bảo rằng mọi đầu vào đều nhất quán và an toàn.

Các khóa form.cleaned_datatương ứng với các trường biểu mẫu, vì vậy bạn có thể truy cập tác giả bằng cách sử dụng form.cleaned_data["author"]ở dòng 17 và nội dung nhận xét ở form.cleaned_data["body"]dòng 18.

Để tạo đúng một Commentđối tượng trong cơ sở dữ liệu của bạn, bạn phải kết nối nó với một đối tượng hiện có Postở dòng 19. Bạn lấy bài đăng liên quan bằng khóa chính của chế độ xem ở dòng 11.

Sau khi tạo nhận xét từ biểu mẫu, bạn lưu nhận xét đó .save()ở dòng 21 và chuyển hướng người dùng đến URL request.path_infochứa ở dòng 22. Trong trường hợp của bạn, đó sẽ là URL của một bài đăng trên blog.

Nói cách khác, điều này có nghĩa là khi bạn truyền một biểu mẫu hợp lệ tới blog_detail()một POSTyêu cầu thì Django sẽ gọi blog_detail()lại sau khi lưu nhận xét của bạn. Lần này, Django sẽ gọi hàm xem với một GETyêu cầu và bài đăng trên blog sẽ được tải với biểu mẫu trống và nhận xét của bạn trong danh sách nhận xét.

Đối với những yêu cầu này GEThoặc khi biểu mẫu không hợp lệ, phần còn lại blog_detail()sẽ thực hiện việc này:

  • Dòng 24 truy vấn cơ sở dữ liệu về bất kỳ nhận xét hiện có nào về bài đăng của bạn.
  • Các dòng từ 25 đến 29 tạo context, bao gồm dữ liệu cho bài đăng, các bình luận được lọc và biểu mẫu.
  • Dòng 30 hiển thị detail.htmlmẫu với context.

Bây giờ nó contextcũng chứa commentsdữ formliệu, bạn có thể cập nhật detail.htmlmẫu của mình:

 1<!-- blog/templates/blog/detail.html -->
 2
 3{% block page_title %}
 4    <h2>{{ post.title }}</h2>
 5{% endblock page_title %}
 6
 7{% block page_content %}
 8    <small>
 9        <!-- ... -->
10    </small>
11    <p>{{ post.body | linebreaks }}</p>
12
13    <h3>Leave a comment:</h3>
14    <form action="/blog/{{ post.pk }}/" method="post">
15        {% csrf_token %}
16        <div>
17            {{ form.author }}
18        </div>
19        <div>
20            {{ form.body }}
21        </div>
22        <button type="submit" class="btn btn-primary">Submit</button>
23    </form>
24
25    <h3>Comments:</h3>
26    {% for comment in comments %}
27        <p>
28            On {{ comment.created_on.date }} <b>{{ comment.author }}</b> wrote:
29        </p>
30        <p>
31            {{ comment.body | linebreaks }}
32        </p>
33    {% endfor %}
34{% endblock page_content %}

Bên dưới bài đăng, bạn hiển thị biểu mẫu của mình. Nếu bạn không xác định actionthuộc tính của biểu mẫu thì hành động của biểu mẫu sẽ trỏ đến trang bạn hiện đang truy cập. Sau đó, bạn thêm csrf_token, cung cấp tính bảo mật và hiển thị nội dung và trường tác giả của biểu mẫu, theo sau là nút gửi.

Cuối cùng, bạn lặp lại tất cả các nhận xét về bài đăng đã cho. Thay vì hiển thị dấu thời gian đầy đủ, bạn chỉ hiển thị ngày cho .created_onthuộc tính của nhận xét.