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

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

  

Django Web开发【7】 投票与评论的更多相关文章

  1. Django web 开发指南 no such table:

    在学习django web开发指南时,发布新博客点击save后会有error提示:no such table balabalabala... 百度了一下说重新运行manage.py syncdb 就可 ...

  2. Django Web开发学习笔记(1)

    一.Python的标准类型 (1)bool型 >>> bool("") False >>> bool(None) False >>& ...

  3. Django Web开发指南笔记

    Django Web开发指南笔记 语句VS表达式 python代码由表达式和语句组成,由解释器负责执行. 主要区别:表达式是一个值,它的结果一定是一个python对象:如:12,1+2,int('12 ...

  4. Django快速开发之投票系统

    https://docs.djangoproject.com/en/1.8/intro/tutorial01/ 参考官网文档,创建投票系统. ================ Windows  7/1 ...

  5. [python] python django web 开发 —— 15分钟送到会用(只能送你到这了)

    1.安装python环境 1.1 安装python包管理器: wget https://bootstrap.pypa.io/get-pip.py sudo python get-pip.py   1. ...

  6. Django web开发【5】 实现标签功能

    标签tag在很多web2.0应用中都很常见,标签其实就是关联某些信息的一个关键字.打标签实际上就是给内容分配标签的过程,它通常由作者或者用户实现.标签之所有这么流行是因为它允许用户对自己创建的博客.图 ...

  7. Django Web开发【4】 用户注册与管理

    几乎所有的网站都提供了用户注册与管理功能,这一节,我们将讲解如何利用Django自身提供的用户认证系统实现用户注册与管理功能. 会话认证 在上一节中,我们学习了User数据模型,并用它来保存用户信息, ...

  8. Django Web开发【1】Django简介

    前言 看完<Django Book>之后, 总想找个实例来实战开发下,无奈国内Django的书籍相当少,只能从英文书籍中吸取养料,偶然之后得到Learning Website Develo ...

  9. Django Web开发基础环境配置流程

    创建虚拟环境 mkvirtualenv django_py3_1.11 -p python3 注意需要联网 安装Django 使用django 1.11.11版本,注意需要联网 pip install ...

随机推荐

  1. MediaChooser图库浏览器

    MediaChooser Android库 MediaChooser是一个库,浏览并选择视频和图像从SD卡.它可以用来显示文件中查看图像和视频(显示所有文件)或文件夹视图(显示文件分类).项目按日期, ...

  2. some knowledge t

    NSNumber static 看下面例子  gCount可以在Person 文件中使用  在main 中不行 @property()括号中可以填的属性 国际化 OC中的快捷键操作 operation ...

  3. WPF中的布局控件(转)

    WPF中使用Panel进行页面布局,Panel是一个抽象类,它作为所有Panel面板控件的基类.Panel并不是继承自Control类,而是直接从FrameworkElement继承.看Panel的继 ...

  4. api (三)文本字符输出 (转)

    在使用Win32编程时,我们常常要输出文本到窗口上,Windows所有的文本字符或者图形输出都是通过图形设备接口(GDI)进行的,Windows的三大核心组件之一的GDI32.dll封装了所有的文本和 ...

  5. c++中的const参数,const变量,const指针,const对象,以及const成员函数

    const 是constant 的缩写,“恒定不变”的意思.被const 修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性.所以很多C++程序设计书籍建议:“Use const whe ...

  6. 最大流算法----(SAP 和 EK)

    EK算法的核心 反复寻找源点 s 到汇点 t 之间的增广路径,若有,找出增广路径上每一段的最小值delta,若无,则结束. 寻找增广路径时用BFS来找,并且更新残留网的值. 找到delta后,则使最大 ...

  7. 学习python 一些错误记录

    1. TypeError: 'unicode' object is not callable当遇到这样的错误时候, 一般是属性当做方法调用了,比如,selenium 脚本, driver.title ...

  8. mysql触发器使用注意

    1.在创建触发器的时候,语句中避免在一个select语句查询多个列,例如使用select a,b from table,应该分开使用select语句, 例如select a from table  s ...

  9. ios 应用程序之间的跳转(内置程序的实现)

    ios 应用程序之间的跳转(内置程序的实现) 一个程序若要跳到另一个程序.需要在目标程序的plist文件里面修改: 打开info.plist,添加一项URL types 展开URLtypes,再展开I ...

  10. 2 kNN-K-Nearest Neighbors algorithm k邻近算法(一)

    给定训练数据样本和标签,对于某测试的一个样本数据,选择距离其最近的k个训练样本,这k个训练样本中所属类别最多的类即为该测试样本的预测标签.简称kNN.通常k是不大于20的整数,这里的距离一般是欧式距离 ...