基于Django构建Blog(08)-Templates

对于Templates的实现,可以使用Bootstrap中的资源(html/css/js),稍加修改即可。另外,这几个网站也提供了大量的优秀资源,关注一下: DjangoTemplates是可以继承的,对于上一篇中创建的几个模板文件:home.html, archives.html, articles_of_tag.html, articles_of_category.html, article_detail.html来说,应该存在一个基础模板base.html,该模板中定义了隶属于本站的所有页面都应该具备的一般性内容,例如:导航部分,head,footer等,它是所有页面的框架,而articles_of_tag.html和articles_of_category.html的显示效果其实是与home.html完全一样的,home.html应该是它俩的父模板。

另外,根据Haystack的要求,搜索结果的展示,需要我们提供一个名叫search.html的模板,要放在templates/search目录下。我们这个搜索搜的是文章,搜索结果其实就是文章列表,很容易想到,这个search.html也应该像上面的几个模板一样,直接去继承home.html。但这里要注意的是,articles_of_tag.html和articles_of_category.html之所以能这么干,是因为他们三者所使用的与文章相关的变量名都是一样的,也就是说A模板继承了B模板的内容,A的View在给A传递数据的时候,用的这个变量B也认识,于是在渲染的时候,就把A中继承的B的内容给渲染出来了。所以说search.html直接继承home.html是没意义的,因为search.html的View是Haystack实现的,它传递数据用的变量名未必就是articles,而且变量的使用方法也可能不一样。

其实我们无非就是想复用一下home.html中展示文章信息的代码而已,可以把这块代码抽出来放到一个单独的模板文件article_info.html中,在search.html文件中include进来就行了。因为在include一个模板文件的时候是可以对被include的模板中的变量进行“赋值”的,这就解决了上面说的问题,变量名不同没问题,把值传递过去就OK了。

模板关系如下: Bootstrap提供的示例中,找一个与设计中的草图相近的,其实就有专门的Blog示例:

查看这个页面的源码,会发现里面引用了一些外部文件,有几个文件是在本地路径下的,需要把这些本地路径的文件下载到static/css或static/js目录中(.css文件放到css目录,.js文件放到js目录),另外,这个线上文件jquery.min.js也要下载下来。为方便起见,这里列出了所有需要下载的文件链接:

然后,修改这个示例的源码,作为base模板,base以及其它模板的代码如下:

base.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
{% load blog_extras %}
<!DOCTYPE html>
<html lang="en">
 <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content=""> 
    <meta name="author" content="">
    <title>xblog</title>
    {% block custom_styles %}
    {% endblock custom_styles %}
    <!-- Bootstrap core CSS -->
    <link href="/static/css/bootstrap.min.css" rel="stylesheet">
    <!-- Custom styles for this template -->
    <link href="/static/css/blog.css" rel="stylesheet">
    <!-- Just for debugging purposes. Don't actually copy these 2 lines! -->
    <!--[if lt IE 9]>
      <script src="/static/js/ie8-responsive-file-warning.js">
      </script>
    <![endif]-->
    <script src="/static/js/ie-emulation-modes-warning.js">
    </script>
    <!-- 
    HTML5 shim and Respond.js for IE8 support 
    of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js">
      </script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js">
      </script>
    <![endif]-->
 </head>
 <body>
    <div class="blog-masthead">
      <div class="container">
        <nav class="blog-nav">
          <a class="blog-nav-item 
            {% if column == 'home' %}active{% endif %}"
            href="{% url 'home' %}">Home
          </a>
          <a class="blog-nav-item 
            {% if column == 'archives' %}active{% endif %}"
            href="{% url 'archives' %}">Archives
          </a>
        </nav>
      </div>
    </div>
    <div class="container">
      <div class="row">
        <div class="col-sm-8 blog-main">
        {% block main %}
        {% endblock main %}
        </div>
        <div class="col-sm-3 col-sm-offset-1 blog-sidebar">
          <div class="sidebar-module">
            <h4>Search</h4>
            <form action="/search/" method="get">
              <input type="text" name="q">
              <input type="submit" value="GO!">
            </form>
          </div>
          <div class="sidebar-module">
            <h4>Tag</h4>
            {% GetTags %} 
          </div>
          <div class="sidebar-module">
            <h4>Category</h4>
            {% GetCategories %}
          </div>
        </div>
      </div>
    </div>
    <footer class="blog-footer">
      <div class="row">
        <div class="col-lg-12">
          <p>Copyright &copy; xblog 2014</p>
        </div>
      </div>
    </footer>
    <!-- Bootstrap core JavaScript
    ================================================== -->
    <!-- Placed at the end of the document so the pages load faster -->
    <script src="/static/js/jquery.min.js"></script>
    <script src="/static/js/bootstrap.min.js"></script>
    <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
    <script src="/static/js/ie10-viewport-bug-workaround.js"></script>
 </body>
</html>

该模板load了一个叫做blog_extras的模块,这个模块中注册了两个自定义的template tag:GetCategories和GetTags,有关blog_extras的详细说明在下一篇中给出。

home.html

1
2
3
4
5
6
7
8
{% extends "base.html" %}
{% load django_markdown %}
{% block main %}
{% for article in articles %}
 {% include "article_info.html" with article=article %}
{% endfor %}
{% include "articles_pagination.html" with is_query_result=False page=articles%}
{% endblock main %}

article_info.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{% load django_markdown %}
{% if article.is_publish %}
<div class="blog-post">
 <h2 class="blog-post-title">
    <a href="{{ article.GetAbsoluteURL }}">
      {{ article.title }}
    </a>
 </h2>
 <p class="blog-post-meta">
    {{ article.create_date | date:"Y/m/d" }}
    by {{ article.author }}
 </p>
 <p>
    {{ article.text | markdown | safe | truncatewords_html:20 }}
 </p>
 <p>
    <div class="readmore">
      <a href="{{ article.GetAbsoluteURL }}">
        <span class="glyphicon glyphicon-plus">
        </span> more
      </a>
    </div>
 </p>
 <p class="blog-post-meta">
    Category: {{ article.GetCategory }}<br>
    Tag:
    {% for tag in article.GetTags %}
      {{ tag.name }}{% if not forloop.last %},{% endif %}
    {% endfor %}
 </p>
 <hr class="line">
</div>
{% endif %}

articles_pagination.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<nav>
 <ul class="pagination">
    {% if page.has_other_pages %}
    <li>
      {% if page.has_previous %}
      <a href="?{% if is_query_result %}q={{ query }}&amp;{% endif %}page={{ page.previous_page_number }}"
         aria-label="Previous">
        <span aria-hidden="true">&laquo;</span>
      </a>
      {% endif %}
    </li>
    {% for page_num in page.paginator.page_range %}
      {% if page.number > 4 %}
        {% if page_num > page.number|add:"-4" %}
          {% if page_num < page.number|add:"3" %}
            <li class="
              {% if page_num == page.number %}
              active{% endif %}">
              <a href="?{% if is_query_result %}q={{ query }}&amp;{% endif %}page={{ page_num }}">
                {{ page_num }}<span class="sr-only">(current)</span>
              </a>
            </li>
          {% endif %}
        {% endif %}
      {% elif page_num < 7 %}
      <li class="{% if page_num == page.number %}active{% endif %}">
        <a href="?{% if is_query_result %}q={{ query }}&amp;{% endif %}page={{ page_num }}">
          {{ page_num }}<span class="sr-only">(current)</span>
        </a>
      </li>
      {% endif %}
    {% endfor %}
    <li>
      {% if page.has_next %}
      <a href="?{% if is_query_result %}q={{ query }}&amp;{% endif %}page={{ page.next_page_number }}" aria-label="Next">
        <span aria-hidden="true">&raquo;</span>
      </a>
      {% endif %}
    </li>
    {% endif %}
 </ul>
</nav>

这是对页码条的实现,这里的设定是:页码条最多显示6个页码,只有1页的话,不显示页码条

archives.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
{% extends "base.html" %}
{% block main %}
<div>
{% for year, articles in archives.items %}
    <h3>{{ year }}</h3>
    <ul>
    {% for article in articles %}
    <li>
      <div>
        <h4>
          <a href="{{ article.GetAbsoluteURL }}">
          {{ article.title }}
          </a>
        </h4>
      </div>
    </li>
    {% endfor %}
    </ul>
{% endfor %}
</div>
{% endblock main %}

article_detail.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
{% extends "base.html" %}
{% load django_markdown django_markdown_static %}
{% block custom_styles %}
<link href="{% static 'django_markdown/preview.css' %}" rel="stylesheet">
{% endblock custom_styles %}

{% block main %}
<h2 class="blog-post-title">
{{ object.title }}
</h2>
{{ object.text | markdown }}

<!—
================= Disqus begin ================= -->

<div id="disqus_thread"></div>
<script type="text/javascript">

 /* * * CONFIGURATION VARIABLES * * */
 var disqus_shortname = 'xus123';

 /* * * DON'T EDIT BELOW THIS LINE * * */
 (function() {
    var dsq = document.createElement('script');
      dsq.type = 'text/javascript'; dsq.async = true;
    dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
    (document.getElementsByTagName('head')[0]
      || document.getElementsByTagName('body')[0]).appendChild(dsq);
 })();
</script>
<noscript>
 Please enable JavaScript to view the
 <a href="https://disqus.com/?ref_noscript" rel="nofollow">
    comments powered by Disqus.
 </a>
</noscript>

<!—
================= Disqus end ================= -->
{% endblock main %}

评论功能借助于第三方评论托管平台Disqus来实现,Disqus begin和end之间的代码是按照Disqus的使用说明添加的,详细内容可到官网查看。需要注意的是 var disqus_shortname = 'xus123' 中的xus123是我的Disqus账号,现实当中需要替换为你自己的Disqus账号,其他地方不需要修改。

除了Disqus,同类的服务平台还有:JiaThis灯鹭等,使用方法类似。

articles_of_tag.html

1
{% extends "home.html" %}

articles_of_category.html

1
{% extends "home.html" %}

search.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{% extends "home.html" %}
{% block main %}
{% if query %}
 {% for result in page.object_list %}
    {% include "article_info.html" with article=result.object %}
    {% empty %}<p>No results found !</p>
 {% endfor %}
 {% include "articles_pagination.html" with is_query_result=True %}
{% else %}
<p>No search query given !</p>
{% endif %}
{% endblock main %}

有关Haystack的使用,详见搜索中的介绍。


Prev-基于Django构建Blog(07)-Views
Next-基于Django构建Blog(09)-Templates续