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)

测试完没有问题之后,就可以使用上面写的测试搭建集成测试了,做集成测试需要安装Java的一款工具,Jenkins,由于我使用的是mac,所以可以直接使用brew安装,非常方便

brew cask install jenkins

安装完成之后会自动启动并打开

然后我们需要继承Github插件,然后将代码提交到Github之后就可以自动运行测试了

添加GitHub plugin和

时间: 2024-10-26 11:22:31

Django创建博客应用的相关文章

django 简易博客开发 1 安装、创建、配置、admin使用(转)

Django 自称是“最适合开发有限期的完美WEB框架”.本文参考<Django web开发指南>,快速搭建一个blog 出来,在中间涉及诸多知识点,这里不会详细说明,如果你是第一次接触Django ,本文会让你在感性上对Django有个认识,完成本文操作后会让你有兴趣阅读的相关书籍和文档. 废话少说,come on!! 本操作的环境: =================== Windows 7/10 python 2.7 Django 1.8.2 =================== 创建

Django快速创建博客,包含了整个框架使用过程,简单易懂

创建工程                                                                                                           创建mysite工程项目: D:/djpy> django-admin.py startproject mysite 工程目录结构: manage.py ----- Django项目里面的工具,通过它可以调用django shell和数据库等. settings.py ----

Django搭建博客网站(三)

Django搭建博客网站(三) 第三篇主要记录view层的逻辑和template. Django搭建博客网站(一) Django搭建博客网站(二) 结构 网站结构决定我要实现什么view. 我主要要用view展示首页,标签页,网站管理员(也就是本人啦)信息页,以及文章详情页. settings.py 因为到这个阶段需要编写html文件了,但是每一个网页的每一行代码都靠自己去写,各种渲染也靠自己去写的话,太麻烦了,Django提供了html模板功能,可以在settings.py里面进行配置. #

Django搭建博客网站(二)

Django搭建自己的博客网站(二) 这里主要讲构建系统数据库Model. Django搭建博客网站(一) model 目前就只提供一个文章model和一个文章分类标签model,在post/models.py中做如下修改: # models.py from django.db import models class PostTag(models.Model): tag_name = models.CharField(max_length=50) def __str__(self): retur

Django搭建博客网站(四)

Django搭建博客网站(四) 最后一篇主要讲讲在后台文章编辑加入markdown,已经在文章详情页对markdown的解析. Django搭建博客网站(一) Django搭建博客网站(二) Django搭建博客网站(三) 要用到的package django-pagedown markdown2 django-pagedown用来在后台生成markdown编辑器,markdown2则是用来将markdown解析成html显示在网页上. install $ pip install django-

Django 系列博客(二)

Django 系列博客(二) 前言 今天博客的内容为使用 Django 完成第一个 Django 页面,并进行一些简单页面的搭建和转跳. 命令行搭建 Django 项目 创建纯净虚拟环境 在上一篇博客中已经安装好了虚拟环境,所以用虚拟环境来安装指定版本的 Django.为了可以从头到尾的走一遍流程,我重新创建了一个虚拟环境. 激活虚拟环境并安装 Django 首先进入虚拟环境路径下的 bin 目录 使用命令激活虚拟环境 安装指定版本 Django 首先使用 pip3 list 命令查看 可以看到

Django 系列博客(七)

Django 系列博客(七) 前言 本篇博客介绍 Django 中的视图层中的相关参数,HttpRequest 对象.HttpResponse 对象.JsonResponse,以及视图层的两种响应方式 CBV 和 FBV,还有简单的文件上传. 视图函数 一个视图函数,简称视图,是一个简单的Python 函数,它接受Web请求并且返回Web响应.响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片. . . 是任何东西都可以.无论视图本身包含什么逻辑,都要返回

Django 系列博客(十一)

Django 系列博客(十一) 前言 本篇博客介绍使用 ORM 来进行多表的操作,当然重点在查询方面. 创建表 实例: 作者模型:一个作者有姓名和年龄. 作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息.作者详情模型和作者模型之间是一对一的关系(one-to-one). 出版商模型:出版商有名称,所在城市. 书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一

Django 系列博客(十三)

Django 系列博客(十三) 前言 本篇博客介绍 Django 中的常用字段和参数. ORM 字段 AutoField int 自增列,必须填入参数 primary_key=True.当 model 中如果没有自增列,则会自动创建一个列名为 id 的列. IntegerField 一个整数类型,范围在-2147483648 to 2147483647. CharField 字符类型,必须提供max_length参数, max_length表示字符长度. DateField 日期字段,日期格式