Django官网:https://www.djangoproject.com/
Django定义了服务器发布、路由映射、模板编程、数据处理的一整套功能,这意味着开发者需要学习Django自己定义的一整套技术。
Django是遵循MVC架构的Web开发框架,其主要由以下几部分组成:
- 管理工具
- 模型
- 视图
- 模板
- 表单
- 管理站
下面逐个学习这些模块。
用Django开发网站需要遵循其开发流程。本节通过建立一个消息录入页面来演示Django的开发流程及相关技术。
建立Django项目,命名为djangosite:
命令格式:django-admin startproject <项目名>
# django-admin是安装好Django组建后在Python目录中生成的Django项目管理工具;startproject参数用于新建项目。
- 上面看到的默认生成的文件都是极其重要的,在今后的开发中要一直使用或者维护它们。含义如下:
manage.py
:用于管理本项目的命令行工具。之后进行站点运行、数据库自动生成、静态文件收集等都要通过该文件生成。djangosite/__init__.py
:表明djangosite是一个Python包;其中暂无内容。djangosite/settings.py
:项目配置文件。初始情况下,其中定义了本项目引用的Django组件、Django项目名等。在之后的开发中,还需要在其中配置数据库参数、导入的其他Python包等信息。djangosite/urls.py
:维护项目的URL路由映射,即定义客户端访问的URL由哪一个Python模块解释并提供反馈。在初始情况下,其中只定义了“/admin”即管理员站点的解释器。djangosite/wsgi.py
:定义WSGI的接口信息,用于与其他Web服务器集成,一般本文件在生成后无需改动。
为了在项目中开发符合MVC架构的实际应用程序,我们需要在项目中建立Django应用。每个Django项目可以包含多个Django应用。
下面在djangosite项目里创建一个名为app的应用:
1. 进入Django项目的目录文件下:cd djangosite
2. 输入如下命令:python manage.py startapp <应用名>
- 对默认生成的文件进行解释:
__init__.py
:表明app是一个Python包;其中暂无内容。admin.py
:管理站点模型的声明文件,默认为空。apps.py
:应用信息定义文件。在其中生成了类AppConfig,该类用于定义应用名等Meta数据。migrations包
:用于在之后定义引用迁移功能。models.py
:添加模型层数据类的文件。tests.py
:用于测试代码文件。views.py
:在此定义URL响应函数。
完成项目和应用的建立后,即可开始编写网站的应用代码。这里通过为注册页面显示一个欢迎标题,来演示Django的路由映射功能。
在djangosite/app/views.py
中建立一个路由响应函数:
from django.http import HttpResponse
def welcome(request):
return HttpResponse("Welcome to my tiny twitter!
")
通过URL映射将用户的HTTP访问与上面建立的路由响应函数绑定起来——在djangosite/app/
目录中新建一个urls.py
文件,管理应用app中的所有URL映射:
from django.conf.urls import url
from . import views # .表示在本目录下引用views.py
urlpatterns = [
url(r'', views.welcome, name='first-url'),
]
'''
解释:
第一行代码引入了django.conf.urls中的url()函数,因为Django中的所有路由映射由该函数生成。
第二行代码引入了djangosite/app/views.py模块。
定义了关键变量urlpatterns,该变量是一个列表,保存所有由url()函数生成的路由映射。
url(r'', views.welcome, name='first-url')表示:
把所有路由映射到views.py的welcome函数中,并把该映射命名为first-url。
'''
在djangosite/urls.py
的urlpatterns
中增加一项,声明对应用app中urls.py文件的引用:
from django.contrib import admin
from django.urls import path
from django.conf.urls import url # 新增语句1
from django.conf.urls import include # 新增语句2
urlpatterns = [
url(r'^app/', include('app.urls')), # 新增语句3
path('admin/', admin.site.urls),
]
'''
解释:
首先通过import语句引入了django.conf.urls.include()函数;
然后在urlpatterns列表中增加了一个路径app/,将其转接到app.urls包,即djangosite/app/urls.py文件。
这样,通过include()函数就将两个urlpatterns连接起来了。
说明:
可以看到这里的urlpatterns中既有url()函数,又有path()函数,
两者的作用都是在urlpatterns中定义路由映射。
它们的区别在于url()的第一个参数用正则表达式表达URL路由,
而path()的第一个参数是普通字符串。
'''
通过以上配置和编码过程,我们已经可以查看网站效果了。查看网站效果首先需要通过manage.py
启动内置的本地Web服务器。命令格式如下:
python manage.py runserver <可选项:指定IP和端口号>
# 该命令中的`runserver`是启动网站的关键字,后面的参数指定网站绑定的IP地址与端口号(0.0.0.0表示绑定本机的所有IP)
第一次启动服务器时,出现了上面的错误,这个错误表示没有数据库应用。解决方式为输入
python manage.py migrate
(migrate在后面5.3、5.4节中将再次涉及):
第一次输入网址进行测试时,遇到了如下的错误:
解决方式:修改djangosite/setting.py
文件中ALLOWED_HOSTS的值为[’*’] 。
现在开始Model层的处理,即设计和开发信息发布的数据访问层。
在djangosite/settings.py
中找到INSTALLED_APPS
数组,向其中添加应用app的Config类,目的是告诉Django需要安装应用app中的模型:
INSTALLED_APPS = [
'app.apps.AppConfig', # 此行新增
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
'''
解释:
新增语句app.apps.AppConfig表示djangosite/app/apps.py中自动生成的AppConfig类。
''
打开djangosite/app/models.py
,在其中新建一个模型类Moment
用来定义信息发布表:
from django.db import models
# Create your models here.
class Moment(models.Model):
content = models.CharField(max_length=200)
user_name = models.CharField(max_length=20)
kind = models.CharField(max_length=20)
'''
解释:
首先通过import引入django.db.models类,所有Django模型类必须继承自它。
然后定义了models的子类Moment,在其中定义了3个字符串类型的字段:
content保存消息的内容;
user_name保存发布人的名字;
kind保存消息的类型。
'''
Django的术语“生成数据移植文件”(makemigrations)是指将models.py
中定义的数据表转换成脚本的过程:
python manage.py makemigrations <应用名>
- 对上图的运行结果进行解释:
在模型的修改过程中,可以随时调用makemigrations
生成中间移植文件。而当需要使移植文件生效、修改真实的数据库schema时,则需要通过manage.py的migrate
命令使修改同步到数据库中。比如:
此命令执行的过程是检查djangosite/app/migration
目录中的所有文件,逐步使历次生成的移植文件生效。
技巧:可以在每次修改models.py文件内容后运行
makemigrations命令
,检查改动是否符合数据库的语法规则;在调试运行之前,运行一次migrate命令
使之前的所有改动全部生效。
小结:模型类就相当于定义了一个数据表,用来存储相应的内容。数据表可以通过后台进行管理(第7节讲述如何进行后台管理)。
本节的任务是设计和开发消息录入页面。该页面的基本功能为:提供输入界面,让用户输入名字、文本消息内容、选择消息类型,用户提交后网页自动设置该消息的时间并保存到数据库中。
在djangosite/app/
下新建forms.py
文件,在其中定义表单类MomentForm
:
from django.forms import ModelForm
from app.models import Moment
class MomentForm(ModelForm):
class Meta:
model = Moment
fields = '__all__' # 引入所有字段
'''
解释:
引入django.forms.ModelForm类,该类是所有Django表单类的基类。
引入在本应用models.py中定义的Moment类,以便在后面的表单类中关联Moment类。
定义表单类MomentForm,在其中定义子类Meta;在Meta中声明与本表单关联的模型类及其字段。
fields字段可以设置为__all__,表示将所有模型类中的字段引入表单类中;
也可以用列表形式声明所要导入的属性,比如:fields = ('content', 'user_name', 'kind')
'''
因为我们要实现【以单选的方式设置消息类型】的功能,所以这里需要修改djangosite/app/models.py
文件——定义单选枚举值:
from django.db import models
# 新增元组用于设置消息类型枚举项
KIND_CHOICES = (
('Python技术', 'Python技术'),
('数据库技术', '数据库技术'),
('经济学', '经济学'),
('文体资讯', '文体资讯'),
('个人心情', '个人心情'),
('其他', '其他'),
)
class Moment(models.Model):
content = models.CharField(max_length=200)
user_name = models.CharField(max_length=20, default='匿名')
# 修改kind定义,加入choices参数
kind = models.CharField(max_length=20, choices=KIND_CHOICES, default=KIND_CHOICES[0])
注意,因为本次编辑将导致模型层发生变化,所以需要用manage.py命令行工具运行makemigrations
和migrate
命令来更新数据库的定义:
模板是Python Web框架中用于产生HTML、XML等文本格式文档的术语。模板文件本身也是一种文本文件,开发者需要手工对其编辑和开发。
建立目录djangosite/app/templates
,在其中新建模板文件moments_input.html
:
<html>
<head>
<meta charset="UTF-8">
<title>消息录入页面title>
head>
<body>
<form action="?">
<fieldset>
<legend>请输入并提交legend>
{{ form.as_p }}
<input type="submit" value="submit" />
fieldset>
form>
body>
html>
开发视图函数使得表单类和页面模板衔接起来。
打开djangosite/app/views.py
文件,向其中加入如下函数:
import os
from app.forms import MomentForm
from django.http import HttpResponse
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.shortcuts import render
# 新增视图函数moments_input()
def moments_input(request):
if request.method == 'POST':
form = MomentForm(request.POST)
if form.is_vaild():
moment.save()
return HttpResponseRedirect(reverse('first-url'))
else:
form = MomentForm()
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
return render(request, os.path.join(PROJECT_ROOT, 'app/templates', 'moments_input.html'), {'form': form})
'''
解释:
moments_input()函数定义了两种访问方式的不同处理方法——
1. 如果是用户的post表单提交,则保存moment对象,并重定向到欢迎页面。
其中reverse()函数根据映射名找到正确的URL地址【URL反向解析】。
2. 如果是普通的访问,则返回moments_input.html模板的渲染结果作为HTTP Response。
render()的第三个参数将form作为参数传给了模板,
这样在模板文件中才能访问该forms.MomentForm的实例。
'''
def welcome(request):
return HttpResponse("Welcome to my tiny twitter!
")
在djangosite/app/urls.py
文件中增加新增的视图函数的路由映射:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'moments_input', views.moments_input), # 本行新增
url(r'', views.welcome, name='first-url'),
]
'''
解释:
url(r'moments_input', views.moments_input)表示:
把moments_input映射到views.py的moments_input函数中。
'''
小结:视图views.py
文件中的方法都需要作为网址名称添加到urls.py
文件中进行映射,从而使得在访问相应的网站时,显示出对应的内容(这一显式出来的内容就是views.py
文件中的方法体)。
Django管理界面是一个通过简单的配置就可以实现的数据模型后台的Web控制台。管理界面通常是给系统管理员使用的,以完成元数据的输入、删除、查询等工作。
首先将管理界面需要管理的模型类添加到djangosite/app/admin.py
文件中,具体如下:
from django.contrib import admin
from .models import Moment
admin.site.register(Moment)
'''
解释:
本文件只需要通过admin.site.register()函数逐个声明要管理的模型类即可。
'''
在第一次访问管理界面之前,需要先建立账户:
然后就可以进行访问了:
说明:管理界面中的模型类都是以英文复数形式呈现,例如Moment模型类被表达为Moments。
点击页面中的Moments
:
点击ADD MOMENT
就可以进行操作了:
添加后效果如下:
Django模型层(models.py
)是Django框架自定义的一套独特的ORM技术。
使用Django模型开发的首要任务就是定义模型类及其属性。每个模型类都可以被映射为数据库中的一个数据表,而类属性被映射为数据字段,除此之外,数据库表的主键、外键、约束等也通过类属性完成定义。
from django.db import models
# 定义模型类
class ModelName(models.Model):
# 定义模型字段
field1 = models.XXField(...)
field2 = models.XXField(...)
...
# 定义模型元数据
class Meta:
db_table = ...
other_metas = ...
all()
filter()
exclude()
get()
save()
delete()
利用数据表之间的关系进行数据建模和业务开发是关系数据库最主要的功能。
Django模型层ORM的一个强大之处是对模型继承的支持,该技术将Python面向对象的编程方法与数据库面向关系表的数据结构有机的结合。Django支持三种风格的模型继承。
继承自models.Model的抽象类不会在底层数据库中生成相应的数据表。继承自该抽象基类的子类中将存储这一父类中的属性列。抽象基类的作用是:在多个表有若干相同的字段时,可以使得开发者将这些字段统一定义在抽象基类中,从而免于重复定义这些字段。
在多表继承技术中,无论是父表还是子表,每个模型类都会在底层数据库中生成相应的数据表管理数据。父类中的字段不会重复的在多个子类的相关数据表中进行定义。从这种意义上讲,多表继承才是真正面向对象的ORM技术。
在前两种继承模型中子类模型都有实际存储数据的作用;而在代理模型继承中子类只用于管理父类的数据,而不实际存储数据。使用代理模型继承的原因是子类中新的特性不会影响父类模型及其已有代码的行为。
Django视图层的主要工作是衔接HTTP请求、Python程序、HTML模板等。
URL分发(URL Dispatcher)映射配置可以被看做Django项目的入口配置,通过URL Dispatcher可以指定用户每一次访问的后台Python处理函数是什么。
每个Django项目都有一个urls.py
文件用于维护URL Dispatcher。该文件通过维护urlpatterns列表的元素完成URL映射,每个元素可以是一个django.conf.urls.url()函数执行结果,也可以是django.urls.path()函数结果。两个函数的第一个参数均表示HTTP路径,第二个参数均表示该路径被映射到的Python函数名;两个函数的区别在于url()函数的HTTP路径名是正则表达式,而path()函数的HTTP路径名是普通字符串。
在普通URL映射中,Django将URL中的变量参数按照路径中的出现顺序传递给被调用函数名。
命名URL参数映射使得开发者可以定义这些被传递参数的参数名称。
在大型Django项目中,一个项目可能包含多个Django应用,而每个应用都有自己的URL映射规则。这时将所有的URL都保存在一个urls.py文件中就不利于对网站的维护,所以Django用户include()
函数提供了分布式URL映射的功能,使得URL映射可以被编写在多个urls.py文件中。
URL映射指的是将HTTP URL映射到Python视图函数,除此之外,Django还提供了反向的从映射名到URL地址的解析功能。URL反向解析使得开发者可以用映射名代替很多需要写绝对URL路径的地方,提高了代码的可维护性。
Django的URL反向解析功能在模板文件和Python程序中有不同的调用方法:在模板文件中用{%url 映射名%}
标签调用反向解析;在Python程序中用django.urls.reverse(映射名)
函数调用反向解析。
Django的带参数的URL反向解析功能在模板文件和Python程序中的调用方法如下:在模板文件中用{%url 映射名, 参数 %}
标签调用反向解析;在Python程序中用django.urls.reverse(映射名, 参数)
函数调用反向解析。
视图函数是Django开发者处理HTTP请求的Python函数。在通常情况下,视图函数的功能是通过模型层对象处理数据,然后用如下方式中的一种返回HTTP Response。
对于一些简单的页面,可以直接在视图函数(views.py
)中构造返回给客户端的字符串,通过HttpResponse()函数封装后返回。
下面给出一个实例,该实例的功能是返回当前服务器的时间给客户端:
from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
return HttpResponse(now)
由于模板文件包含丰富的HTML内容,因此使用渲染模板文件的方法返回页面是最常用的一种Django视图函数技术。
模板渲染通过render(HTTP Request, 模板文件名, 模板参数_字典类型)函数实现。举例如下:
from django.shortcuts import render
from app.models import Moment
def detail(request, moment_id):
m = Moment.objects.get(id=moment_id)
return render(request, 'templates/moment.html', {'headline':m.headline, 'user':m.user_name})
HTTP错误通过HTTP头中的status表达,通过给HttpResponse构造函数传递status参数,可以返回HTTP错误或状态。除此之外,Django对于常用的status状态定义了若干HttpResponse子类,所以也可以通过直接使用这些子类来返回HTTP错误。
模板文件是一种文本文件,模板文件主要由目标文件的内容组成(比如HTML、CSS等),同时辅以模板的特殊语法用于替换动态内容。
下面是一个功能较全的典型模板文件:
{% extends "base.html" %}
{% block title %}
{{ section.title }}
{% endblock %}
{% block content %}
<h1>
{{ section.title }}
</h1>
{% for moment in moment_list %}
<h2>
{{ moment.headline|upper }}
</h2>
{% endfor %}
{% end block %}
{# 注释内容 #}
{{ 变量名 }}
循环:for
条件:if、ifequal、ifnotequal
链接:url
将多个模板文件的公用部分编写在一个模板文件中,然后在其他模板文件中共享该公用部分的内容。这个公共文件被称作父文件,它使用{% block 为这个block命名 %} ...这里是需要被子文件补充的内容... {% endblock %}
标签指定需要被子文件覆盖、重写的区域。
示例:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}title>
head>
<body>
<div>
<a href="{% url 'home' %}">
<h3>我的个人博客网站h3>
a>
div>
<hr>
{% block content %} {% endblock %}
body>
html>
子文件中通过{% extends 父文件名%}
标签指定需要引用的模板文件,然后通过{% block 该block的名称 %} ... {% endblock %}
标签重写。
示例:
{% extends 'base.html' %}
{% block title %}
{{ blog.title }}
{% endblock %}
{% block content %}
<h3>{{ blog.title }}h3>
<p>博客分类:
<a href="{% url 'blogs_with_type' blog.blog_type.pk %}">
{{ blog.blog_type.type_name }}
a>
p>
<p>发表日期:{{ blog.created_time|date:"Y-m-d G:n:s" }}p>
<p>{{ blog.content }}p>
{% endblock %}
注意:通常把父文件放在最外层的
templates
下,而不是放在某个应用下的templates
文件中,这样的话,多个应用都可以使用这个父文件了,从而使得“高内聚,低耦合”。
但这样做的话,需要修改myblog/settings.py
文件使得templates
文件夹下的文件变为公共部分:
另外,关于模板文件到底是放在某个具体的应用下,还是放在这个大项目下,应遵从如下建议:与项目有关则放在项目下,只与某个具体应用有关则放到这个应用下的templates
下:
既然模板文件可以放得位置比较多,那么我们在引用一个模板文件的时候,其内部运行流程是怎么的呢——模板查找顺序:
- 先在settings.py->TEMPLATES->DIRS这个列表中设置的路径查找下有无该模板,如果有,则返回
- 检查当前视图函数所处的app是否已经安装,如果已经安装,则就先该app下的templates文件夹中查找模板
- 检查其他已经安装的app下的templates文件夹
- 抛出TemplateDoesNotExist的异常
Django为继承自Form类的表单维护了一个绑定(bound)状态。
Django表单数据验证是指在服务器端用Python代码验证表单中数据的合法性。
验证表单中的字段是否符合特定的格式要求。
验证开发者自定义的一些逻辑要求。
当视图函数收到表单的post提交时,经常需要通过验证用户是否修改了表单数据然后进行相应的处理。
如果Django默认的管理员站点不能满足应用的需求,那么开发者可以通过继承Django定义的管理员数据模型、模板、站点类来开发出个性化的管理员站点。
具体实例见:https://blog.csdn.net/jy_z11121/article/details/104125765