Django Web开发【7】 投票与评论

  为了让用户更好的发现和共享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;
}

  现在就可以查看我们刚刚实现的评论功能了。

  

 

posted @ 2016-01-29 23:56  楚狂人阿飞  阅读(278)  评论(0编辑  收藏  举报