• 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;
    }

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

      

  • 相关阅读:
    大数据学习总结(7)we should...
    大数据学习总结(6)what is our foucus
    洛谷P4632 [APIO2018] New Home 新家(动态开节点线段树 二分答案 扫描线 set)
    BZOJ5249: [2018多省省队联测]IIIDX(线段树 贪心)
    BZOJ2438: [中山市选2011]杀人游戏(tarjan)
    cf1072D. Minimum path(BFS)
    cf1072B. Curiosity Has No Limits(枚举)
    cf567E. President and Roads(最短路计数)
    六校联考考试反思
    BZOJ4010: [HNOI2015]菜肴制作(拓扑排序 贪心)
  • 原文地址:https://www.cnblogs.com/fireflow/p/5167378.html
Copyright © 2020-2023  润新知