全栈一路坑之使用django创建博客

最近在看一篇全栈增长工程师实战,然后学习里面的项目,结果发现作者用的技术太过老旧,好多东西都已经被抛弃了,所以结合着官方文档和自己的一些理解将错误的信息替换一下,边写边学习

准备工作和工具

作者说需要一些python基础,但是中国程序员是最好的程序员,没有基础照样看,大不了遇到不懂的现学就是喽

需要在计算机上安装一些工具

  • Python环境及包管理工具pip
  • 一款浏览器,推荐Chrome,当然,用自己喜欢的浏览器也可以
  • 版本控制,推荐用Git,但是很多培训机构出来的只会SVN,所以这个没有什么重要的
  • 一款IDE,我用的是pycharm,个人感觉还挺好用的

 Django简介

用来充字数的段落而已,估计读技术书籍的没人关心,值得一提的是Django是一个MTV架构,以前或许会有面试官问问MVC之类的代表什么含义,但现在各种框架,各种标准,已经没法记了,但大体意思是将视图层分为两层,一层Template模板,一层View视图层,感觉有点多此一举

Django应用架构

Django每一个模块在内部都称之为APP,每个APP都有自己的三层架构

安装Django

这里有一个新东西,是类似于php的XAMPP或者MAMP的一个集成环境,可以避免机器被污染,还是挺有用的,叫做virtualenv,安装它的话需要使用python的包管理工具pip,如果没有安装pip的,按照下面的命令安装

curl https://bootstrap.pypa.io/get-pip.py | python

作者在这里使用的是pip3,也就是python3,但是据我的了解,现在市场,尤其是中国,python3还是没有使用的,python2.7才是王道,所以老老实实的用2.7安装吧

$ pip install virtualenv

然后就是要用这个工具创建一个工作区间了

$ mkdir somewhere/virtualenvs
$ virtualenv somewhere/virtualenvs/<project-name> --no-site-packages

工作区间名随便起,虽然写着是项目名,但项目名是在后面指定的,然后到相应的目录,启动集成环境

$ cd somewhere/virtualenvs/<project-name>/bin
$ source activate

要关闭环境需要使用

$ deactivate

虚拟环境和工作区间安装好之后,开始安装Django

$ pip install django

下载完成之后,会自己安装,然后Django给我们提供了一个管理工具,可以用它来控制一些东西,和Laravel中的Artisan是一样的效果

创建项目

创建的项目名可以随便起,我这里起名为blog,执行下面代码

$ django-admin startproject blog

执行完之后会创建一个blog的文件夹,进入文件夹之后就看到了生成的东西

blogpost,.gitignore,db.sqlite3都是后来生成的,至于里边每个文件都是干什么不适合在一起讲述,后边遇到哪个再解释是干什么的,都则字太多了谁都没有兴趣看。接下来我们就可以运行期一个服务器,来看看第一个成果,执行下面命令

python manage.py runserver

 

如果没有报错,打开浏览器,输入网址http://127.0.0.1:8000,应该就可以看到如下图那样的页面了,一些简单的英文阅读问题应该不大

然后我们需要创建一个管理员可以登录的后台,Django已经自己提供了这个功能,我们先需要运行数据迁移创建数据库,数据迁移是比较新的技术都带着的一项功能,为了项目切换数据库或者部署的时候方便一点,迁移的时候往哪儿迁移就看配置文件了,Django的配置文件是settings.py,在本项目由中应该是位于根目录下的blog文件夹里,打开可以看到如下所示的默认配置

执行下面代码,就会在根目录看到新创建的数据库db.sqlite3了

$ python manage.py migrate

然后创建一个超级管理员账号,注意此处密码最少要8位,再也不是当年的一个1可以解决的了

$ python manage.py createsuperuser

创建完成之后就可以打开浏览器看一看了

 

写到这儿差不多该出去遛个弯吃个饭,打个炉石啥的了,但就怕走开的这段时间你的电脑突然起火什么的,为了防止代码丢失,所以我们还需要做相应的版本控制,就是我们刚开始说的准备的工具git。作者用的是命令行的git,但我觉的不够直观,所以我直接用IDE里的git,就是Pycharm。打开最下面的Terminal,会看到一个命令行之类的东西,在这里执行命令更直观一点。刚开始使用git的时候需要先初始化一个仓库

git init

创建成功之后就可以将所有的文件提交到版本控制里了

git add .

.代表所有的文件,但是数据库不能上传到版本控制里,一个是因为太大,另一个是因为如果里边有重要数据,并且你将你的代码在一些开源平台上托管的话,别人就能轻而易举的获得你的数据了,所以使用reset命令来重置数据库的状态

git reset db.sqlite3

但是每次这样操作的话会很麻烦,所以需要添加一个忽略文件.gitignore来忽略数据库的修改,为了方便起见直接用vim创建一个文件,并输入相应的信息

vim .gitignore

然后将刚刚创建的忽略文件添加到版本控制中去

git add .gitignore

然后提交代码到本地

git commit -m "init project"

引号中的内容就是提交信息,如果你想把代码存储到一些远程仓库里去的话就需要将代码push上去,但如果你没有事先配置的话世界使用push会报错,所以我直接使用IDE提供的引入版本控制功能

输入你的用户名和密码就可以连接到github然后将代码push到github上从而让更多的人看到了。

然后我们需要创建一个博文模块,名字是blogpost

django-admin startapp blogpost

然后创建博文的model,打开models.py,然后输入下面代码

from __future__ import unicode_literals

from django.db import models
from django.db.models import permalink


# Create your models here.
class Blogpost(models.Model):
    title = models.CharField(max_length=100, unique=True)
    author = models.CharField(max_length=100, unique=True)
    slug = models.CharField(max_length=100, unique=True)
    body = models.TextField()
    posted = models.DateField(db_index=True, auto_now_add=True)

    def __unicode__(self):
        return '%s' % self.title

    @permalink
    def get_absolute_url(self):
        return ('view_blog_post', None, {'slug': self.slug})

__unicode__函数是用来实现unicode功能的,当对Blogpost对象使用unicode的时候,就会返回它的title.db_index是讲posted设置为索引,auto_now_add是设置时间为添加时的时间,修改后时间不会动。然后作者在这里注册出了问题,害的我辛苦了好久不见成功。注册的时候是需要在blog/settings.py文件中注册,而不是在作者所谓的admin中注册。打开settings.py,将blogpost写入INSTALLER_APPS中,如下所示

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blogpost'
]

然后需要在管理员管理界面能看到博文模块,所以需要在admin.py中注册blogpost模块,打开blogpost/admin.py,把它编辑成这样

from django.contrib import admin

# Register your models here.
from .models import Blogpost


class BlogpostAdmin(admin.ModelAdmin):
    exclude = ['posted']
    prepopulated_fields = {'slug': ('title',)}


admin.site.register(Blogpost, BlogpostAdmin)

 exclude用来排除掉posted字段,prepopulated_fields指定博文的slug和title是一样的。接着我们需要做数据库迁移,好将生成的模型迁移到数据库中

python manage.py migrate

打开浏览器就能看到如下结果

完成一部分,将代码推送到Github上,提交信息写了“创建博文模块”

现在需要修改相应的路由来访问博客,Django的路由在blog/urls.py中,但是一路过来感觉作者在这儿的顺序有点乱,官方文档和序贯都是先写出了view再创建路由,而作者直接创建了路由,让我在阅读的时候很是苦恼,作者这儿为什么要这么写。所以我决定先创建视图,再去修改路由。

首先创建博客列表页,打开blog/views.py,添加index视图,显示博客页的列表

from django.shortcuts import render, render_to_response, get_object_or_404
from blogpost.models import Blogpost


# Create your views here.
def index(request):
    return render_to_response('index.html', {'posts': Blogpost.objects.all()[:5]})

然而这还是不够的,这页是这个框架比较糟糕的地方,还需要再写一个模板才能显示出来,首先在blogpost文件夹下创建一个templates的文件夹,用来存放相应的模板文件,Django将会在这里查找模板文件,Django不会自己创建也是醉了,官方的建议是在这个文件夹中再创建一个名字为blogpost的文件夹,怕命名污染,感觉这里有点违背python的设计理念,可能是我技术不够,还无法体会这个框架的优点。所以按照官方的方法来,创建好对应的文件夹,然后在里面创建一个index.html的文件,如下

{% extends 'base.html' %}
{% block title %}
    Welcome to my blog
{% endblock %}

{% block content %}
    <h1>Posts</h1>
    {% for post in posts %}
        <h2><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></h2>
        <p>{{ post.posted }} - By {{ post.author }}</p>
        <p>{{ post.body }}</p>
    {% endfor %}

{% endblock %}

在这段代码里显然作者用到了一个叫base.html的页面,然后作者很不负责的依然没有给出来,我去他的源代码中扒出了这个页面,现在将它放在templates目录下 ,代码如下

{% load staticfiles %}
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>{% block head_title %}Welcome to my blog{% endblock %}</title>
    <link rel="stylesheet" type="text/css" href="{% static 'css/bootstrap.min.css' %}">
    <link rel="stylesheet" type="text/css" href="{% static 'css/styles.css' %}">
</head>
<body data-twttr-rendered="true" class="bs-docs-home">
<header class="navbar navbar-static-top bs-docs-nav" id="top" role="banner">
    <div class="container">
        <div class="navbar-header">
            <button class="navbar-toggle collapsed" type="button" data-toggle="collapse"
                    data-target=".bs-navbar-collapse">
                <span class="sr-only">切换视图</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a href="/" class="navbar-brand">Growth博客</a>
        </div>
        <nav class="collapse navbar-collapse bs-navbar-collapse" role="navigation">
            <ul class="nav navbar-nav">
                <li>
                    <a href="/pages/about/">关于我</a>
                </li>
                <li>
                    <a href="/pages/resume/">简历</a>
                </li>
            </ul>
            <ul class="nav navbar-nav navbar-right">
                <li><a href="/admin" id="loginLink">登入</a></li>
            </ul>
            <div class="col-sm-3 col-md-3 pull-right">
                <form class="navbar-form" role="search">
                    <div class="input-group">
                        <input type="text" id="typeahead-input" class="form-control" placeholder="Search" name="search" data-provide="typeahead">
                        <div class="input-group-btn">
                            <button class="btn btn-default search-button" type="submit"><i class="glyphicon glyphicon-search"></i></button>
                        </div>
                    </div>
                </form>
            </div>
        </nav>
    </div>
</header>
<main class="bs-docs-masthead" id="content" role="main">
    <div class="container">
        <div id="carbonads-container">
            THE ONLY FAIR IS NOT FAIR <br>
            ENJOY CREATE & SHARE
        </div>
    </div>
</main>
<div class="container" id="container">
    {% block content %}

    {% endblock %}
</div>
<footer class="footer">
    <div class="container">
        <p class="text-muted">@Copyright Phodal.com</p>
    </div>
</footer>
<script src="{% static 'js/jquery.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
<script src="{% static 'js/bootstrap3-typeahead.min.js' %}"></script>
<script src="{% static 'js/main.js' %}"></script>
</body>
</html>

然后,我们还需要一个现实详情的视图,编辑views.py

from django.shortcuts import render, render_to_response, get_object_or_404
from blogpost.models import Blogpost


# Create your views here.
def index(request):
    return render_to_response('index.html', {'posts': Blogpost.objects.all()[:5]})


def view_post(request, slug):
    return render_to_response('blogpost_detail.html', {
        'post': get_object_or_404(Blogpost, slug=slug)
    })

然后就可以编写url使其可以访问了,访问之后发现样式全是错的,去作者的Github上找到了样式文件夹static,放到根目录下,最后得到的效果图如下

提交代码,准备编写单元测试。

先来一个简单的测试,测试首页,在blogpost目录下编辑tests.py文件

from django.core.urlresolvers import resolve
from django.test import TestCase
from blogpost.views import index


# Create your tests here.
class HomePageTest(TestCase):
    def test_root_url_resolves_to_home_page_view(self):
        found = resolve('/blog/')
        self.assertEqual(found.func, index)

运行测试

python manage.py test

结果显示OK

进行下一个测试,测试页面标题是不是我们想要的结果

from django.core.urlresolvers import resolve
from django.http import HttpRequest
from django.test import TestCase
from blogpost.views import index, view_post


# Create your tests here.
class HomePageTest(TestCase):
    def test_root_url_resolves_to_home_page_view(self):
        found = resolve('/blog/')
        self.assertEqual(found.func, index)

    def test_home_page_returns_correct_html(self):
        request = HttpRequest
        response = index(request)
        self.assertIn(b'<title>Welcome to my blog</title>', response.content)

再添加一个测试测试详情页

from datetime import datetime

from django.core.urlresolvers import resolve
from django.http import HttpRequest
from django.test import TestCase

from blogpost.models import Blogpost
from blogpost.views import index, view_post


# Create your tests here.
class HomePageTest(TestCase):
    def test_root_url_resolves_to_home_page_view(self):
        found = resolve('/blog/')
        self.assertEqual(found.func, index)

    def test_home_page_returns_correct_html(self):
        request = HttpRequest
        response = index(request)
        self.assertIn(b'<title>Welcome to my blog</title>', response.content)


class BlogpostTest(TestCase):
    def test_blogpost_url_resolves_to_blog_post_view(self):
        found = resolve('/blog/this_is_a_test.html')
        self.assertEqual(found.func, view_post)

    def test_blogpost_create_with_view(self):
        Blogpost.objects.create(title='hello', author='admin', slug='this_is_a_test', body='This is a blog',
                                posted=datetime.now())
        response = self.client.get('/blog/this_is_a_test.html')
        self.assertIn(b'This is a blog', response.content)

运行测试,得

写完了单元测试,还需要写一些集成测试,这里使用的是一款叫做Selenium的软件,原本就想用这款软件做一些测试,但是不怎么会用,现在正好学习一下。使用之前要先安装Selenium,直接使用pip安装即可

然后编写测试,自动化测试首页是否包含”Welcome to my blog“

from datetime import datetime

from django.core.urlresolvers import resolve
from django.http import HttpRequest
from django.test import TestCase

from blogpost.models import Blogpost
from blogpost.views import index, view_post
from django.test import LiveServerTestCase
from selenium import webdriver


# Create your tests here.
class HomePageTest(TestCase):
    def test_root_url_resolves_to_home_page_view(self):
        found = resolve('/blog/')
        self.assertEqual(found.func, index)

    def test_home_page_returns_correct_html(self):
        request = HttpRequest
        response = index(request)
        self.assertIn(b'<title>Welcome to my blog</title>', response.content)


class BlogpostTest(TestCase):
    def test_blogpost_url_resolves_to_blog_post_view(self):
        found = resolve('/blog/this_is_a_test.html')
        self.assertEqual(found.func, view_post)

    def test_blogpost_create_with_view(self):
        Blogpost.objects.create(title='hello', author='admin', slug='this_is_a_test', body='This is a blog',
                                posted=datetime.now())
        response = self.client.get('/blog/this_is_a_test.html')
        self.assertIn(b'This is a blog', response.content)


class HomepageTestCase(LiveServerTestCase):
    def setUp(self):
        self.selenium = webdriver.Firefox()
        self.selenium.maximize_window()
        super(HomepageTestCase, self).setUp()

    def tearDown(self):
        self.selenium.quit()
        super(HomepageTestCase, self).tearDown()

    def test_visit_homepage(self):
        self.selenium.get('%s%s' % (self.live_server_url, "/blog"))
        self.assertIn("Welcome to my blog", self.selenium.title)

运行测试,ffirefox快速的一闪而过,程序正确运行,出现OK,然后继续测试博客详情页

class BlogpostDetailCase(LiveServerTestCase):
    def setUp(self):
        Blogpost.objects.create(
            title='hello',
            author='admin',
            slug='this_is_a_test',
            body='This is a blog',
            posted=datetime.now
        )
        self.selenium = webdriver.Firefox()
        self.selenium.maximize_window()
        super(BlogpostDetailCase, self).setUp()

    def tearDown(self):
        self.selenium.quit()
        super(BlogpostDetailCase, self).tearDown()

    def test_vist_blog_post(self):
        self.selenium.get('%s%s' % (self.live_server_url, "/blog/this_is_a_test.html"))
        self.assertIn("hello", self.selenium.title)

然后测试用户首页点击博客标题是否能调到对应的博客

class BlogpostFromHomepageCase(LiveServerTestCase):
    def setUp(self):
        Blogpost.objects.create(
            title='hello',
            author='admin',
            slug='this_is_a_test',
            body='This is a blog',
            posted=datetime.now
        )
        self.selenium = webdriver.Firefox()
        self.selenium.maximize_window()
        super(BlogpostDetailCase, self).setUp()

    def tearDown(self):
        self.selenium.quit()
        super(BlogpostDetailCase, self).tearDown()

    def test_visit_blog_post(self):
        self.selenium.get('%s%s' % (self.live_server_url, "/blog"))
        self.selenium.find_element_by_link_text("hello").click()
        self.assertIn("hello", self.selenium.title)

测试完没有问题之后,就可以使用上面写的测试搭建集成测试了,作者在这里犯了一个想当然的错误,需要github的推送,就需要将代码部署到服务器上去,在本地的话github的post是收不到的,我在自己的服务器上搭建了jenkins服务器,具体安装没遇到问题,所以就不在这里说了,百度上能找到很多的教程。安装好之后访问服务器的网址加8080端口,就能看到安装页面了,安装的时候会提示一串字符串密码,需要记录下来输入刚开始的页面里。我下载的是2,千万不要下载1的稳定版。

我选择安装所有建议的插件,安装过程确实相当缓慢,可以站起来活动一下去接个水之类的,反正我是这么干的

安装好之后,就是要创建用户名和密码了,然后到了主界面,创建一个任务

源码管理选择git,然后输入git的仓库地址,构建触发器选择Github的那个,然后点击增加构建步骤,选择execute shell,这里又卡了我两天,刚开始是各种命令找不到,如果是按照我说的在linux下的话就不用担心了,下面是我的shell脚本

virtualenv  python
source python/bin/activate
pip install -r requirements.txt
python manage.py test

然后想要提交后就出发构建的话,还需要去github里配置钩子

然后当代码提交到github上的时候就可以自动构建了,然后,事情的进展往往会往你最不想看到的结果发展的,结果可视化测试果然出错了,报的错说浏览器没有打开,不过想想linux连桌面系统都没装,自然没法打开。然后安装了一款虚拟桌面软件xfvb,然后再安装python的pyvirtualdisplay的模块,将测试代码修改为如下这样,就可以正常测试了

from datetime import datetime

from django.core.urlresolvers import resolve
from django.http import HttpRequest
from django.test import TestCase

from blogpost.models import Blogpost
from blogpost.views import index, view_post
from django.test import LiveServerTestCase
from selenium import webdriver
from pyvirtualdisplay import Display

#test
class HomePageTest(TestCase):
    def test_root_url_resolves_to_home_page_view(self):
        found = resolve('/blog/')
        self.assertEqual(found.func, index)

    def test_home_page_returns_correct_html(self):
        request = HttpRequest
        response = index(request)
        self.assertIn(b'<title>Welcome to my blog</title>', response.content)


class BlogpostTest(TestCase):
    def test_blogpost_url_resolves_to_blog_post_view(self):
        found = resolve('/blog/this_is_a_test.html')
        self.assertEqual(found.func, view_post)

    def test_blogpost_create_with_view(self):
        Blogpost.objects.create(title='hello', author='admin', slug='this_is_a_test', body='This is a blog',
                                posted=datetime.now())
        response = self.client.get('/blog/this_is_a_test.html')
        self.assertIn(b'This is a blog', response.content)


class HomepageTestCase(LiveServerTestCase):
    def setUp(self):
        self.display = Display(visible=0, size=(1024, 768))
        self.display.start()
        self.selenium = webdriver.Firefox()
        self.selenium.maximize_window()
        super(HomepageTestCase, self).setUp()

    def tearDown(self):
        self.selenium.quit()
        self.display.stop()
        super(HomepageTestCase, self).tearDown()

    def test_visit_homepage(self):
        self.selenium.get('%s%s' % (self.live_server_url, "/blog"))
        self.assertIn("Welcome to my blog", self.selenium.title)


class BlogpostDetailCase(LiveServerTestCase):
    def setUp(self):
        Blogpost.objects.create(
            title='hello',
            author='admin',
            slug='this_is_a_test',
            body='This is a blog',
            posted=datetime.now
        )
        self.display = Display(visible=0, size=(1024, 768))
        self.display.start()
        self.selenium = webdriver.Firefox()
        self.selenium.maximize_window()
        super(BlogpostDetailCase, self).setUp()

    def tearDown(self):
        self.selenium.quit()
        self.display.stop()
        super(BlogpostDetailCase, self).tearDown()

    def test_vist_blog_post(self):
        self.selenium.get('%s%s' % (self.live_server_url, "/blog/this_is_a_test.html"))
        self.assertIn("hello", self.selenium.title)


class BlogpostFromHomepageCase(LiveServerTestCase):
    def setUp(self):
        Blogpost.objects.create(
            title='hello',
            author='admin',
            slug='this_is_a_test',
            body='This is a blog',
            posted=datetime.now
        )
        self.display = Display(visible=0, size=(1024, 768))
        self.display.start()
        self.selenium = webdriver.Firefox()
        self.selenium.maximize_window()
        super(BlogpostFromHomepageCase, self).setUp()

    def tearDown(self):
        self.selenium.quit()
        self.display.stop()
        super(BlogpostFromHomepageCase, self).tearDown()

    def test_visit_blog_post(self):
        self.selenium.get('%s%s' % (self.live_server_url, "/blog"))
        self.selenium.find_element_by_link_text("hello").click()
        self.assertIn("hello", self.selenium.title)

最后测试一次,完美

django有很多自带的功能,然后使用自带的功能来做一个简单的评论功能,先安装flatpages,首先添加两个应用到settings.py文件的INSTALLED_APPS中

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blogpost',
    'django.contrib.sites',
    'django.contrib.flatpages'
]

然后添加中间件

MIDDLEWARE_CLASSES = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
]

 然后修改url

然后这儿其实是一个重点,但是作者却一笔带过,导致我看的没头没尾的,Django有一个可选的简单页面的应用,它可以让你存储简单的扁平化结构的HTML内容在数据库中,你可以通过Django的管理界面和一个Python API处理要管理的内容。

一个浮动页面是一个简单的包含有URL,标题和内容的对象。使用它作为一次性,特殊用途的页面,比如关于我们或者隐私政策的页面,那些你想要保存在数据库,但是又不想开发一个自定义的Django应用。

一个简单的页面应用可以是用自定义的模板或者系统默认的模板,系统的单页面模板,他可以和一个或者多个站点相关联

from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^blog/$', 'blogpost.views.index'),
    url(r'^blog/(?P<slug>[^.]+).html', 'blogpost.views.view_post', name='view_blog_post'),
    url(r'^admin/', admin.site.urls),
    url(r'^pages/', include('django.contrib.flatpages.urls')),
]

最后,再做一个数据迁移

python manage.py migrate

然后在根目录下面创建一个templates的文件夹,在templates中创建flatpages文件夹用来存放模板,然后创建about.html

{% extends 'base.html' %}
{% block title %}关于我{% endblock %}

{% block content %}
<div>
<h2>关于博客</h2>
    <p>一方面,找到更多志同道合的人;另一方面,扩大影响力。</p>
    <p>内容包括</p>
    <ul>
        <li>成长记录</li>
        <li>技术笔记</li>
        <li>生活思考</li>
        <li>个人试验</li>
    </ul>
</div>
{% endblock %}

然后登陆后台,添加对应的静态页面

然后访问127.0.0.1:8000/pages/about就会发现,模板不存在,这里又是作者挖的一个坑,Django的默认模板文件夹是在每个APP下面,然而作者是直接在最外边建了一个模板文件夹,然后修改了settings.py却没有告诉读者。所以我这里还是按照django的规矩,将模板文件夹建在blogpost内,如下图所示

然后再次访问上述网址,一切正常,天下大吉

接下来才要正式进入正题,为我们的博客添加评论功能了

安装comments包

pip install django-contrib-comments

然后重新生成依赖文件

pip freeze > requirements.txt

然后将comments包添加到INSTALLED_APPS中

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blogpost',
    'django.contrib.sites',
    'django.contrib.flatpages',
    'django_comments'
]

进行数据库迁移

python manage.py migrate

修改url

urlpatterns = [
    url(r'^$', 'blogpost.views.index'),
    url(r'^blog/(?P<slug>[^.]+).html', 'blogpost.views.view_post', name='view_blog_post'),
    url(r'^admin/', admin.site.urls),
    url(r'^pages/', include('django.contrib.flatpages.urls')),
    url(r'^comments/', include('django_comments.urls')),
]

然后登陆后台添加一些评论

添加完评论报了如下的错误

是因为没有安装pytz这个包导致的,安装pytz并重启runserver

pip install pytz

然后要在首页上显示,修改博客详情页

{% extends 'base.html' %}

{% block head_title %}{{ post.title }}{% endblock %}
{% block title %}{{ post.title }}{% endblock %}

{% block content %}
    <div>
        <div>
            <h2><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></h2>
        </div>
        {{ post.body }}
        <p>
            {{ post.posted }} - By {{ post.author }}
        </p>
    </div>
    {% render_comment_list for post %}
{% endblock %}

然后,我依然没有找到评论在哪儿显示,坑实在是太多了,等以后整明白了再来这儿补充吧。

接着再来玩玩SEO

安装sitemaps,网站地图是用来告诉搜索引擎你的页面更新频率和页面之间关系的的一个XML文件

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blogpost',
    'django.contrib.sites',
    'django.contrib.flatpages',
    'django_comments',
    'django.contrib.sitemaps',
]

在根目录下创建一个sitemap的目录用来保存站点地图,创建sitemaps.py,priority是搜索引擎的优先级,changefreq是更新频率,items方法用来返回对象的列表,在这里只返回含有一个元素的列表,就是main,然后后面的location方法是根据items()返回的对象列表返回绝对路径,就是返回main的绝对路径。

from django.contrib.sitemaps import Sitemap
from django.core.urlresolvers import reverse


class PageSitemap(Sitemap):
    priority = 1.0
    changefreq = 'daily'

    def items(self):
        return ['main']

    def location(self, obj):
        return reverse(obj) 

然后修改urls.py

from django.conf.urls import url, include
from django.contrib import admin
from django.contrib.sitemaps.views import sitemap
from sitemap.sitemaps import PageSitemap

sitemaps = {
    "page": PageSitemap
}

urlpatterns = [
    url(r'^$', 'blogpost.views.index', name='main'),
    url(r'^blog/(?P<slug>[^.]+).html', 'blogpost.views.view_post', name='view_blog_post'),
    url(r'^admin/', admin.site.urls),
    url(r'^pages/', include('django.contrib.flatpages.urls')),
    url(r'^comments/', include('django_comments.urls')),
    url(r'^sitemap.xml$', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap')
]

然后访问http://127.0.0.1:8000/sitemap.xml

然后创建静态页面的sitemap,先从数据库中取出当前的站点,在取出当前站点中的flatpage集合,使用registration_required=False过滤那些不需要注册的页面

class FlatPageSitemap(Sitemap):
    priority = 0.8

    def items(self):
        Site = apps.get_model('sites.Site')
        current_site = Site.objects.get_current()
        return current_site.flatpage_set.filter(registration_required=False)

然后修改urls.py

from sitemap.sitemaps import PageSitemap, FlatPageSitemap

sitemaps = {
    "page": PageSitemap,
    'flatpages': FlatPageSitemap
}

然后创建博客的sitemap,在lastmod中,返回这篇博客的发表日期,一面他们返回的是同一个日期

class BlogSitemap(Sitemap):
    changefreq = 'never'
    priority = 0.5

    def items(self):
        return BlogSitemap.objects.all()

    def lastmod(self, obj):
        return obj.posted
from sitemap.sitemaps import PageSitemap, FlatPageSitemap, BlogSitemap

sitemaps = {
    "page": PageSitemap,
    'flatpages': FlatPageSitemap,
    'blog': BlogSitemap
}

最后结果如下图

至于最后作者所说的使用站长工具的,这里就不再多尝试了,本篇文章到此结束,下一篇文章中开始尝试制作一些API

原文地址:https://www.cnblogs.com/xzjs/p/5527731.html