問題描述:
在Django開發中,我們增加一個功能的時候,如:評論功能,因爲需要在不同的頁面下實現展示評論和提交評論的功能,所以可能需要修改不同頁面對應的視圖函數,這樣極大的提高了代碼的維護成本。
問題解決:
這個時候,我們可以藉助Django的自定義標籤來實現評論的功能,把評論功能對應的數據操作放在標籤函數中去實現,這樣就可以減少代碼的耦合,降低維護成本。
自定義標籤的使用:
(1)評論Comment對應的model定義:
models.py
from django.db import models
# Create your models here.
class Comment(models.Model):
STATUS_NORMAL = 1
STATUS_DELETE = 0
STATUS_ITEMS = (
(STATUS_NORMAL, '正常'),
(STATUS_DELETE, '刪除'),
)
# target = models.ForeignKey(Post, verbose_name='評論目標')
target = models.CharField(max_length=100, verbose_name='評論目標')
content = models.CharField(max_length=500, verbose_name='內容')
nickname = models.CharField(max_length=50, verbose_name='暱稱')
website = models.URLField(verbose_name='網站')
email = models.EmailField(verbose_name='郵箱')
status = models.PositiveIntegerField(default=STATUS_NORMAL, choices=STATUS_ITEMS,
verbose_name='狀態')
created_time = models.DateTimeField(auto_now_add=True, verbose_name='創建時間')
class Meta:
verbose_name = verbose_name_plural = '評論'
ordering = ['-created_time'] # 根據created_time進行降序排序
@classmethod
def get_by_target(cls, target):
return cls.objects.filter(target=target, status=cls.STATUS_NORMAL)
(2)評論對應的後臺配置:
admin.py
from django.contrib import admin
from .models import Comment
# Register your models here.
@admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
list_display = ('target', 'nickname', 'content', 'website', 'created_time')
(3)評論表單form的定義,在comment應用目錄下,新建forms.py:
forms.py:
import mistune
from django import forms
from .models import Comment
class CommentForm(forms.ModelForm):
nickname = forms.CharField(
label='暱稱',
max_length=50,
widget=forms.widgets.Input(
attrs={'class': 'form-control', 'style': "width: 60%;"}
)
)
email = forms.CharField(
label='Email',
max_length=50,
widget=forms.widgets.EmailInput(
attrs={'class': 'form-control', 'style': "width: 60%;"}
)
)
website = forms.CharField(
label='網站',
max_length=100,
widget=forms.widgets.URLInput(
attrs={'class': 'form-control', 'style': "width: 60%;"}
)
)
content = forms.CharField(
label="內容",
max_length=500,
widget=forms.widgets.Textarea(
attrs={'rows': 6, 'cols': 60, 'class': 'form-control'}
)
)
# 對評論內容進行驗證
def clean_content(self):
content = self.cleaned_data.get('content')
if len(content) < 1:
raise forms.ValidationError('內容長度怎麼能這麼短呢!!')
content = mistune.markdown(content)
return content
class Meta:
model = Comment
fields = ['nickname', 'email', 'website', 'content']
(4)創建comment自定義標籤,在在comment應用目錄下,新建templatetags目錄,在該目錄下新建__init__.py和comment_block.py文件。
在comment_block.py文件中編寫自定義標籤的代碼:
comment/templatetags/comment_block.py:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from django import template
from comment.forms import CommentForm
from comment.models import Comment
register = template.Library()
@register.inclusion_tag('comment/block.html')
def comment_block(target):
return {
'target': target,
'comment_form': CommentForm(),
'comment_list': Comment.get_by_target(target)
}
(5)編寫模板文件comment/block.html:
comment/block.html:
注意:因爲我們在後面的detail.html中引入了css文件:bootstrap.css,所以我們在block.html中使用了bootstrap.css中定義的一些樣式。
<hr/>
<div class="comment">
<form class="form-group" action="/comment/" method="POST">
{% csrf_token %}
<input name="target" type="hidden" value="{{ target }}"/>
{{ comment_form }}
<input type="submit" value="submit">
</form>
<!--評論列表-->
<ul class="list-group">
{% for comment in comment_list %}
<li class="list-group-item">
<div class="nickname">
<a href="{{ comment.website }}">{{ comment.nickname }}</a>
<span>{{ comment.created_time}}</span>
</div>
<div class="comment-content">
{{ comment.content }}
</div>
</li>
{% endfor %}
</ul>
</div>
(6)在其他頁面添加評論功能,如在文章詳情頁detail.html加入評論功能,
1、在detail.html的開頭引入評論的模板標籤:
{% load comment_block %}
2、在detail.html中需要引入評論的位置添加:
{% comment_block request.path post.title %}
即可引入評論的功能。
(7)在評論的form中(block.html),
<form class="form-group" action="/comment/" method="POST">
提交評論的時候,訪問了action="/comment/",所以需要在urls.py中配置該路徑對應的URL,並且在該URL對應的視圖函數中,對評論的數據進行驗證和保存。
1、urls.py代碼:
"""typeidea URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/1.11/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.conf.urls import url, include
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
from django.contrib import admin
from blog.views import (
IndexView, CategoryView, TagView,
PostDetailView
)
from comment.views import CommentView
urlpatterns = [
url(r'^$', IndexView.as_view(), name='index'), # 首頁
url(r'^category/(?P<category_id>\d+)/$', CategoryView.as_view(), name='category-list'), # 分類列表頁
url(r'^tag/(?P<tag_id>\d+)/$', TagView.as_view(), name='tag-list'), # tag列表頁
url(r'post/(?P<post_id>\d+).html$', PostDetailView.as_view(), name='post-detail'), # 文章詳情頁
url(r'^comment/$', CommentView.as_view(), name='comment'), # 評論提交
url(r'^admin/', admin.site.urls),
]
2、實現視圖函數comment/views.py:
from django.shortcuts import redirect
from django.views.generic import TemplateView
from .forms import CommentForm
class CommentView(TemplateView):
http_method_names = ['post']
template_name = 'comment/result.html'
def post(self, request, *args, **kwargs):
comment_form = CommentForm(request.POST)
target = request.POST.get('target')
if comment_form.is_valid():
instance = comment_form.save(commit=False)
instance.target = target
instance.save()
succeed = True
return redirect(target) # 評論成功,返回原評論頁面
else:
succeed = False
context = {
'succeed': succeed,
'form': comment_form,
'target': target
}
return self.render_to_response(context) # 提交數據失敗,則去到評論結果頁comment/result.html。
3、評論結果頁comment/result.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>評論結果頁</title>
<style>
body {TEXT-ALIGN: center;}
.result {
text-align: center;
width: 40%;
margin: auto;
}
.errorlist {color: red;}
ul li {
list-style-type: None;
}
</style>
</head>
<body>
<div class="result">
{% if succeed %}
評論成功!
<a href="{{ target }}">返回</a>
{% else %}
<ul class="errorlist">
{% for field, message in form.errors.items %}
<li>字段{{ field }}:{{ message }}</li>
{% endfor %}
</ul>
<a href="javascript:window.history.back();">返回</a>
{% endif %}
</div>
</body>
</html>
(8)結果演示:
文章詳情頁: