问题描述:
在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)结果演示:
文章详情页: