Nội dung
Bài này sẽ giới thiệu những nội dung sau:
- Thiết kế bảng Comment
- Tạo Form Comment
- Xử lý code bình luận
- Thiết kế lại template
- Hiển thị bình luận trong django admin
Thiết kế bảng Comment
Trước tiên ta sẽ thiết kế bảng Comment, ta sẽ thiết kế ở models.py ở app blog:
from django.conf import settings
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
body = models.TextField()
date = models.DateTimeField(auto_now_add=True)
- ở đây Kteam sử dụng field ForeignKey để cho Django xác định đây là khóa ngoại, việc dùng ForeignKey để áp dụng trường hợp mối quan hệ 1-nhiều. Vì trường hợp blog này là 1 bài viết có nhiều bình luận nhưng 1 bình luận chỉ dành cho 1 bài viết (tương tự với user và bình luận).
- ta khai báo field post là khóa ngoại liên kết với bảng Post, tham số on_delete Kteam cho nó thuộc loại CASCADE (có nghĩa là khi xóa bài viết thì xóa luôn bình luận), tham số related_name có nghĩa khi dev muốn tìm các bình luận từ một bài viết thì thông qua từ khóa ‘comments’ từ post.
- tương tự author là khóa ngoại liên kết bảng user của hệ thống Django để biết ai là người bình luận bài viết.
- body lưu nội dung bình luận, date lưu ngày viết bình luận.
Tạo Form Comment
Tiếp theo, Kteam sẽ thiết kế form comment để hiển thị cho template. Bây giờ Kteam sẽ sử dụng ModelForm để giúp ta tạo các field input form. Ở app blog, ta tạo thêm 1 file forms.py để xử lý:
from django import forms
from .models import Comment
class CommentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.author = kwargs.pop('author', None)
self.post = kwargs.pop('post', None)
super().__init__(*args, **kwargs)
def save(self, commit=True):
comment = super().save(commit=False)
comment.author = self.author
comment.post = self.post
comment.save()
class Meta:
model = Comment
fields = ["body"]
- khai báo CommentForm kế thừa forms.ModelForm.
- trong class có thêm class Meta để xử lý thông tin model: ta cho form này xử lý model Comment, vì ở bảng này ta chỉ nhập nội dung của bình luận (tên tác giả và bài viết được comment không thể nhập tay được) nên khai báo fields chỉ có body để form chỉ tạo input body.
- Kteam override phương thức khởi tạo, mục đích là lấy giá trị author và post để lưu vào bảng comment. Sau khi lấy xong thì ta gọi tiếp phương thức khởi tạo của class cha.
- Kteam override phương thức save có sẵn của ModelForm, việc override này mục đích gán giá trị author và post. Đầu tiên là gọi phương thức save của class cha (ta cho tham số commit = False để chưa lưu xuống database). Sau đó lấy instance comment ra và gán giá trị author và post rồi mới gọi phương thức save() của model.
Xử lý code bình luận
Ở views.py trong app blog, ta sẽ xử lý hiển thị form bình luận cho bài viết và xử lý khi người dùng bình luận:
from django.shortcuts import render, get_object_or_404
from .models import Post, Comment
from .forms import CommentForm
from django.http import HttpResponseRedirect
# Create your views here.
def post(request, pk):
post = get_object_or_404(Post, pk=pk)
form = CommentForm()
if request.method == "POST":
form = CommentForm(request.POST, author=request.user, post=post)
if form.is_valid():
form.save()
return HttpResponseRedirect(request.path)
return render(request, "blog/post.html", {"post": post, "form": form})
- dùngget_object_or_404 để tìm bài viết qua khóa chính, nên không thấy thì trả về 404.
- nếu không phải method POST thì khởi tạo form bình thường để hiển thị cho người dùng viết bình luận.
- nếu method bằng POST thì ta xử lý data từ máy user gửi về, ta khởi tạo lại CommentForm với truyền dữ liệu POST vào, ngoài ra thêm dữ liệu user đang đăng nhập và bài viết được bình luận. Nếu form hợp lệ thì lưu bài viết, sau đó redirect lại đường dẫn để mất đi thông tin POST (vì nếu không redirect thì khi user nhấn F5 thì lại gửi lại thông tin bình luận).
- xử lý render truyền bài viết và form vào.
Bây giờ, ta chỉnh lại path để khi vào đường dẫn bài viết sẽ gọi hàm xử lý này:
from django.urls import path
from .models import Post
from . import views
from django.views.generic import ListView
urlpatterns = [
path('', ListView.as_view(
queryset = Post.objects.all().order_by('-date'),
template_name = 'blog/blog.html',
context_object_name = 'Posts',
paginate_by = 1)
, name='blog'),
path('<int:pk>/', views.post, name='post'),
]
ở PostDetailView thì class sẽ truy vấn bằng primary key, nên thay url id thành pk để path hợp lệ.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.
Thiết kế lại template
Bây giờ, ở template post.html ta phải thiết kế lại để hiển thị bình luận và form cho user bình luận:
{% extends "pages/base.html" %}
{% block title %}{{post.title}}{% endblock %}
{% block content %}
<h3><a href="/redirect?Id=duECimTrtcY%2f9%2fkQF45R8ricuL8rnN1W89bqLQLRmAR%2b2AE1Wuc2%2fsatIaeyTQ2SiSNKPWJ7vem62cw9ZxE2LzZQEtuy6DuaTgL6aoimSYA%3d"
<img src="{{post.image.url}}" width="500px" height="300px" />
<h6>on {{post.date}}</h6>
{{post.body|safe|linebreaks}}
{% for comment in post.comments.all %}
<h4><strong>{{comment.author}}</strong></h4>
<h6><p>{{comment.date}}</p></h6>
<p>{{comment.body|linebreaks}}</p>
{% endfor %}
{% if user.username %}
<form action="{% url 'post' post.id %}" method="POST">
{% csrf_token %}
<p><label>Bình luận:</label></p>
{{form.body}}
<br/>
<input type="submit" value="Bình luận"/>
</form>
{% endif %}
{% endblock %}
- dùng post.comments.all để lấy hết bình luận trong bài viết đó, ‘comments’ chính là related_name ta đã khai báo ở models.
- dùng vòng lặp for để hiển thị hết thông tin.
- ở form, ta kiểm tra user có đăng nhập chưa, nếu đăng nhập mới hiển thị form bình luận.
Bây giờ, ta sẽ kiểm tra kết quả:
Hiển thị bình luận trong django admin
Bây giờ, Kteam muốn admin hiển thị bình luận ngay ở trong thông tin bài viết để dễ quản lý hơn, ta có thể sử dụng StackedInline của django admin như sau:
from django.contrib import admin
# Register your models here.
from .models import Post, Comment
# Register your models here.
class CommentInline(admin.StackedInline):
model = Comment
class PostAdmin(admin.ModelAdmin):
list_display = ['title', 'date']
list_filter = ['date']
search_fields = ['title']
inlines = [CommentInline]
admin.site.register(Post, PostAdmin)
Hoặc ta sử dụng TabularInline:
from django.contrib import admin
# Register your models here.
from .models import Post, Comment
# Register your models here.
class CommentInline(admin.TabularInline):
model = Comment
class PostAdmin(admin.ModelAdmin):
list_display = ['title', 'date']
list_filter = ['date']
search_fields = ['title']
inlines = [CommentInline]
admin.site.register(Post, PostAdmin)
Project tham khảo
Nếu quá trình thực hành của bạn không diễn ra suôn sẻ như bài hướng dẫn thực hiện, bạn có thể tham khảo Project mẫu của Kteam trong link bên dưới hoặc để lại BÌNH LUẬN để được hỗ trợ nhé!
Đừng quên like hoặc +1 Google để ủng hộ Kteam và tác giả nhé!