github链接 https://github.com/yt-xy/Django-blog
增加搜索功能
blogs/views.py
class SearchView(IndexView):
def get_context_data(self, **kwargs):
context = super().get_context_data()
context.update({
'keyword': self.request.GET.get('keyword', '')
})
return context
def get_queryset(self):
queryset = super().get_queryset()
keyword = self.request.GET.get('keyword')
if not keyword:
return queryset
# SELECT * FROM post WHERE title ILIKE '%<keyword>%' or title ILIKE '%<keyword>%'
return queryset.filter(Q(title__icontains=keyword) | Q(desc__icontains=keyword))
urls.py
from blogs.views import (
...
SearchView,
)
urlpatterns = [
...
url(r'search/$', SearchView.as_view(), name='search'),
]
templates/blogs/base.html
<form class="form-inline" action="/search/" action="GET">
<input class="form-control" type="search" placeholder="Search" name="keyword" placeholder="Search" aria-label="Search" value="{{ keyword }}">
<button class="btn btn-outline-success" type="submit">搜索</button>
</form>
增加作者页面
blogs/views.py
class AuthorView(IndexView):
def get_queryset(self):
queryset = super().get_queryset()
author_id = self.kwargs.get('owner_id')
return queryset.filter(owner_id=author_id)
urls.py
from blogs.views import (
AuthorView,
...
)
urlpatterns = [
...
url(r'author/(?P<owner_id>\d+)/$', AuthorView.as_view(), name='author'),
]
增加友链页面
config/views.py
from django.views.generic import ListView
from blogs.views import CommonViewMixin
from config.models import Link
class LinkListView(CommonViewMixin, ListView):
queryset = Link.objects.filter(status=Link.STATUS_NORMAL)
template_name = 'config/links.html'
context_object_name = 'link_list'
urls.py
urlpatterns = [
...
url(r'^links/$', LinkListView.as_view(), name='links'),
]
config/links.html
{% extends 'blogs/base.html' %}
{% block title %}友情链接{% endblock %}
{% block main %}
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">名称</th>
<th scope="col">网址</th>
</tr>
</thead>
<tbody>
{% for link in link_list %}
<tr>
<th scope="row">{{ forloop.counter }}</th>
<td>{{ link.title }}</td>
<td><a href="{{ link.href }}">{{ link.href }}</a></td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
增加评论页面
为了增加评论的范围,比如通过评论来完成友链的申请,即在友情链接页面下增加评论内容的展示和提交。因为我们的模型设计是针对Post对象的,因此需要稍作调整
*把Comment中的target改为CharField,里面存放着被评论内容的网址。就像很多其他社交评论所做的那样,只需要有一个能够唯一标识当前页面地址的标记即可。但这种方法存在的问题是,在admin后台无法处理权限,因为是多用户系统,理论上只有文章的作者才能删除当前文章下的评论
comment/models.py
# target = models.ForeignKey(Post, verbose_name='评论目标', on_delete=models.PROTECT, db_constraint=False)
target = models.CharField(max_length=100, verbose_name='评论目标')
# 记得数据迁移
comment/forms.py
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) < 10:
raise forms.ValidationError('内容长度怎么能这么短呢!!!')
return content
class Meta:
model = Comment
fields = ['nickname', 'email', 'website', 'content']
comment/models.py
# 增加一个 返回某篇文章下的所有有效评论 接口
@classmethod
def get_by_target(cls, target):
return cls.objects.filter(target=target, status=cls.STATUS_NORMAL)
blogs/views.py
class PostDetailView(CommonViewMixin, DetailView):
queryset = Post.latest_posts()
model = Post
template_name = 'blogs/detail.html'
context_object_name = 'post'
pk_url_kwarg = 'post_id'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'comment_form': CommentForm,
'comment_list': Comment.get_by_target(self.request.path),
})
return context
templates/blogs/detail.html
{% block main %}
...
<hr />
<div class="comment">
<form class="form-group" action="/comment/" method="POST">
{% csrf_token %}
<input name="target" type="hidden" value="{{ request.path }}" />
{{ comment_form }}
<input type="submit" value="写好了!" />
</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>
{% endblock %}
comment/views.py
from django.shortcuts import redirect
from django.views.generic import TemplateView
from comment.forms import CommentForm
class CommentView(TemplateView):
http_method_name = ['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
<!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>{{ message }}</li>
{% endfor %}
</ul>
<a href="javascript:window.history.back()">返回</a>
{% endif %}
</div>
</body>
</html>
urls.py
from comment.views import CommentView
urlpatterns = [
...
url(r'^comment/$', CommentView.as_view(), name='comment'),
]
抽象出评论模块组件和Mixin
上面的实现满足了基本功能,但是结构上不太合理,因为我们还需要在blogs/views.py中来操作comment的数据。这意味着,如果要在友链页面上增加评论,也得去修改View层的代码,需要把评论弄成一个即插即用的组件。要完成这个需求,就要用到Django的template tag(自定义标签)这部分接口
在任何需要添加评论的地方,只需要使用{% comment_block request.path %}即可。之所以叫comment_block。是因为comment是Django内置的tag,用来作大块代码的注释
comment/templatetags/comment_block.py
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),
}
上面的代码编写完之后,就可以把PostDetailView中新增的那个get_context_data去掉了,同时也可以去掉评论相关的引用了
接着编写模板,也就是上面用到的comment/block.html,这个模板里面的代码直接从blogs/detail.html中剪切粘贴过来即可。唯一需要处理的是target部分,因为是自定义标签,默认是没有request对象的
comment/block.html
<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="写好了!" />
</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>
因为是自定义的tag,所以需要在模板的最上面(但是需要在extends下面)增加{% load comment_block %},用来加载我们自定义的标签文件,然后在需要展示评论的地方增加{% comment_block request.path %}即可
修改最新评论模板
之前写过最新评论的模板,是基于外键关联Post的方式,现在修改为通用的方法
config/blocks/sidebar_comments.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
{% for comment in comments %}
<li>
<a href="{{ comment.target }}">{{ comment.target.title }}</a> | {{ comment.nickname }} : {{ comment.content }}
</li>
{% endfor %}
</ul>
</body>
</html>