为了让用户更好的发现和共享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; }
现在就可以查看我们刚刚实现的评论功能了。