为了让用户更好的发现和共享bookmark,可以提供投票与评论功能。现在我们的主页还是一个很简单的欢迎页面,我们可以让用户在主页共享自己的bookmark,此外,用户还可以对主页的bookmark进行投票以及评论,最后创建一个页面,显示十条最受欢迎的bookmark。
在主页分享Bookmarks
当保存bookmark的时候,让用户选择是否要在主页进行分享,当bookmark被分享之后,用户就可以对它进行投票,此外,我们还将创建一个页面,显示十条最受欢迎的bookmark。
实现这几个功能的步骤如下:
- 创建一个数据模型,用来保存分享在主页的bookmark。
- 修改bookmark提交表单,让用户能够选择是否在主页分享bookmark。
- 修改主页,给bookmark添加投票按钮。
- 创建投票的视图函数。
SharedBookmark数据模型
当bookmark在主页共享的时候,需要保存一下信息:
- bookmark被分享的时间
- bookmark的投票数
- 投票给bookmark的用户信息。
创建SharedBookmark数据模型,编辑bookmarks/models.py,添加如下代码:
class SharedBookmark(models.Model):
bookmark = models.ForeignKey(Bookmark, unique=True)
date = models.DateTimeField(auto_now_add=True)
votes = models.IntegerField(default=1)
users_voted = models.ManyToManyField(User)
def __str__(self):
return '%s, %s' % self.bookmark, self.votes
bookmark字段类型为外键类型,并且具有唯一性,因为同一个bookmark只能被分享一次。date的字段类型为models.DateTimeField,可以用来保存日期时间类型,参数auto_now_add告诉Django将这个字段的值设为当前的日期或时间。vote字段类型为models.IntegerField,默认值为1。user_voted使用多对多字段类型。
编辑完之后,不要忘记执行下面的命令:
$ python manage.py syncdb
修改Bookmark提交表单
在bookmark提交表单中放置一个复选框,如果选中,则分享到主页。编辑bookmarks/forms.py中的BookmarkSaveForm:
class BookmarkSaveForm(forms.Form):
url = forms.URLField(
label='URL',
widget=forms.TextInput(attrs={'size': 64})
)
title = forms.CharField(
label='Title',
widget=forms.TextInput(attrs={'size': 64})
)
tags = forms.CharField(
label='Tags',
required=False,
widget=forms.TextInput(attrs={'size': 64})
)
share = forms.BooleanField(
label='Share on the main page',
required=False
)
给BookmarkForm添加share字段,字段类型为BooleanField,它会被渲染为复选框。
修改bookmarks/views.py,添加如下高亮部分代码:
def _bookmark_save(request, form):
# Create or get link.
link, dummy = Link.objects.get_or_create(
url=form.cleaned_data['url']
)
# Create or get bookmark.
bookmark, created = Bookmark.objects.get_or_create(
user=request.user,
link=link
)
# Update bookmark title.
bookmark.title = form.cleaned_data['title']
# If the bookmark is being updated, clear old tag list.
if not created:
bookmark.tag_set.clear()
# Create new tag list.
tag_names = form.cleaned_data['tags'].split()
for tag_name in tag_names:
tag, dummy = Tag.objects.get_or_create(name=tag_name)
bookmark.tag_set.add(tag)
# Share on the main page if requested.
if form.cleaned_data['share']:
shared_bookmark, created = SharedBookmark.objects.get_or_create(
bookmark=bookmark
)
if created:
shared_bookmark.users_voted.add(request.user)
shared_bookmark.save()
# Save bookmark to database and return it.
bookmark.save()
return bookmark
如果用户选择分享bookmark,我们就使用get_or_create来检查这个bookmark是否已经保存到SharedBookmark当中,如果没有,则新建,如果是新建的,就将当前用户保存到给这个bookmark投票的用户列表中。
查看和投票
现在我们已经创建了SharedBookmark数据模型,那么获取最受欢迎的bookmark列表也就很简单了。首先编辑bookmarks/views.py中的main_page视图:
def main_page(request):
shared_bookmarks = SharedBookmark.objects.order_by(
'-date'
)[:10]
variables = RequestContext(request, {
'shared_bookmarks': shared_bookmarks
})
return render_to_response('main_page.html', variables)
使用order_by方法可以对查询到的结果进行排序。
接下来需要修改主页模板,以便显示分享的bookmark。我们之前都是使用bookmark_list.html来展示bookmarks,但是分享的bookmark
与普通的bookmark并不同,所以我们需要单独编写一个模板。创建templates/shared_bookmark_list.html。
{% if shared_bookmarks %}
<ul class="bookmarks">
{% for shared_bookmark in shared_bookmarks %}
<li>
<a href="{{ shared_bookmark.bookmark.link.url }}" class="title">
{{ shared_bookmark.bookmark.title|escape }}</a>
<br />
Posted By:
<a href="/user/{{ shared_bookmark.bookmark.user.username }}/" class="username">
{{ shared_bookmark.bookmark.user.username }}</a> |
<span class="vote-count">Votes:
{{ shared_bookmark.votes }}</span>
</li>
{% endfor %}
</ul>
{% else %}
<p>No bookmarks found.</p>
{% endif %}
创建完shared_bookmark_list.html,再在main_page.html中包含这个模板:
{% extends "base.html" %}
{% block title %}Welcome to Django Bookmarks{% endblock %}
{% block head %}Welcome to Django Bookmarks{% endblock %}
{% block content %}
{% if user.username %}
<p>Welcome {{ user.username }}!
Here you can store and share bookmarks!</p>
{% else %}
<p>Welcome anonymous user!
You need to <a href="/login/">login</a>
before you can store and share bookmarks.</p>
{% endif %}
<h2>Bookmarks Shared by Users</h2>
{% include 'shared_bookmark_list.html' %}
{% endblock %}
修改完之后,就可以看到新的主页视图了,但是投票功能还不可用。
接下来编辑urls.py,添加下面的url:
urlpatterns = patterns('',
# Account management
(r'^save/$', bookmark_save_page),
(r'^vote/$', bookmark_vote_page),
)
然后编辑bookmarks/views.py,添加如下代码:
@login_required
def bookmark_vote_page(request):
if request.GET.has_key('id'):
try:
id = request.GET['id']
shared_bookmark = SharedBookmark.objects.get(id=id)
user_voted = shared_bookmark.users_voted.filter(
username=request.user.username
)
if not user_voted:
shared_bookmark.votes += 1
shared_bookmark.users_voted.add(request.user)
shared_bookmark.save()
except ObjectDoesNotExist:
raise Http404('Bookmark not found.')
if request.META.has_key('HTTP_REFERER'):
return HttpResponseRedirect(request.META['HTTP_REFERER'])
return HttpResponseRedirect('/')
给视图函数添加@login_required修饰器,因为只有已登录用户才可以进行投票。
如果顺序执行,最后将重定向至用户投票之前显示的页面,这是通过HTTP头部HTTP_REFERER来实现的。当用户单击链接时,会发送当前页面的URL到服务器。HTTP头部都保存在request.META中。有些浏览器不支持这个头部,所以先检查是否含有这个头部,如果没有则返回主页。
现在投票的视图函数已经实现了,只需要在主页中添加投票链接就可以了。编辑shared_bookmark_list.html:
{% if shared_bookmarks %}
<ul class="bookmarks">
{% for shared_bookmark in shared_bookmarks %}
<li>
<a href="/vote/?id={{ shared_bookmark.id }}" class="vote">[+]</a>
<a href="{{ shared_bookmark.bookmark.link.url }}" class="title">
{{ shared_bookmark.bookmark.title|escape }}</a>
<br />
Posted By:
<a href="/user/{{ shared_bookmark.bookmark.user.username }}/" class="username">
{{ shared_bookmark.bookmark.user.username }}</a> |
<span class="vote-count">Votes:
{{ shared_bookmark.votes }}</span>
</li>
{% endfor %}
</ul>
{% else %}
<p>No bookmarks found.</p>
{% endif %}
这样,整个功能就完成了。
统计页面
实现一个统计页面,显示十条最受欢迎的bookmarks。通过投票数进行排序,但是只显示最后一天最受欢迎的bookmark。
首先创建视图函数popular_page,编辑bookmarks/views.py:
from datetime import datetime, timedelta
def popular_page(request):
today = datetime.today()
yesterday = today - timedelta(1)
shared_bookmarks = SharedBookmark.objects.filter(
date__gt=yesterday
)
shared_bookmarks = shared_bookmarks.order_by(
'-votes'
)[:10]
variables = RequestContext(request, {
'shared_bookmarks': shared_bookmarks
})
return render_to_response('popular_page.html', variables)
这个视图比main_page更加复杂。timedelta对象代表两个时间之间的时间差。
创建templates/popular_page.html:
{% extends "base.html" %}
{% block title %}Popular Bookmarks{% endblock %}
{% block head %}Popular Bookmarks{% endblock %}
{% block content %}
{% include 'shared_bookmark_list.html' %}
{% endblock %}
这个模板相当简单直接。接着再添加一个url:
urlpatterns = patterns('',
# Browsing
(r'^$', main_page),
(r'^popular/$', popular_page),
(r'^user/(\w+)/$', user_page),
(r'^tag/([^\s]+)/$', tag_page),
(r'^tag/$', tag_cloud_page),
(r'^search/$', search_page),
)
最后给导航菜单中添加popular导航链接:
[...]
<div id="nav">
<a href="/">home</a> |
<a href="/popular/">popular</a> |
{% if user.is_authenticated %}
<a href="/save/">submit</a> |
<a href="/search/">search</a> |
<a href="/user/{{ user.username }}/">
{{ user.username }}</a> |
<a href="/logout/">logout</a>
{% else %}
<a href="/login/">login</a> |
<a href="/register/">register</a>
{% endif %}
</div>
[...]
现在用户就可以查看最受欢迎的bookmarks了。
对bookmarks进行评论
实现评论功能包含以下步骤:
- 安装comments应用,然后创建相应数据表
- 使用comments库提供的模板标签展示评论信息
- 创建评论提交表单以及成功页面。
安装comments库
Django自身提供了评论功能,它包含在django.contrib.comments中,激活它需要进行下面操作,编辑settings.py:
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.comments',
'django_bookmarks.bookmarks'
)
然后执行下面命令创建数据表:
$ python manage.py syncdb
接着在url.py中添加相应url:
urlpatterns = patterns('',
# Comments
(r'^comments/', include('django.contrib.comments.urls.comments')),
)
这里使用include将comments库中的URL定义包含进来。
为评论创建视图函数
这个视图函数使用分享的bookmark ID作为参数,然后展示分享的bookmark,它的评论以及提交新评论的表单。首先添加url,编辑urls.py:
urlpatterns = patterns('',
# Browsing
(r'^$', main_page),
(r'^popular/$', popular_page),
(r'^user/(\w+)/$', user_page),
(r'^tag/([^\s]+)/$', tag_page),
(r'^tag/$', tag_cloud_page),
(r'^search/$', search_page),
(r'^bookmark/(\d+)/$', bookmark_page),
)
接着编辑bookmarks/views.py:
def bookmark_page(request, bookmark_id):
shared_bookmark = get_object_or_404(
SharedBookmark,
id=bookmark_id
)
variables = RequestContext(request, {
'shared_bookmark': shared_bookmark
})
return render_to_response('bookmark_page.html', variables)
然后创建bookmark_page.html模板:
{% extends "base.html" %}
{% block title %}Bookmark:
{{ shared_bookmark.bookmark.title|escape }}{% endblock %}
{% block head %}
<a href="/vote/?id={{ shared_bookmark.id }}" class="vote">[+]</a>
<a href="{{ shared_bookmark.bookmark.link.url }}" class="title">
{{ shared_bookmark.bookmark.title|escape }}</a>
{% endblock %}
{% block content %}
Posted By:
<a href="/user/{{ shared_bookmark.bookmark.user.username }}/"
class="username">
{{ shared_bookmark.bookmark.user.username }}</a> |
<span class="vote-count">Votes: {{ shared_bookmark.votes }}</span>
{% endblock %}
展示评论详情与评论提交表单
Django提供的comments是实现评论功能异常简单。comments提供了三个模板标签:
- get_comments_count 返回当前页面中评论的数目
- get_comment_list 返回当前页面中评论的列表
- comment_form 评论提交表单
默认情况下模板是不支持这些标签的,要激活它们,必须先使用下面这个标签:
{% load comments %}
load标签通常用于激活自定义的模板标签。
上面三个标签都需要提供以下参数:
- 接受评论的对象类型,格式如下:application.model(全部小写)。
- 接受评论的对象ID。
所以如果你想获取指定分享的bookmark的评论数,需要使用下面的代码:
{% get_comment_count for bookmarks.sharedbookmark shared_bookmark.id as comment_count %}
现在模板变量comment_count的值就是当前分享的bookmark所具有的评论数。
同样的,为了获取当前bookmark的评论数,需要使用以下代码:
{% get_comment_list for bookmarks.sharedbookmark shared_bookmark.id as comment_list %}
现在模板变量comment_list包含了当前页面中所有评论组成的列表,每个评论具有以下属性:
- user 进行评论的用户对象
- submit_date 提交评论的日期
- comment 评论内容
- ip_address 提交评论的IP地址
最后,展示评论提交表单:
{% comment_form for bookmarks.sharedbookmark shared_bookmark.id %}
将上面的代码应用到templates/bookmark_page.html中:
{% extends "base.html" %}
{% load comments %}
{% block title %}Bookmark:
{{ shared_bookmark.bookmark.title|escape }}{% endblock %}
{% block head %}
<a href="/vote/?id={{ shared_bookmark.id }}" class="vote">[+]</a>
<a href="{{ shared_bookmark.bookmark.link.url }}" class="title">
{{ shared_bookmark.bookmark.title|escape }}</a>
{% endblock %}
{% block content %}
Posted By:
<a href="/user/{{ shared_bookmark.bookmark.user.username }}/" class="username">
{{ shared_bookmark.bookmark.user.username }}</a> |
<span class="vote-count">Votes: {{ shared_bookmark.votes }}</span>
<h2>Comments</h2>
{% get_comment_count for bookmarks.sharedbookmark shared_bookmark.id as comment_count %}
{% get_comment_list for bookmarks.sharedbookmark shared_bookmark.id as comment_list %}
{% for comment in comment_list %}
<div class="comment">
<p><b>{{ comment.user.username }}</b> said:</p>
{{ comment.comment|escape|urlizetrunc:40|linebreaks }}
</div>
{% endfor %}
<p>Number of comments: {{ comment_count }}</p>
{% comment_form for bookmarks.sharedbookmark shared_bookmark.id %}
{% endblock %}
上面使用了一些新的过滤器:
- escape 转义,将HTML标签转义成HTML实体。
- urlizetrunc 将URL转换成链接
- linebreaks 将行转换成<p>与<br/>
创建评论模板
django的模板库要求我们提供两个模板,一个是评论提交表单,另一个是成功提交之后的页面。这些模板应该位于templates/comments/中。
首先创建评论提交表单,在templates/comments/中创建form.html:
{% if user.is_authenticated %}
<form action="/comments/post/" method="post">
<p><label>Post a comment:</label><br />
<textarea name="comment" rows="10" cols="60"></textarea></p>
<input type="hidden" name="options" value="{{ options }}" />
<input type="hidden" name="target" value="{{ target }}" />
<input type="hidden" name="gonzo" value="{{ hash }}" />
<input type="submit" name="post" value="submit comment" />
</form>
{% else %}
<p>Please <a href="/login/">log in</a> to post comments.</p>
{% endif %}
如果用户已经登录,则展示评论提交评论,如果没登录,则显示登录链接。表单中action以及字段的值都是从comments库的文档中获取的。
接下来,创建成功评论之后显示的模板,这个模板中包含一个object对象,指代接受评论的对象,最好是在页面中提供返回分享的bookmark页面,所以在templates/comments/中创建posted.html。
{% extends "base.html" %}
{% block title %}Comment Posted Successfully{% endblock %}
{% block head %}Comment Posted Successfully{% endblock %}
{% block content %}
<p>Thank you for contributing.</p>
{% if object %}
<p><a href="/bookmark/{{ object.id }}/">
View your comment</a></p>
{% endif %}
{% endblock %}
现在,我们就实现了评论功能,但是还需要给评论页面添加链接,编辑templates/shared_bookmark_list.html:
{% if shared_bookmarks %}
<ul class="bookmarks">
{% for shared_bookmark in shared_bookmarks %}
<li>
<a href="/vote/?id={{ shared_bookmark.id }}" class="vote">[+]</a>
<a href="{{ shared_bookmark.bookmark.link.url }}" class="title">
{{ shared_bookmark.bookmark.title|escape }}</a>
<br />
Posted By:
<a href="/user/{{ shared_bookmark.bookmark.user.username }}/" class="username">
{{ shared_bookmark.bookmark.user.username }}</a> |
<span class="vote-count">Votes:
{{ shared_bookmark.votes }}</span> |
<a href="/bookmark/{{ shared_bookmark.id}}/">Comments</a>
</li>
{% endfor %}
</ul>
{% else %}
<p>No bookmarks found.</p>
{% endif %}
最后,给评论添加样式,编辑/static/style.css:
.comment {
margin: 1em;
padding: 5px;
border: 1px solid #000;
}
现在就可以查看我们刚刚实现的评论功能了。