BLOG
Enjoy when you can, and endure when you must.
NOV 12, 2012/Django
Django个人博客开发(五)

本文已历史长远,建议阅读最新的《用 Django 构建简易博客》系列文章

 

上一节中,我们注重完成了博客UI的设计和主页的定制以及开启了Django站点管理以方便博客的管理,不过我们的主页并没有完善,其中很多东西和链接都还没加入,这将在以后来优化。

光有主页肯定是不行的。其实,对于很多博客来说主页只会显示博客的内容提要,而浏览全文则应该进入相应的博客展示页面,其中还包含访客的评论等内容。这就是这一节的主要内容,下面我们就一步步实现。

这一部分的主要内容:

· 使用Django评论库

· 使用模板继承

· 创建博客展示页

 

一、开启Django评论库:

这一节中,我们要创建博客的展示页,其中会包含评论部分,因此在这里我们首先开启Django内置的评论库,其可以很方便地帮助我们搭建一个评论系统。启动的步骤如下:

   · 在settings.py中的INSTALLED_APPS中加入'django.contrib.comments';

   · 同步数据库:

 

[danny@localhost dblog]$ python manage.py syncdb

Creating tables ...

Creating table django_comments

Creating table django_comment_flags

Installing custom SQL ...

Installing indexes ...

Installed 0 object(s) from 0 fixture(s)

   · 在dblog下的urls.py中添加如下代码:

       urlpatterns = patterns('',

           ...

          url(r'^comments/', include('django.contrib.comments.urls')),

           ...

       )

再次打开Django站点管理界面可以看到增加了一条Comments,如下图所示:

           

现在即可正常使用了。

 

二、优化模板:

之前我们已经成功创建了主页的模板index.html,它工作的很好。但如果我们再创建一个模板,比如博客展示页,肯定会包含很多重复的内容,例如页眉的LOGO和导航栏以及页脚的一些内容等等。如果单纯的复制粘贴会造成大量的冗余代码,更好的方法是利用Django的模板继承。下面就让我们来优化一下以最大化的重用。

本质上来说,模板继承就是先构造一个基础框架模板,而后在其子模板中对它所包含站点公用部分和定义块进行重载。

要使用继承,首先定义的应该是基础模板,其中包含各模板的公用部分,该框架之后将由子模板所继承。

在我们的模板中,头和尾都应该放在基础模板中让各页面直接继承,而中间的内容才是各页面自己定制的,所以将我们的基础模板定义如下:

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>{% block title %}{% endblock %}</title>
  <link href="/static/css/dblog.css" rel="stylesheet" type="text/css" />
</head>
           
<body>
  <div class="container">
    <div class="header">
        <div id="top">
          <a href="/blog/"><img src="/static/images/LOGO.png" alt="LOGO" name="logo" width="180" height="60" id="logo" /></a>
          <div id="nav_div">
            <ul id="top_nav">
              <li><a href="/" class="a_normal">首页</a></li>
              <li><a href="/rss/" class="a_normal">订阅</a></li>
              <li><a href="/about/" class="a_normal">关于</a></li>
            </ul>
          </div>
        </div>
    <!-- end .header --></div>
           
    <div class="content_body">
      {% block content_body %}{% endblock %}
    </div>
           
    <div class="footer">
      <div id="footer_logo"></div>
      <div id="siteinfo">&copy; 2007 - 2012 DannyWork Project</div>
    <!-- end .footer --></div>
  <!-- end .container --></div>
</body>
</html>

将其保存在templates目录下并命名为base.html,下面再修改index.html使其成为base.html的一个子模板:

           
{% block title %}DannyBlog{% endblock %}
           
{% block content_body %}
<div class="main_body">
  {% for blog in blogs %}
    <div class="blog_body">
      <div class="blog_title"><a href="/detail/?id={{ blog.id }}">{{ blog.caption }}</a></div>
      <div class="blog_info1">
        <span class="blog_info1_list">
          <span class="li_small_1">分类:<a href="#">{{ blog.classification }}</a></span>
          <span class="li_small_1">发表时间:{{ blog.publish_time|date:"Y-m-d H:i" }}</span>
        </span>
      </div>
      <div class="blog_splitline"></div>
      <div class="blog_description">{{ blog.content }}</div>
      <div class="blog_info2">
        <span class="blog_info2_list">
          <span class="li_small_2">标签:{% for tag in blog.tags.all %}<a href="#">{{ tag.tag_name }}</a>{% endfor %}</span>
        </span>
      </div>
    </div>
  {% endfor %}
</div>
{% endblock %}

再次访问我们的主页,是不是和以前一样呢?那是肯定的,说明模板继承已经见成效了,下面我们来看看其中的一些重点:

在加载index.html模板时,模板引擎发现了{% extends %}标签,注意到该模板是一个子模板。模板引擎立即装载其父模板,即base.html。此时,模板引擎注意到base.html中的两个{% block %}标签,并用子模板的内容替换这些block。因此,引擎将会使用我们在{ block title %}中定义的标题,对{% block content %}也是如此。所以,网页标题一块将由{% block title %}替换,同样地,网页的内容一块将由{% block content %}替换。注意当子模板没有定义父模板中的block块时,模板系统将使用在父模板中定义的值。 父模板 {% block %} 标签中的内容总是被当作一条退路。继承并不会影响到模板的上下文。 换句话说,任何处在继承树上的模板都可以访问到你传到模板中的每一个模板变量。

 

三、创建博客展示页:

通过上一步中对模板的优化,现在我们可以很方便的创建博客展示页的模板了,其代码如下:

           
{% block title %} {{ blog.caption }} - DannyBlog {% endblock %}
           
{% load comments %}
           
{% block content_body %}
  <div class="main_body">
    <div class="blog_body">
      <div class="blog_title">{{ blog.caption }}</div>
      <div class="blog_info1">
        <span class="blog_info1_list">
          <span class="li_small_1">分类:<a href="#">{{ blog.classification }}</a></span>
          <span class="li_small_1">作者:<a href="#">{{ blog.author }}</a></span>
          <span class="li_small_1">发表时间:{{ blog.publish_time|date:"Y-m-d H:i" }}</span>
        </span>
      </div>
      <div class="blog_splitline"></div>
      <div class="blog_description">{{ blog.content }}</div>
    </div>
           
    <div class="comments">
      <a name="blog_comments" id="comments"></a>
      {% get_comment_count for blog as comment_count %}
      <div class="comments_nav">评论({{ comment_count }})</div>
      {% ifequal comment_count 0 %}
        <div class="comments_body">
          <div class="comment_container">没有评论
        </div>
      {% else %}
        {% get_comment_list for blog as comments %}
        <div class="comments_body">
            {% for comment in comments %}
            <div class="comment_container">
              <div class="comment_id"><a href="{{ comment.user_url }}"><img src="/static/images/hp.jpg" width="42" height="42" /></a></div>
              <div class="cmt_user_info">
                <span class="li_small_2">{{ comment.user_name }}</span>
                <span class="li_small_2">{{ comment.submit_date|date:"Y-m-d H:i"}}</span>
                <span class="reply_comment" id="{{ comment.user_name }}"><a href="#newcomment">回复</a></span>
              </div>
              <div id="comment_content">{{ comment.comment }}</div>
            </div>
            {% endfor %}
      {% endifequal %}
      </div>
           
      <div class="comments_nav">新的评论</div>
      {% get_comment_form for blog as blog_form %}
      <div class="comments_body">
        <div class="comment_form">
          <form class="form-horizontal" action="{% comment_form_target %}" method="post" id="cmt_sub_form">
            {% csrf_token %}
            {{ blog_form.object_pk }}
            {{ blog_form.content_type }}
            {{ blog_form.timestamp }}
            {{ blog_form.site }}
            {{ blog_form.submit_date }}
            {{ blog_form.security_hash }}
            <div class="control-group">
              <label class="control-label" for="id_name">用户名: </label>
              <div class="controls">
                <input type="text" id="id_name" class="input-xlarge" name="name" placeholder="请输入您的用户名" value="{{ user.username }}"/>
              </div>
            </div>
            <div class="control-group">
              <label class="control-label" for="id_email">E-mail: </label>
              <div class="controls">
                <input class="input-xlarge" id="id_email" type="email" name="email" placeholder="请输入您的邮箱地址" value="{{ user.email }}"/>
              </div>
            </div>
            <div class="control-group">
              <label class="control-label" for="id_email">网址: </label>
              <div class="controls">
                <input class="input-xlarge" id="id_url" type="url" name="url" placeholder="请输入您的网址" value="{{ user.url }}"/>
              </div>
            </div>
            <a name="newcomment" id="newcomment"></a>
            <div class="control-group">
              <label class="control-label" for="id_comment">评论: </label>
              <div class="controls">
                <textarea class="input-xlarge comment" id="id_comment" name="comment" placeholder="请输入评论内容"></textarea>
              </div>
            </div>
            <p style="display:none;"><label for="id_honeypot">如果你在该字段中输入任何内容,你的评论就会被视为垃圾评论。</label> <input type="text" name="honeypot" id="id_honeypot"></p>
            <div class="controls">
              <div class="form-actions">
                <input class="btn btn-info" id="submit_btn" type="submit" name="submit" value="提交"/>
                <input type="hidden" name="next" value="/detail/?id={{ blog.id }}"/>
              </div>
            </div>
          </form>
        </div>
      </div>
    </div>
  </div>
{% endblock %}

这里,我们需要了解一些基础:

   · 在模型中使用Django评论库,必须加载自定义标签:{% load comments %};

   · 获取评论数:{% get_comment_count for entry as comment_count %};

   · 获取评论对象的列表:{% get_comment_list for event as comment_list %},之后遍历该列表即可依次读取评论;

   · 对于评论表单,如果希望对其进行自定义,就必须使用get_comment_form来获取form对象并应用于模型:{% get_comment_form for event as form %};

   · 在form的action属性中,需要使用comment_form_target,这可以确保获得正确的表单提交地址:

       <form action="{% comment_form_target %}" method="post">;

   · 如果希望自定义提交后的跳转,可以在表单内包含一个隐藏的input元素:

       <input type="hidden" name="next" value="{% url my_comment_was_posted %}"/>;

   · 还有一个需要关注的就是{% csrf_token %}:

       Django默认开启了csrf中间件以提升安全性,所以需要在form表单内加入该语句,否则会导致提交失败(403)。

   · 如果用户提交的表单有误,comments库会默认加载源码中的comments/preview.html这个模板并返回要求用户修改,可以对其进行自定义以满足自己网站的需求。

同样,将其保存在templates目录下,并命名为detail.html。接下来修改urls.py,加入如下内容:

    url(r'^detail/$', 'blog_detail'),
)

最后打开blog下的views.py,增加相应的视图函数:

           
def blog_detail(request):
    if request.method == 'GET':
        id = request.GET.get('id', '');
        try:
            blog = Article.objects.get(id=id)
        except Article.DoesNotExist:
            raise Http404
        return render_to_response("detail.html", {"blog": blog}, context_instance=RequestContext(request))
    else:
        raise Http404

这里,我们通过关键字id来获知用户希望查阅的文章。

到这里一个简单的展示页就算完成了,访问我们的主页,并点击标题的链接应该就可以跳转到指定的博客展示页面:

       

尝试评论一下:

    

It works!

这一小节内容就结束了,如需系统学习,可以参考《The Django Book》第四、七、九章以及Django官方文档内有关comments库和csrf的介绍。

COMMENTS
20/12From zqq

感謝

01/05From 笑话

积极

01/05From 笑话

谢谢

13/03From test

<script>alert('afd')</script>

27/08From sunsy

tinghaode

28/07From Qiu

tets ajax

28/06From Janvn

为什么我在settings.py中的INSTALLED_APPS中加入'django.contrib.comments',同步数据库的时候,显示ImportError: No module named

01/06From 125.*.*.37

博主还来看留言吗? 如果可以的话加一下我的Q 我正在做这个 太多不会了 245386954 谢谢了

11/05From Good

1.dblog.css 没有

2.
{% get_comment_count for blog as comment_count %}
出现:'str' object has no at

21/04From lany2015

有没有人遇到问题:int() argument must be a string or a number, not 'tuple'.

Error during template renderin

11/02From letfly

赞!

11/02From letfly

验证一下

01/02From ian

如果有同学遇到了TypeError 'str' object is not callable这个报错的话可以检查一下自己的url.py中detail/对应的view的路经写没写对,如果完全按照文章中的

24/01From max

{% get_comment_count for entry as comment_count %}
为什么总是出现'str' object has no attribute '_meta' 啊

01/12From naxiehuaer

这个网站设计的很清新,赞

18/11From zkwolf

。。将在1.8版本移除,之前的版本都可以用只不过会跳警告

18/11From jack

写的不错顶一下

13/11From wind

哈哈,不错啊~!感谢楼主!

03/11From bricks

django.contrib.comments is deprecated and will be removed before Django 1.8.", RemovedInDjango18Warn

30/10From CI_Knight

博主, 现在 用django的话, 版本有要求吗? 我在用1.7的学习. 在添加 comments 的时候,出现了 fields.E300: Field defines a relation with

09/09From chenxc

请问怎样 让评论系统 不需要 输入 我的站点 字段

29/08From asap

同求

27/08From asap

路过,楼主自己写的网站很棒!

22/07From jc

多级评论是怎样的

03/06From qq

ddd

10/05From jwfy

我来测试一下这个评论的表单提交。。。。

28/02From Danny

你好~感谢你的支持!这篇博客已经很老了,也只是我初次尝试的记录。也许我们可以更多的交流以下~

27/02From jwfy

学习了,不过这个留言 ,我是希望已提交 不需要转跳 直接显示在这个博客的页面。。。

LEAVE COMMNT