Django搭建个人博客:使用 Bootstrap 4 改写模板文件

9363阅读 · 35评论 · 2018/09/16发布   前往评论

上一章我们的网站页面实在太粗糙,你肯定不会拿来做真正的博客首页。因此这章我们要借助Bootstrap的力量,改写一个大气的博客。

配置Bootstrap 4

Bootstrap是用于网站开发的开源前端框架(“前端”指的是展现给最终用户的界面),它提供字体排印、窗体、按钮、导航及其他各种组件,旨在使动态网页和Web应用的开发更加容易。

Bootstrap有几个版本都比较流行,我们选择最新版本的Bootstrap 4:下载地址,并解压。

然后在项目根目录下新建目录static/bootstrap/,用于存放Bootstrap静态文件。静态文件通常指那些不会改变的文件。Bootstrap中的css、js文件,就是静态文件。

把刚才解压出来的cssjs两个文件夹复制进去。

因为bootstrap.js依赖 jquery.js 和 popper.js 才能正常运行,因此这两个文件我们也需要一并下载保存。附上官网下载链接(进入下载页面,复制粘贴代码到新文件即可):

2018-10-29 新增:

不清楚popper.js如何下载的戳这个链接:

https://unpkg.com/popper.js@1.14.4/dist/umd/popper.js

进去后页面显示很长一段代码,把这段代码全部拷贝;在项目中新建名叫popper.js的文件,把刚拷贝的代码复制进去就可以了。很多开源js文件都是通过这样的方式下载。

现在我们的static/目录结构像这样:

my_blog
│
├─article
└─my_blog
│ ...
└─static
    └─bootstrap
    │   ├─css # 文件夹
    │   └─js # 文件夹
    └─jquery
    │   └─jquery-3.3.1.js # 文件
    └─popper
        └─popper-1.14.4.js # 文件

因为在Django中需要指定静态文件的存放位置,才能够在模板中正确引用它们。因此在settings.py的末尾加上:

my_blog/settings.py

...

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, "static"),
)

再确认一下settings.py中有没有STATIC_URL = '/static/'字段,如果没有把它也加在后面。

编写模板

在根目录下的templates/中,新建三个文件:

  • base.html是整个项目的模板基础,所有的网页都从它继承;

  • header.html是网页顶部的导航栏;

  • footer.html是网页底部的注脚。

这三个文件在每个页面中通常都是不变的,独立出来可以避免重复写同样的代码,提高维护性。

现在templates\的结构像下面这个样子:

templates
│
├─base.html
├─header.html
├─footer.html
└─article
    └─list.html # 上一章创建的

加上之前的list.html,接下来就要重新写这4个文件了。

因为前端知识非常博大精深,并且也不是Django学习的重点,本教程不会展开篇幅去讲。如果之前没接触过前端知识也没关系,这里可以先复制粘贴,不影响后面Django的学习。

你可以试着改写其中的某段代码,看看会对页面产生什么样的影响;遇到不懂的就在Bootstrap官方文档找答案。慢慢就会明白它的运行机制,毕竟Bootstrap真的是非常简单易用的工具。

2018-10-29 新增:

Bootstrap是非常优秀的前端框架,上手简单,所以很流行。

官网是最权威的文档。你可以在官方网站上进行系统的学习:https://getbootstrap.com/docs/4.1/getting-started/introduction/

通篇去看Bootstrap文档会非常枯燥的,因此建议你可以像查字典一样的,需要用哪个模块,就到官网上找相关的代码,修改一下拷贝到你的项目中就可以了。用多了自然会明白每个字段的作用。

这里会一次性写大量代码,不要着急慢慢看,理解了就很简单了。

首先写base.html

templates/base.html

<!-- 载入静态文件 -->
{% load staticfiles %}

<!DOCTYPE html>
<!-- 网站主语言 -->
<html lang="zh-cn">

<head>
    <!-- 网站采用的字符编码 -->
    <meta charset="utf-8">
    <!-- 预留网站标题的位置 -->
    <title>{% block title %}{% endblock %}</title>
    <!-- 引入bootstrap的css文件 -->
    <link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
</head>

<body>
    <!-- 引入导航栏 -->
    {% include 'header.html' %}
    <!-- 预留具体页面的位置 -->
    {% block content %}{% endblock content %}
    <!-- 引入注脚 -->
    {% include 'footer.html' %}
    <!-- bootstrap.js 依赖 jquery.js 和popper.js,因此在这里引入 -->
    <script src="{% static 'jquery/jquery-3.3.1.js' %}"></script>
    <script src="{% static 'popper/popper-1.14.4.js' %}"></script>    
    <!-- 引入bootstrap的js文件 -->
    <script src="{% static 'bootstrap/js/bootstrap.min.js' %}"></script>
</body>

</html>
  • 模板中要加上 {% load staticfiles %} 之后,才可使用 {% static 'path' %} 引用静态文件。
  • HTML语法中,所有的内容都被标签包裹;标签及标签中的属性可以对内容进行排印、解释说明等作用。
  • <head></head>标签内包含网页的元数据,是不会在页面内显示出来的。<body></body>标签内才是网页会显示的内容。
  • 留意Bootstrap的css、js文件分别是如何引入的
  • jquery.js 和 popper.js 要在 bootstrap.js 前引入。

然后是header.html

templates/header.html

<!-- 定义导航栏 -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
  <div class="container">

    <!-- 导航栏商标 -->
    <a class="navbar-brand" href="#">我的博客</a>

    <!-- 导航入口 -->
    <div>
      <ul class="navbar-nav">
        <!-- 条目 -->
        <li class="nav-item">
          <a class="nav-link" href="#">文章</a>
        </li>
      </ul>
    </div>

  </div>
</nav>

标签内的class属性是Bootstrap样式的定义方法。试着改写或删除其中一些内容,观察页面的变化。

然后改写之前的list.html

templates/article/list.html

<!-- extends表明此页面继承自 base.html 文件 -->
{% extends "base.html" %}
{% load staticfiles %}

<!-- 写入 base.html 中定义的 title -->
{% block title %}
    首页
{% endblock title %}

<!-- 写入 base.html 中定义的 content -->
{% block content %}

<!-- 定义放置文章标题的div容器 -->
<div class="container">
    <div class="row mt-2">

        {% for article in articles %}
        <!-- 文章内容 -->
        <div class="col-4 mb-4">
        <!-- 卡片容器 -->
            <div class="card h-100">
                <!-- 标题 -->
                <h4 class="card-header">{{ article.title }}</h4>
                <!-- 摘要 -->
                <div class="card-body">
                    <p class="card-text">{{ article.body|slice:'100' }}...</p>
                </div>
                <!-- 注脚 -->
                <div class="card-footer">
                    <a href="#" class="btn btn-primary">阅读本文</a>
                </div>
            </div>
        </div>
        {% endfor %}

    </div>
</div>
{% endblock content %}
  • 留意{% block title %}{% block content %}是如何与base.html中相对应起来的。
  • 摘要中的{{ article.body|slice:'100' }}取出了文章的正文;其中的|slice:'100'是Django的过滤器语法,表示取出正文的前100个字符,避免摘要太长。

最后写入footer.html

{% load staticfiles %}
<!-- Footer -->
<div>
    <br><br><br>
</div>
<footer class="py-3 bg-dark fixed-bottom">
    <div class="container">
        <p class="m-0 text-center text-white">Copyright &copy; www.dusaiphoto.com 2018</p>
    </div>
</footer>

呼,真是一大堆的东西啊。

让我们来捋一捋发生了什么:

当我们通过url访问list.html时,顶部的{% extends "base.html" %}告诉Django:“这个文件是继承base.html的,你去调用它吧。”

于是Django就老老实实去渲染base.html文件:

  • 其中的{% include 'header.html' %}表明这里需要加入header.html的内容
  • {% include 'footer.html' %}加入footer.html的内容
  • {% block content %}{% endblock content %}表明这里应该加入list.html中的对应块的内容

运行服务器

老规矩,保存全部文件,进入虚拟环境,运行开发服务器,在浏览器中输入http://127.0.0.1:8000/article/article-list/,看到如下页面:

一个漂亮的博客界面就这样出现在眼前,非常神奇。

如果报错也不要着急,程序员就是不断与bug斗争的一个职业。仔细检查Django给出的错误提示,修复它,你一定行。

总结

本章我们引入了前端框架Bootstrap 4,借助它重新组织了模板的结构,编写了一个漂亮的博客网站的首页。

下一章我们将学习编写文章详情页面。




本文作者: 杜赛
发布时间: 2018年09月16日 - 09:25
最后更新: 2019年06月07日 - 20:09
知识共享许可协议   转载请保留原文链接及作者


登录 后回复

共有35条评论

avatar
河底客 么么哒! 2

老师你好,我尝试用bootstrap4做一个滚动监听的全站导航栏。

从这里看到的教程:

菜鸟教程:Bootstrap 滚动监听(Scrollspy)

教程所讲的功能仅限于停留在首页时,导航栏按钮监听才有效,

如果我进入了文章详情页再点导航栏按钮没有任何反应,因为导航栏按钮的指向是当前页面的id为"photos"的div元素

<!-- 导航栏某按钮 -->
<a class="nav-link" href="#photos">照片</a>

<!-- index首页的id为"photos"的div元素 -->
<div id="photos" class="container-fluid text-muted">
    <h1>照片</h1>
</div>

我想知道的是,我的导航栏按钮herf=该怎么指向首页,才能在全站任何页面都可用?

 

类似这样的效果:

http://www.ju-jingyi.com/news-180615-2.html

在他这个页面上点击右上角的任意导航栏按钮,都可以跳转到首页的相应监听区域,我也用检查元素的方法看了他的写法:

<a href="index.html#news"><b>News</b> 新 / 闻 / 资 / 讯</a>

试着用了下,但是好像不起作用。

谢谢。

9个月前 回复


avatar
杜赛 [博主] 河底客 么么哒! 2

你是想实现锚点定位吗?

如果导航中的链接是这样:

<a href="#photos">照片</a>

点击链接,页面会跳转到id="photos"的页面元素的位置,即这里:

<div id="photos">...</div>

当然前提是你的页面中得有这个容器。如果没有,当然就不会跳转了。

我的博客文章详情页面的“前往评论”链接、消息通知栏的跳转相应评论,都是这样实现的。

如果想要跳转时带有滚动的动画,就要结合javascript代码来实现。

9个月前 回复


avatar
河底客 杜赛 [博主] 么么哒! 2

老师你好,仅停留在首页内时的定位已经实现了。平滑滚动我是用了一个css的样式,没有用JavaScript:

html{
  scroll-behavior:smooth;
}

 

现在问题是这样的,比如我当前在文章详情页面(非index首页),也想通过点击顶部导航栏按钮来直接跳转到首页的id为“photos”的元素位置

因为导航栏按钮指向的#photos只对当前页面内的容器id有效,

当我在https://localhost:8000/article/detail/1/这样的页面内时,

点击按钮实际指向的地址为:https://localhost:8000/article/detail/1/#photos,

显然,这无法跳转到首页的“photos”元素位置。

如何才能正确的指向首页的位置?

谢谢。

9个月前 回复


avatar
杜赛 [博主] 河底客 么么哒! 2

我的博客有留言通知,你肯定使用过了。

有注意到点击留言通知的href时,会跳转到对应的评论的位置吗?

实现起来其实也简单。导航栏的href你可以这样写:

<a href="{% url 'xxx' %}#photos">...</a>

直接将锚点拼接在href里面就可以了。

动态的锚点也没问题:

<a href="{% url 'xxx' %}#{{ photo.id }}">...</a>

其中的photo是你传递给模板的对象。

这种方法会对网站所有位置的导航栏都生效。

如果你想局部应用锚点,另外一种方法是在视图views内部进行拼接:

from django.shortcuts import redirect

def your_function(request):
    ...
    # get_absolute_url()是Model中获取页面url的方法
    redirect_url = YourModel.get_absolute_url() + '#photos'
    return redirect(redirect_url)

根据你的实际情况选择,希望可以帮到你。

9个月前 回复


avatar
河底客 杜赛 [博主] 么么哒! 2

谢谢老师,已解决问题!yes

9个月前 回复


avatar
pidada 么么哒! 2

老师您好

<link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">

在这个代码中好像找不到bootstrap.min.css这个文件,您的三个css文件中没有这个名字,是不是指的就是bootstrap.css文件?望您回复

5个月前 回复


avatar
杜赛 [博主] pidada 么么哒! 2

见邮件。加油wink

5个月前 回复


avatar
ac1864 么么哒! 2

老大,每次我都卡在模板,

这次居然一次通关。

你的方法真好,直接拷贝,简单粗暴。

4个月前 回复


avatar
杜赛 [博主] ac1864 么么哒! 2

过关了就好。

后面还有boss战

4个月前 回复


avatar
Leo 么么哒! 2

博主你好,我用了ckeditor-uploder在文章中加了图片,现在遇到了一个问题是文章列表中显示摘要时文本切片切到了图片 会破坏列表块的展示,请教一下有没有好的处理方法,如果需要的话我给你邮箱发截图

{{ article.body|slice:'100' }}

4个月前 回复


avatar
杜赛 [博主] Leo 么么哒! 2

邮箱截图给我吧,我看看具体情况

4个月前 回复


avatar
ac1864 Leo 么么哒! 2

简单粗暴的解决方案是: 在100个字之后再使用图片。

4个月前 回复


avatar
pengshilin 么么哒! 2

老师你好, 然后在项目根目录下新建目录static/bootsrap/

这里少打了一个 t ,static/bootstrap  

4个月前 回复


avatar
杜赛 [博主] pengshilin 么么哒! 2

感谢提醒!已经修正过来了yes

4个月前 回复


avatar
zuolei 么么哒! 2

 

Template-loader postmortem
Django tried loading these templates, in this order:

Using engine django:

django.template.loaders.filesystem.Loader: E:\Git\file\django_file\my_blog\base.html (Source does not exist)
django.template.loaders.app_directories.Loader: E:\python\lib\site-packages\django\contrib\admin\templates\base.html (Source does not exist)
django.template.loaders.app_directories.Loader: E:\python\lib\site-packages\django\contrib\auth\templates\base.html (Source does not exist)
Error during template rendering
In template E:\Git\file\django_file\my_blog\article\list.html, error at line 9

base.html
1	<!DOCTYPE html>
2	<html>
3	<head lang="en">
4	    <meta charset="UTF-8">
5	    <title></title>
6	</head>
7	<body>
8	<!-- extends表明此页面继承自 base.html 文件 -->
9	{% extends "base.html" %}
10	{% load staticfiles %}
11	
12	<!-- 写入 base.html 中定义的 title -->
13	{% block title %}
14	    首页
15	{% endblock title %}
16	
17	<!-- 写入 base.html 中定义的 content -->
18	{% block content %}
19	

访问报错,求指教

4个月前 回复


avatar
杜赛 [博主] zuolei 么么哒! 2

报错里讲得很清楚了。

在模板引擎指定的位置,没有找到base.html这个模板文件

4个月前 回复


avatar
zuolei 杜赛 [博主] 么么哒! 2
本回复已被 zuolei 删除

4个月前 回复


avatar
zuolei 杜赛 [博主] 么么哒! 2

这这个怎么指定他去那个目录下找文件呢

4个月前 回复


avatar
杜赛 [博主] zuolei 么么哒! 2

您不是从头看的本教程吧?

改写View这个章节里有讲的。

4个月前 回复


avatar
sys0613 么么哒! 2

请问在 endblock中什么时候需要 在endblock 后面跟一个名字(例如下面的content),再加%,

{% endblock content %}

例如咱这个base.html中有时endblock的后面跟一个名字(例如content),有时不跟(例如title)。

在list.html中进行替换时,block和endblock后面是一定要跟参数名字么?谢谢了

3个月前 回复


avatar
杜赛 [博主] sys0613 么么哒! 2

这两种写法作用是一样的,{% endblock content %}这种写法只是为了增加额外的可读性,告诉开发人员目前正在关闭的继承模板是哪一个。通常用于大型模板。

3个月前 回复


avatar
sys0613 么么哒! 2

您好,博主,我现在想改写导航栏样式,像咱这里的navbar,navbar-expand-lg等标签我在bootstrap教程中看到了。我理解含义

<nav class="navbar navbar-expand-lg navbar-dark bg-dark">

但像navbar-dark bg-dark等我在教程中没有看到呢。我想查看每个class属性代表的含义,有什么专门的手册么?谢谢了。刚开始学bootstrap有点蒙。

3个月前 回复


avatar
杜赛 [博主] sys0613 么么哒! 2

navbar-dark bg-dark 这些都是Bootstrap定义的css样式,在官方文档的某个角落肯定是有讲的,仔细找找。

  • navbar-dark在导航栏相关的几个章节,用处是设置导航栏主题
  • bg-dark我有点忘了,是设置背景颜色,大概是在讲背景布局的地方

3个月前 回复


avatar
sys0613 杜赛 [博主] 么么哒! 2

谢谢,今天找了个bootstrap原生手册看看,明白了一些。http://www.php.cn/manual/view/34100.html

3个月前 回复


avatar
sys0613 么么哒! 2

请问如何才能让nav中的“文章”按钮紧跟着“我的博客出现”呢?不让他到右边去。我试了很多次都不行呢。还想保留container属性。

目前研究结果是,container貌似必须填充满整行,所以导致的该问题。正在测试用多个div解决。

3个月前 回复


avatar
杜赛 [博主] sys0613 么么哒! 2

像我的博客这样吗?

加上这个就行了:

<div class="collapse navbar-collapse">
    <ul>
        ...
    </ul>
</div>

不过教程中没有做自适应(不是重点),所以当屏幕太小时导航会消失。

如何做自适应,官方有详细的介绍:Navbar

3个月前 回复


avatar
sys0613 杜赛 [博主] 么么哒! 2

非常感谢。确实好用。我同时有深入想了下,起作用的原因好像是因为这个div正好将厂商图片和所有标签包裹为一个东西了。所以一起置为左端了。我感觉像是这个原因,如果不加collapse 和navbar-collapse类,直接放到div中,再给个display:inline属性,也可以实现这个功能了。

3个月前 回复


avatar
杜赛 [博主] sys0613 么么哒! 2

你说得很对。Bootstrap说穿了就是css样式的快捷方式,和你自己在标签里写style其实是一回事。只不过Bootstrap把一套好看的样式封装起来了,帮你免去了很多麻烦。

3个月前 回复


avatar
tan920229 么么哒! 2
本回复已被 tan920229 删除

2个月前 回复


avatar
littleh 么么哒! 2

你好 博主。我在做多级评论的时候,回复评论人点击 发送 没什么反应

检查出现这样的错误:

Failed to find a valid digest in the 'integrity' attribute for resource 'https://cdn.bootcss.com/jquery/3.2.1/jquery.js' with computed SHA-256 integrity 'DZAnKJ/6XZ9si04Hgrsxu/8s717jcIzLy3oi35EouyE='. The resource has been blocked.

Uncaught TypeError: $(...).stop is not a function
    at (index):448
    at dispatch (jquery.slim.min.js:3)
    at q.handle (jquery.slim.min.js:3)
    at Object.trigger (jquery.slim.min.js:3)
    at jquery.slim.min.js:3
    at Function.each (jquery.slim.min.js:2)
    at r.fn.init.each (jquery.slim.min.js:2)
    at r.fn.init.trigger (jquery.slim.min.js:3)
    at r.fn.init.r.fn.<computed> [as scroll] (jquery.slim.min.js:3)
    at HTMLDocument.<anonymous> ((index):450)

请问这是怎么回事啊?感谢

2个月前 回复


avatar
杜赛 [博主] littleh 么么哒! 2

这个错误似乎是因为js文件过时造成的。

你在引入js文件的时候加了intergrity属性?删除掉是否正常了?

2个月前 回复


avatar
littleh 杜赛 [博主] 么么哒! 2

我把intergrity属性删除掉还是不行啊,

点击发送还是有这样的错误:

(index):59 Uncaught ReferenceError: $ is not defined
    at confirm_submit ((index):59)
    at HTMLButtonElement.onclick ((index):41)

 

2个月前 回复


avatar
杜赛 [博主] littleh 么么哒! 2

看起来是jquery.js没有正确载入。检查一下

2个月前 回复


avatar
littleh 杜赛 [博主] 么么哒! 2

请问是不是因为我引入的是jquery.min.js所以不行啊?

2个月前 回复


avatar
杜赛 [博主] littleh 么么哒! 2

应该不是。打开浏览器的控制台看看,是不是静态文件都是报了404错误?

2个月前 回复