欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Django速成:构建一个Blog

程序员文章站 2022-04-28 10:28:04
1.创建项目 组织Django代码最简单的方式是使用Django的“项目”(project):一个包含了组成单个网站的所有文件的目录。Django提供了一个叫django admin.py的命令来帮助创建这样项目的目录。 Windows下: 运行之后,我们用资源管理器,可以发现新建了一个mysite ......

1.创建项目

组织django代码最简单的方式是使用django的“项目”(project):一个包含了组成单个网站的所有文件的目录。django提供了一个叫django-admin.py的命令来帮助创建这样项目的目录。

windows下:

(py2_django) e:\demo_django>django-admin startproject mysite
(py2_django) e:\demo_django>

运行之后,我们用资源管理器,可以发现新建了一个mysite文件夹,如图:
Django速成:构建一个Blog

  • 解释:
    • (1) __init__.py 把这个项目目录变成一个python的包(package)——相关python模块的一个集合。这让我们可以用python的“点记号”(dot-notation)来指定项目中的某个部分,比如mysite.urls。

    • (2) manage.py 是一个同这个django项目一起工作的工具。你可以从它在目录列表中的权限里看到它是可以执行的。我们马上就会用到它。

    • (3) settings.py 包含了项目的默认设置。包括数据库信息、调试标志以及其他一些重要的变量。你项目里安装的任何应用都可以访问这个文件。

    • (4) urls.py文件在django里叫urlconf,它是一个将url模式映射到你应用程序上的配置文件。urlconf是django里非常强大的一个特性。

2.运行开发服务器

到这里,我们还没有构建完blog应用,不过django为我们提供了一些可以就地使用的方便。其中最好用的就是django的内置web服务器了。这个服务器不是用来部署公共站点,而是用来做快速开发的。其优点在于:

  • 不需要安装apache、lighttpd,或是其他任何实际生产所需的web服务器软件一—一如果你在一台新的服务器或是没有服务器环境的开发机上,或是就想实验一下的话,那就非常方便了。

  • 它会自动检测到你对python源码的修改并且重新加载那些模块。相比每次修改代码后都要手动地重启web服务器可是大大地节约了不少时间,这对绝大多数python的web服务器设置来说都是很有必要的。

  • 它知道如何为admin应用程序寻找并显示静态的媒体文件,所以你就可以直接使用它。运行开发服务器(或“dev”)简单到只需要一个命令就行了。这里我们要用到项目里的manage.py工具,这是一个简单的包襄脚本,能直接告诉django-admin.py去读入项目特定的settings文件。启动dev的命令如下:

接着,进入\demo_django\mysite

(py2_django) e:\demo_django>cd mysite

(py2_django) e:\demo_django\mysite>python manage.py runserver

回车后,可以看到类似如下的输出,ctrl+c可以退出:

(py2_django) e:\demo_django\mysite>python manage.py runserver
performing system checks...

system check identified no issues (0 silenced).

you have 13 unapplied migration(s). your project may not work properly until you apply the migrations for app(s): admin,
 auth, contenttypes, sessions.
run 'python manage.py migrate' to apply them.
april 10, 2019 - 19:59:26
django version 1.11, using settings 'mysite.settings'
starting development server at http://127.0.0.1:8000/
quit the server with ctrl-break.

复制 http://127.0.0.1:8000/ 到浏览器中,回车后,可以看到django的“it worked!”页面,如下图:

Django速成:构建一个Blog

同时,查看终端的话,会看到dev服务器记录下了get请求。

[10/apr/2019 20:07:11] "get / http/1.1" 200 1716

log从左到右有4个部分:时间戳、请求、http状态码,以及字节数。(你看到的字节数或许会有所不同。)

成功启动服务器后,我们就可以来设置我们的第一个django应用了。

3.创建blog应用

有了项目以后,就可以在它下面创建应用(按django的说法是“app”)了。我们再次使用manage.py来创建这个blog app。

(py2_django) e:\demo_django\mysite>python manage.py startapp blog

这样就完成项目的创建了。现在我们在项目的目录下有了一个blog目录。如下图:

Django速成:构建一个Blog

和项目一样,app也是一个包。现在.py文件里还没有真正的代码,它们只是先占住位子而已。

要告诉django这个app是项目里的一部分,需要去编辑settings.py文件(也称之为“配置文件”)。打开配置文件并在文件尾部找到installed_apps元组。把你的app以模块的形式添加到元组里,就像这样(注意结尾的逗号):

'blog',

如图所示:
Django速成:构建一个Blog

django用installed_apps来决定系统里不同部分的配置,包括自动化的admin应用以及测试框架。

4.设计model

现在我们来到了这个基于django的blog应用的核心部分:models.py文件。
这是我们定义blog数据结构的地方。根据dry原则,django会尽量利用你提供给应用程序的model信息。我们先来创建一个基本的model,看看django用这个信息为我们做了点什么。
用你最习惯的编辑器(如果它能支持python模式就最好)打开models.py文件。你会看到这样的占位文本:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models

# create your models here.

删掉注释,加入下列代码:

class blogpost(models.model):
    title = models.charfield(max_length=150)
    body = models.textfield()
    timestamp = models.datetimefield()

即,如下图所示:

Django速成:构建一个Blog

这是一个完整的model,代表了一个有3个变量的“blogpost”对象。(严格说来应该有4个,django会默认为每个model自动加上一个自增的、唯一的id变量。)这个新建的blogpost类是django.db.models.model的一个子类。这是django为数据model准备的标准基类,它是django强大的对象关系映射(orm)系统的核心。此外,每一个变量都和普通的类属性一样被定义为一个特定变量类(field class)的实例。这些变量类也是在django.db.models里定义,它们的种类非常多,从booleanfield到xmlfield应有尽有,可不止这里看到的区区三个。

5.设置数据库

用数据库管理工具为django项目创建一个新的数据库。在这里我们把数据库取名为“djangodb”,不过你可以选用任何喜欢的名字。
当你有了一个(空的)数据库以后,接下来只需要告诉django如何使用它即可。这就需要用到项目的settings.py文件。

使用数据库服务器

这里选用mysql这样的关系数据库和django配合使用。这里有6个相关的设置(虽然你可能只要两个就够了):

  • database_engine
  • database_name
  • database_host
  • database_port
  • database_user
  • database_password

它们的作用从名字上就能看的出来。只需在相应的位置填入正确的值就可以了。
mysql的设置如下:

databases = {
    'default': {
        # 'engine': 'django.db.backends.sqlite3',
        # 'name': os.path.join(base_dir, 'db.sqlite3'),
        'engine': 'django.db.backends.mysql',
        'name': 'djangodb',
        'user': 'root',
        'password': 'root',
        'host': 'localhost',
        'port': '3306',

    }
}

创建表

首先,我们先配置一下e:\demo_django\mysite\blog下的__init__.py文件的内容,添加内容如下:

import pymysql
pymysql.install_as_mysqldb()

现在你可以告诉django用你提供的连接信息去连接数据库并且设置应用程序所需的表。命令很简单:

# 老版本:
(py2_django) e:\demo_django\mysite>python manage.py syncdb
     
# django 1.7.1 及以上的版本需要用以下命令
(py2_django) e:\demo_django\mysite>python manage.py makemigrations
no changes detected

(py2_django) e:\demo_django\mysite>python manage.py migrate
operations to perform:
  apply all migrations: admin, auth, contenttypes, sessions
running migrations:
  applying contenttypes.0001_initial... ok
  applying auth.0001_initial... ok
  applying admin.0001_initial... ok
  applying admin.0002_logentry_remove_auto_add... ok
  applying contenttypes.0002_remove_content_type_name... ok
  applying auth.0002_alter_permission_name_max_length... ok
  applying auth.0003_alter_user_email_max_length... ok
  applying auth.0004_alter_user_username_opts... ok
  applying auth.0005_alter_user_last_login_null... ok
  applying auth.0006_require_contenttypes_0002... ok
  applying auth.0007_alter_validators_add_error_messages... ok
  applying auth.0008_alter_user_username_max_length... ok
  applying sessions.0001_initial... ok

(py2_django) e:\demo_django\mysite>

6.试用admin

应用程序需要告诉django要在admin窗口里显示哪一个model以供编辑。要做到这一点很简单,只要定义之前提到的默认admin站点,并向其注册blogpost model就行了。打开mysite/blog/models.py文件,确认导入了admin应用,然后在最后加上一行注册model的代码。

# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
from django.contrib import admin

class blogpost(models.model):
    title = models.charfield(max_length=150)
    body = models.textfield()
    timestamp = models.datetimefield()

admin.site.register(blogpost)

之后,

(py2_django) e:\demo_django\mysite>python manage.py makemigrations
migrations for 'blog':
  blog\migrations\0001_initial.py
    - create model blogpost

(py2_django) e:\demo_django\mysite>python manage.py migrate
operations to perform:
  apply all migrations: admin, auth, blog, contenttypes, sessions
running migrations:
  applying blog.0001_initial... ok

在浏览器输入 http://127.0.0.1:8000/admin

Django速成:构建一个Blog
纳尼?用户名和密码?我们还没有设过啊?

接下来,创建超级管理员权限:

(py2_django) e:\demo_django\mysite>python manage.py createsuperuser
username (leave blank to use 'administrator'): admin
email address: xxxxxxxx@126.com
password:
password (again):
this password is too short. it must contain at least 8 characters.
this password is too common.
password:
password (again):
superuser created successfully.  # admin123

(py2_django) e:\demo_django\mysite>

好了,现在我们登陆后台。操作如下:

(py2_django) e:\demo_django\mysite>python manage.py runserver

打开浏览器, http://127.0.0.1:8000/admin ,
输入刚刚创建的用户名和密码,进入后台,如下所示:
Django速成:构建一个Blog
确认你的应用程序blog和截图里一样出现在屏幕上。
如果没有的话,请重新检查之前的步骤。

提示
三个最常见的“我的app没有显示在admin里”的原因是:
1)忘记向admin.site.register注册你的model类;
2)models.py里有错误;
3)忘记在settings.py中的installed_apps里添加app。

有没有内容的blog么?点击blog posts右侧的add按钮。admin会显示一个表单让你添加新的帖子,如图所示。

Django速成:构建一个Blog

给帖子取一个名字然后往里填一点内容。
至于时间戳,你可以点击today和now的快捷链接来获取当前的日期和时间。还可以点击日历或时钟标志来方便地选择时间。
完成之后,点击save按钮保存。

Django速成:构建一个Blog

你会收到一条确认消息(“the blog post blogpost object'was added successfully.”)和一个列出你所有blog帖子的列表——目前只有一篇而已,如图所示。
Django速成:构建一个Blog
为什么帖子有“blogpost object”这么难看的名字?django的设计是希望能灵活地处理任意类型的内容,所以它不会去猜测对于给定的内容什么变量才是最合适的。在第三部分的例子里,你会看到如何为你对象的默认标签指定一个特定变量,或是特别定制的字符串。
现在点击右上角的“add blogpost+”按钮添加另一篇不同内容的帖子。
Django速成:构建一个Blog

当你回到列表视图里,你会看见页面上加入了另一个blogpost。如果你刷新页面或是离开应用程序再回来的话,输出不会有任何变化一—你一定不会喜欢看见所有条目都被标为“blogpost object”,如图
Django速成:构建一个Blog

有些难看,怎么样才可以更好看一些?

之前我们通过很少的配置就激活了admin工具,即向admin app注册我们的model。
现在只要额外的两行代码以及对注册调用的一些修改,我们就可以让列表看起来更漂亮更有用。更新我们的mysite/blog/models.py文件,添加一个blogpostadmin类,并将它加到注册代码那一行里,于是现在madels.py看起来是这样的:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
from django.contrib import admin

class blogpost(models.model):
    title = models.charfield(max_length=150)
    body = models.textfield()
    timestamp = models.datetimefield()

class blogpostadmin(admin.modeladmin):
    list_display = ('title', 'timestamp')

admin.site.register(blogpost, blogpostadmin)

开发服务器会注意到你的修改并自动重新加载model文件。

刷新一下页面,现在你就可以看到一个更有意义的页面了,它是根据你添加到blogpostadmin类里ist_display变量来生成输出的,如图所示。
Django速成:构建一个Blog
试着点一下title和timestamp列的标题—一每一个都影响了你的条目是如何排序的。例如,点一下title会按照升序排列标题,再点一下则变成降序排列。
admin还有很多有用的特性只需一到两行代码就可以激活:搜索、自定义排序、过滤等。

7.建立blog的公共部分

完成我们应用的数据库部分和admin部分后,现在来看看面向公众的页面部分。从django的角度来说,一个页面具有三个典型的组件:

  • 一个模板(template),模板负责将传递进来的信息显示出来(用一种类似python字典的对象context)。
  • 一个视图(view)函数,它负责获取要显示的信息,通常都是从数据库里取得。
  • 一个url模式,它用来把收到的请求和你的视图函数匹配,有时也会向视图传递一些参数。

    创建模板

    django的模板语言相当简单,我们直接来看代码。这是一个简单的显示单个blog帖子的模板:
<h2>{{ post. title }}</h2>
<p>{{ post. timestamp }}</p>
<p>{{ post. body })</p>

它就是一个html(虽然django模板可以用于任何形式的输出)加上一些大括号里的特殊模板标签。这些是变量标签(variable tag),用于显示传递给模板的数据。在变量标签里,你可以用python风格的dotted-notation(点记号)来访问传递给模板的对象的属性。例如,这里假设你传递了一个叫“post”的blogpost对象。这三行模板代码分别从blogpost对象的title、timestamp和body变量里获取了相应的值。

现在我们稍微改进一下这个模板,通过django的for模板标签让它能显示多篇blog帖子。

{% for post in posts %}
<h2>{{ post.title }}</h2>
<p>{{ post.timestamp }}</p>
<p>{{ post.body }}</p>
{% endfor %}

原来的3行没有动,我们只是简单地增加了一个叫做for的块标签(block tag),用它将模板渲染到序列中的每个元素上。其语法和python的循环语法是一致的。注意和变量标签不同,块标签是包含在{%...%}里的。
把这5行模板代码保存到文件archive.html里,然后把文件放到你的blog app目录里的templates目录下。这个文件的路径即为:
mysite\blog\templates\archive.html
模板本身的名字是随意取的(叫它foo.html也没问题),但是templates目录的名字则是强制的。django在默认情况下会在搜索模板时逐个查看你安装的应用程序下的每一个templates目录。

创建一个视图函数

现在我们来编写一个从数据库读取所有blog帖子的视图函数,并用我们的模板将它们显示出来。打开blog/views.py文件并输入:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.shortcuts import render
from django.template import loader, context
from django.http import httpresponse
from mysite.blog.models import blogpost

def archive(request): # 每个django视图函数都将django.http.httprequest对象作为它的第一个参数。它还可以通过urlconf接受其他参数。
    posts = blogpost.object.all() # 当我们把blogpost类作为django.db.model.model的一个子类时,我们就获得了django对象关系映射的全部力量。这一行只是使用orm(对象关系映射)的一件简单例子,获取数据库里所有blogpost对象。
    t = loader.get_template("archive.html") # 告诉django模板的名字创建模板对象t。(其保存在app下的templates目录里,django无需更多指示就能找到它。)
    c = context.get({ 'posts': posts }) # django模板渲染的数据是由一个字典类的对象context提供的,这里的contextc只有一对键和值。
    return httpresponse(t.render(c)) # 每个django视图函数都会返回一个django.http.httpresponse对象。最简单的就是给其构造函数传递一个字符串。这里模板的render方法返回的正是一个字符串。

此处有坑,下面为可正常运行代码:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.shortcuts import render
from django.template import loader, context
from django.http import httpresponse
from models import blogpost

def archive(request): # 每个django视图函数都将django.http.httprequest对象作为它的第一个参数。它还可以通过urlconf接受其他参数。
    posts = blogpost.objects.all() # 当我们把blogpost类作为django.db.model.model的一个子类时,我们就获得了django对象关系映射的全部力量。这一行只是使用orm(对象关系映射)的一件简单例子,获取数据库里所有blogpost对象。
    t = loader.get_template("archive.html") # 告诉django模板的名字创建模板对象t。(其保存在app下的templates目录里,django无需更多指示就能找到它。)
    # c = context({ 'posts': posts }) # django模板渲染的数据是由一个字典类的对象context提供的,这里的contextc只有一对键和值。
    # typeerror: context must be a dict rather than context.
    c = { 'posts': posts }
    return httpresponse(t.render(c)) # 每个django视图函数都会返回一个django.http.httpresponse对象。最简单的就是给其构造函数传递一个字符串。这里模板的render方法返回的正是一个字符串。

创建一个url模式

我们的页面还差一步就可以工作了——和任何网页一样,它还需要一个url。
当然我们可以直接在mysite/urls.py里创建所需的url模式,但是那样做只会在项目和app之间制造混乱的耦合。blog app还可以用在别的地方,所以最好是它能为自己的url负责。这需要两个简单的步骤。

  • (1)在mysite/urls.py里添加:
url(r'blog/', include('mysite.blog.urls')),

即,
Django速成:构建一个Blog
实际有坑,改:

"""mysite url configuration

the `urlpatterns` list routes urls to views. for more information please see:
    https://docs.djangoproject.com/en/1.11/topics/http/urls/
examples:
function views
    1. add an import:  from my_app import views
    2. add a url to urlpatterns:  url(r'^$', views.home, name='home')
class-based views
    1. add an import:  from other_app.views import home
    2. add a url to urlpatterns:  url(r'^$', home.as_view(), name='home')
including another urlconf
    1. import the include() function: from django.conf.urls import url, include
    2. add a url to urlpatterns:  url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
from django.contrib import admin
from django.conf.urls import include

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^blog/', include('blog.urls')),
]

这会捕捉任何以blog/开始的请求,并把它们传递给一个你马上要新建的urlconf。

  • (2)在blog应用程序包里定义url。创建一个包含如下内容的新文件,mysite/blog/urls.py:
from django.conf.urls.defaults import *
from mysite.blog.views import archive

urlpatterns = patterns('',
    url(r'^$', archive),
    )

亦有坑,正确代码如下:

from django.conf.urls import *
from views import archive

urlpatterns = [
    url(r'^$', archive),
    ]

它看起来和基本的urlconf很像。其中的关键是第5行,注意url请求里和根urlconf匹配的blog/已经被去掉了——这样blog应用程序就变得可以重用了,它不用关心自己是被挂接到blog/下,或是news/下,还是what/i/had/for/lunch/下。url(r'^$', archive),里的正则表达式可以匹配任何url,比如/blog/。
视图函数archive是在模式元组第二部分里提供的。(注意我们传递的不是函数的名字,而是一个first-clas的函数对象。当然用字符串也行,你在后面会看到。)现在来看看效果吧!开发服务器还在运行中么?如果没有,执行manage.py runserver来启动它,然后在浏览器里输入http://127.0.0.1:8000/blog/。你可以看到一个简单朴素的页面,显示了所有你输入的blog帖子,有标题、发布时间和帖子本身。
Django速成:构建一个Blog

8.最后的润色

你有好几种方式来继续改进这个基本的blog引擎。我们来讨论几个关键的概念,把这个项目弄得再漂亮一点。

模板的精确定位

毫不夸张地说,我们的模板实在是太平庸了。毕竟这是一本关于web编程而不是web设计的书,美术方面的东西你就自己处理吧,但是模板继承是模板系统里另一个能让你减少工作量的特性,特别是当你的页面风格快速成长的时候。
我们这个模板现在是自给自足的。但是如果我们的站点有一个blog、一个相册和一个链接页面,并且我们希望所有这些都能基于同一个基础风格的话该怎么办?经验告诉我们要是用复制粘贴的办法做出三个几乎完全一样的模板肯定是不行的。在django里正确的做法是创建一个基础模板,然后在这之上扩展出其他特定模板来。在mysite/blog/templates目录里,创建一个叫做base.html的模板,其内容如下:

<html lang="en">
<style type="text/css">
    body { color: #efd; background: #453; padding: 0 5em; margin: 0 }
    h1 { padding: 2em 1em; background: #675 }
    h2 {color: #bf8; border-top: 1px dotted #fff; margin-top: 2em}
    p {margin: 1em 0 }
</style>
<body>
    <h1>mysite.example.com</h1>
    {% block content %}
    {% endblock %}
</body>
</html>

虽然不是完全符合xhtmlstrict标准,不过差不多就行了。这里要注意的细节是{% block ... %}标签。它定义了一个子模板可以修改的命名块(named block)。修改archive.html模板,让它引用新的基础模板和它的“content”块,就能在blog app里使用它了。

{% extends "base.html" %}
{% block content %}
{% for post in posts %}
<h2>{{ post.title }}</h2>
<p>{{ post.timestamp }}</p>
<p>{{ post.body }}</p>
{% endfor %}
{% endblock %}

这里的{% extends .… %}标签告诉django去查找一个叫base.html的标签,并将这个模板里命名块的所有内容填入到那个模板里相应的块里去。
Django速成:构建一个Blog

按日期排序

你应该已经注意到blog的帖子不是按照传统的时间倒序排列的。告诉django这么做非常简单。实际上,有好几种做法可供选择。我们可以在model里加入一个默认的顺序,或者在视图代码里的blogpost.objects.all)上添加排序功能。在这里修改model会比较好,因为基本上帖子都是按时间倒序排列的。如果在model里设置我们想要的排序方式,django里任何访问数据的部分都会采用这个排序结果。
设置model默认排序的方法是给它定一个meta嵌套类,然后设置ordering属性。

class meta:
    ordering = ("-timestamp", )

还没找到放哪。。。
现在看一下blog的首页(/blog/)。最新的帖子应该出现在页面最上方了。字符串“timestamp”能简洁地通知django,“对‘timestamp’变量按照降序排列”。(如果省略“-”的话则是按升序排列。)
注意
千万不要忘了小括号里结尾的那个逗号!它代表这是一个单元素的元组,而不是一个带小括号的字符率。django在这里要的是一个元组,你可以排序任意数目的变量。如果你在逗号后面加上title',并且你有两个相同发布时间的帖子“a”和“b”的话,“a”就会出现在前面。

通过模板过滤器格式化时间戳

虽然时间戳很好用,但是它的iso8601格式却有点怪异。我们现在用django模板系统里另一个很酷的特性:过滤器(filter)来把它弄得人性化一点。
由于这是一个表示层的(presentation)细节而非数据结构或是商业逻辑的细节,最适合它的位置应该是模板。打开archive.html文件并修改“post.timestamp”一行。

<p>{{ post.timestamp|date }}</p>

只需像这样用一个竖杠,或“管道”符号接在变量名后面(大括号内部)就能把过滤器应用于变量之上了。刷新blog首页。现在你可以看到日期的显示更加友好了(“july7”)。
如果date过滤器的默认风格你也不喜欢的话,你可以传递一个strftime风格的格式化串作为它的参数。不过这里它用的不是python里time模块的转换代码,而是php的date函数的格式化说明。例如,如果你要显示星期几,但是不要显示年份的话,代码就变成下面这个样子了:

<p>{{ post.timestamp|date:"l,f js" }}</p>

这个格式化字符串会返回“friday,july 6th”风格的日期。注意这里不要在冒号两边留有空格——django的模板引擎对空格敏感。