Django是现在世界上python语言应用最广泛、发展最成熟的web框架。它足够完整 ,不需要依赖其他就可以开发出 非常完整的web应用。Django的主要内容如下:
- Django综述:了解Django的历史,特点,总体结构,并且实践在主机中安装Django框架。
- 开发Django站点:通过实际的例子了解Django站点的开发流程。
- Django模型层:系统学习models层组件及其开发技巧。
- Django视图层:详细的URL定制方法,装饰器的开发和使用。
- Django表单:系统学习Django表单的开发方法,常用的字段类型。
- 管理员站点:学习配置和开发Django自带的管理员站点组件,快速建立管理界面
一.Django综述
1. Django的特点和结构
Django于2003年诞生于美国的堪萨斯州,最初是用来制作在线新闻web网站,于2005年加入了BSD许可证家族,成为开源网络框架。Django根据比利时的爵士音乐家Django Reinhardt命名。
1.1 Django框架的特点
相对于python的其它web框架,Django的功能是最完整的,Django定义了服务发布,路由映射,模板编程,数据处理的一整套功能。这也意味着Django模块之间紧密耦合,开发者需要学习Django自己定义的一整套技术。它的主要特点如下:
- 最完善的文档。
- 集成数据访问组件:Django的Models层自带数据库ORM组件,似的开发者无需学习其他的数据库访问技术(DBI,SQLAlchemy)
- 强大的URL映射技术:Django使用正则表达式管理URL映射,因此给开发者带来了极高的灵活性。
- 后台管理系统的自动生成:开发者只需要通过简单的几行配置和代码就可以实现完整的后台数据管理web控制台。
- 错误信息非常完整:开发调试过程中出现错误,Django可以提供非常完整的错误信息帮助开发者定位问题,不如缺少xxx组件的配置引用等。
1.2 Django的组成结构
Django是遵循MVC架构的web开发框架,主要又以下几个部分组成。
- 管理工具:一套内置的创建站点,迁移数据,维护静态文件的命令工具。
- 模型(Model):提供数据访问接口和模块,包括数据字段、元数据、数据关系等的定义和操作。
- 视图(View):Django的视图层封装了HTTP Response和request的一系列操作和数据流,主要的功能包括URL映射机制、绑定模板等。
- 模板(Templates):是一套Django自己的页面渲染模板语言,用若干内置的tags和filters定义页面的生成方式。
- 表单(form):通过内置的数据类型和控件生成HTML表单。
- 管理站点(Admin):通过声明需要管理的Model,快速生成后台数据管理站点。
1.3 安装Django
在安装pip工具的python环境中可以直接使用pip install django命令进行安装。或者下载Django的离线包,使用python setup.py install命令进行安装。
安装完成后,进入python,通过如下命令测试是否安装成功。
import django
print(django.version)
二.Django项目站点
2.1 建立项目
在进行项目开发的第一步就是要建立Django项目,建立的语法如下:
django-admin startproject djangotest。
上述代码中的django-admin是安装好Django组件后在python目录中生成的Django项目管理工具。该命令在当前的目录下创建了项目工程文件,工程文件中的目录结构如下:
上图就是成功生成的项目文件,默认生成的几个文件非常重要,在今后的开发或者维护中要一直使用它们。各个文件简单的解释如下:
- manage.py:django用于管理项目的命令行工具,其中的站点开启,数据库生成。迁移,静态文件设置等都要通过该文件完成。
- 内置的djangotest/目录中包括的是项目的实际文件,同时包含了一个__init__.py文件,故该目录是一个python包。
- setting.py文件:django项目的配置文件,其中定义了django的组件、项目名称、数据库连接参数以及其他的python包信息。
- urls.py:维护项目的URL映射。
- wsgi.py:定义WSGI的接口信息,用于与其他的web服务器进行交互,一般不需要进行改动。
2.2 建立应用
为了在项目中开发符合MVC架构的实际应用程序,我们需要在项目中建立Django应用。每一个django项目可以包含多个django应用。应用的建立语法如下。
python manage.py startapp app。
其中的manage.py是建立项目是用到的命令行工具,startapp是命令行关键字,app是建立的应用名称。命令完成后会在项目的目录下建立如下图所示的文件结构。
其中各个文件功能的说明如下:
- __init__.py:暂无内容,只是声明app是一个python包。
- admin.py:管理站点模型的的声明文件,默认情况下什么东西都莫得。
- apps.py:应用信息的定义文件。在其中生成了AppConfig,该类用于定义应用名等Meta数据。
- migrations包:迁移数据时生成的中间文件放在该目录下。
- models.py:添加模型层数据类的文件。
- test.py:测试代码文件。
- views.py:定义URL响应函数,业务代码的具体实现。
2.3 基本视图
在完成django项目和应用的建立后,就可以开始编写网站的代码了,下面简单演示一下django的路由映射功能。
(1)在app/views.py中创建一个路由响应函数,代码如下
from django.http import HttpResponse def welcome(request): return HttpResponse('<h1>你好,django</h1>')
(2)接下来,要通过URL映射将用户的HTTP访问和该函数绑定起来,在app/目录下创建一个urls.py文件,其文件的内容如下:
from django.contrib import adminfrom django.conf.urls import url from .apps import views urlpatterns = [ url(r'',views.welcome), #url(r'',views.welcome), ]
(3)在项目djangotest/urls.py文件中的urlpatterns中新增加一项,声明对应用app中的urls.py文件的引用,具体代码如下
from django.contrib import admin from django.urls import url from django.conf.urls import include urlpatterns = [ url('admin/', admin.site.urls),
url(r'app/', include('app.urls')),
]
首先导入django.conf.urls.include()函数,之后再urlpatterns列表中增加一个路径app/,将其转接到app.urls包,这样通过include()函数就可以将2个urlpatterns连接起来。
(4)上述代码书写完成后,输入python manage.py runserver 127.0.0.0:8888。命令开启web服务,在浏览其中输入网址http://127.0.0.0:8888/app,结果如下图所示。
2.4 模型类
这一节介绍的是Model层的处理,设计和开发信息发布的数据访问层。
2.4.1 配置项目INSTALLED_APPS
要在django项目的setting.py文件中告诉django需要安装应用app中的模型,方法是在setting.py文件中的INSTALLED_APPS数据结构中,在其中添加应用app的 config类,代码如下:
INSTALLED_APPS = [ 'app', # 新增 ]
2.4.2 模型定义
打开app/models.py文件,在其中新建一个模型类Mount,用来定义信息发布表,代码如下:
from django.db import models class Mount(models.Model): content = models.CharField(max_length=200) username = models.CharField(max_length=20) kind = models.CharField(max_length=20)
首先引入modoels类,所有的django模型类都是继承于它。类Mount继承了models类,在其中定义了3个字段content:消息的内容、username:发布人名称、kind:消息的类型。
2.4.3 生成数据迁移文件
django的术语“生成数据移植文件”是指将models.py中定义的数据表转换成数据库生成脚本的过程。该过程通过命令行工具manage.py完成,具体的命令输出如下:
通过输出我们可以看到完成了模型Mount的建立。输出中的0001_initial.py是数据库生成的中间文件,通过它可以知道当前数据库的版本;该文件以及以后所有的migration文件都存储于目录migrations/中。
同时在makemigrations的过程中,django会对比models.py中模型和已有数据库之间的差异,没有差异则不做操作,如再次执行makemigrations操作时将产生如下输出:
但是如果对models.py文件作出修改时,则在下一次makemigrations的时候会将修改的内容同步到数据库中。例如将kind字段修改为长度30后,执行操作,结果如下:
在该过程中产生了新的中间文件0002_auto_20190526_1431.py。需要注意的是djangotest\app\migrations目录中的文件都有manage.py自己维护,开发者不宜修改其中的内容。
2.4.4 移植到数据库
在模型的修改过程中可以随时调用makemigrations生成的中间移植文件。当需要真正的修改数据库的时候,需要通过manage.py的migrate命令让修改同步到数据库。例如:
2.5 表单视图
2.5.1 定义表单类
建立表单类文件djangotest/app/forms.py,在其中定义表单类MountForm,代码如下:
from django.forms import ModelForm from models import Mount class MountForm(ModelForm): class Meta: model = Mount fields = '__all__'
- 引入from django.forms import ModelForm类,该类是所有Django表单类的基类。解析如下:
- 引入models.py文件中的Mount类,以便在以后进行关联。
- 定义表单类MountForm,在其中定义子类Meta。在子类中声明与本表单关联的模型类和字段。
- Fields字段可以设为__all__,也可以用列表的形式声明所要导入的属性,比如:fields = ('content','username','kind'),all是将所有字段导入。
2.5.2 修改模型类
为了使用户能够以单选的方式设置消息类型,则需要在models.py文件中定义单选的枚举类型,并且与模型类Mount进行关联。代码如下:
from django.db import models # Create your models here. class Mount(models.Model): content = models.CharField(max_length=200) username = models.CharField(max_length=20,default='张晓琳') # 新增元祖用于设置消息枚举类型 KIND_CHOICES = (('python','python'),('java','java'),('C语言','C语言'), ('C++', 'C++'), ('mysql', 'mysql'),) kind = models.CharField(max_length=30,choices=KIND_CHOICES,default=KIND_CHOICES[0])
- 为kind字段增加了消息枚举类型,为username字段使用default增加了默认属性。代码解析:
- 本次编辑使得模型层发生改变,需要进行同步数据库的操作,具体操作参考2.4.3和2.4.4章节的内容。
2.5.3 开发模板文件
模板文件是python web框架中用于产生HTML、XML等文本格式文档的术语。模板文件本身也是一种文本文件,开发者需要手工进行编辑。建立目录djangotest/app/templates,在其中新建模板文件moments_input.html,文件内容如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>消息录入页面</title> </head> <body> <form action="?" method="post"> <fieldset> <legend>请输入并提交</legend> {{ form.as_p }} <input type="submit" value="提交" /> </fieldset> </form> </body> </html>
模板文件是html文件,其中的模板内容使用大括号进行标识。这里使用{{ form.as_p }}定义表单类的输入字段。
2.5.4 开发视图文件
接下来开发的是视图文件,使得表单类和页面模板能够衔接起来。打开djangotest/app/views.py文件,在其中加入如下函数:
import os
from app.form import MountForm
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
def moments_input(request):
if request.method == 'POST':
form = MountForm(request.POST)
if form.is_valid():
moment = form.save()
moment.save()
# return HttpResponseRedirect("app.views.welcome")
else:
form = MountForm()
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})
在urls.py文件中增加视图函数的路由映射。
urlpatterns = [
url('admin/', admin.site.urls),
url(r'^app/', include('app.urls')),
]
访问地址为http://127.0.0.1:9999/app/moments_input。
三.Django管理界面
站点分为“内容发布”和“公共访问”两部分,“内容发布”的部分负责添加、修改、删除内容,开发这些重复的功能是一件单调乏味、缺乏创造力的工作。为此,Django会根据定义的模型类完全自动地生成管理模块
3.1使用django的管理
3.1.1创建管理员用户
- 创建一个管理员用户:
python manage.py createsuperuser
,按提示输入用户名、邮箱、密码
- 启动服务器,通过“127.0.0.1:8000/admin”访问,输入上面创建的用户名、密码完成登录
- 进入管理站点,默认可以对groups、users进行管理
3.1.2管理界面本地化
编辑settings.py文件,设置编码、时区
LANGUAGE_CODE = 'zh-Hans' TIME_ZONE = 'Asia/Shanghai'
3.1.3向admin注册booktest的模型
打开booktest/admin.py文件,注册模型
from django.contrib import admin from models import BookInfo admin.site.register(BookInfo)
刷新管理页面,可以对BookInfo的数据进行增删改查操作
问题:如果在str方法中返回中文,在修改和添加时会报ascii的错误
解决:在str()方法中,将字符串末尾添加“.encode('utf-8')”
3.1.4自定义管理页面
Django提供了admin.ModelAdmin类
通过定义ModelAdmin的子类,来定义模型在Admin界面的显示方式
class QuestionAdmin(admin.ModelAdmin): ... admin.site.register(Question, QuestionAdmin)
列表页属性说明
- list_display:显示字段,可以点击列头进行排序——
list_display = ['pk', 'btitle', 'bpub_date']
- list_filter:过滤字段,过滤框会出现在右侧——
list_filter = ['btitle']
- search_fields:搜索字段,搜索框会出现在上侧——
search_fields = ['btitle']
- list_per_page:分页,分页框会出现在下侧——
list_per_page = 10
添加、修改页属性
- fields:属性的先后顺序——
fields = ['bpub_date', 'btitle']
- fieldsets:属性分组
fieldsets = [ ('basic',{'fields': ['btitle']}), ('more', {'fields': ['bpub_date']}), ]
3.2关联对象
对于HeroInfo模型类,有两种注册方式
- 方式一:与BookInfo模型类相同
- 方式二:关联注册
按照BookInfor的注册方式完成HeroInfo的注册
接下来实现关联注册
from django.contrib import admin from models import BookInfo,HeroInfo class HeroInfoInline(admin.StackedInline): model = HeroInfo extra = 2 class BookInfoAdmin(admin.ModelAdmin): inlines = [HeroInfoInline] admin.site.register(BookInfo, BookInfoAdmin) 可以将内嵌的方式改为表格 class HeroInfoInline(admin.TabularInline)
3.2.1布尔值的显示
发布性别的显示不是一个直观的结果,可以使用方法进行封装
def gender(self): if self.hgender: return '男' else: return '女' gender.short_description = '性别' 在admin注册中使用gender代替hgender class HeroInfoAdmin(admin.ModelAdmin): list_display = ['id', 'hname', 'gender', 'hcontent']
四.Django模型层
Django模型层是Django框架自定义的一套独特的ORM技术,通过上几章节的总结学习,我们已经大概了解了Django模型层的基本概念和开发流程。现在来学习模型层的技术细节和高级话题。
4.1 模型类定义语法
使用Django模型开发的首要任务就是定义模型类和类的属性。每个模型类都可以被映射为数据库中的一个表,类属性被映射成为数据字段,除此之外,数据库中的主键、外键、约束等也可以通过类属性完成定义。
4.1.1基本操作
模型定义的的基本结构如下所示:
from django.db import models class ModelsName(models.Model): field1 = models.XXfield(...) field2 = models.XXfield(...) ... class Meta: db_table = ... other_metas = ...
- 所有Django模型类继承自django.db.models.Model类。解析如下:
- 通过其中的类属性定义模型字段,模型的字段属性必须是某种models.XXField类型。
- 通过模型类中的Meta子类定义模型元数据,比如数据库表的名称,数据默认的排序方式等。
4.1.2 子类Meta属性汇总
属性 | 属性值 | 说明 |
abstract | True or False | 标识本类是否为抽象的基类 |
app_lable | app_lable = “myapp” | 定义本类所属的应用 |
db_table |
db_table=“mytable” | 映射的数据库表名称 说明:如果Meta中不提供db_table字段,则django会为模型自动生成数据库表名,格式为“应用名_模型名”。 |
db_tablespace |
无 | 映射的表空间名称。表空间的概念只在某些数据库中如oracle中存在,不存在表空间概念的数据库将会忽略本字段。 |
Default_related_name | 无 | 定义本模型的反向关系引用名称,默认和模型名一致。 |
get_latest_by |
无 | 定义按那个字段值排列获得模型的开始或者结束记录,本属性值通常指向一个日期或者整形的模型字段。 |
managed |
True or False | 定义django的manage.py命令行工具是否管理本模型。属性默认设置为TRUE。设置为false时,需要手动维护数据库表。 |
order_with_respect_to | 无 | 定义本模型可以按照某种外键引用的关系排序。 |
ordering | Ordering = [“username”“-pub_data”] | 本模型记录的默认排序字段,可以设置多个字段,默认为升序排列。降序排列的话在字典前加上- |
default_permissions | Add,change,delete | 模型的操作权限,默认如值所示。 |
proxy | True or False | 本模型及所有继承自本模型的子类是否为代理模式。 |
required_db_features |
fequired_db_features =[“gis_enabled”] | 定义底层数据库所必须具备的特性,如值所示只将本数据模型生成在满足gis_enabled特性的数据库中 |
required_db_vendor | SQLite、PostgreSQL、MySQL、Oracle | 定义底层数据库的类型,如果定义了本属性,则模型只能在其声明的数据库中维护。 |
unique_together |
unique_together = ((“user”,“pwd”)) | 用来设置不重复的的字段组合,必须唯一(可以将多个字段做联合唯一)。 |
index_together | index_together=[ [“pun_data”,“deadline”]] | 定义联合索引的字段,可以定义多个。 |
verbose_name |
verbose_name = ‘资产管理’ | 定义一个易于理解和表述的单数形式的对象名称。如果不设置该值,则Django将会使用该models的类名作为它的对象表述名。 |
verbose_name_plural |
| 定义一个易于理解和表述的复数形式的对象名称。 |
4.1.3 常用字段类型
Django的普通字段是指模型类中除了外键以外的数据字段属性。它为Django使用模型的时候提供如下信息:
- 在数据库中用什么类型定义模型字段,比如INTEGER、VARCHAR等。
- 用什么样的HTML标签显示模型字段,比如<input type=”radio”>
- 需要什么样的HTML表单验证数据。
所有的数据字段的属性必须继承自抽象类django.db.models.Field,我们可以自己定义继承自该类的数据字段,也可以使用Django预定义的一系列数据字段,常见的Django预定义字段类型描述如下表所示。
字段 | 字段说明 |
AutoField | 一个自动递增的整形字段,添加记录时它会自动增长。AutoField字段通常充当数据表的主键;如果没有添加,django会自动添加一个该字段。 |
BigIntegerField | 64位的整型字段 |
BinaryField | 二进制数据字段,通过bytes进行赋值 |
BooleanField | 布尔字段,对应<input type=“CheckBox”> |
CharField | 字符串字段,对应<input type=“text”> |
TextField | 大容量文本字段,对应<textarea> |
CommaSeparatedIntegerField | 存放逗号分隔的整数值,有特殊的表单验证要求 |
DateField | 日期字段,相对应的HTML标签是<input type=“text”>、JavaScript日历和一个today快捷按键。可选参数:auto_now:保存时,设置为当前时间;auto_now_add:首次创建时,保存为当前时间。 |
DateTimeField | 类似于DateField,但是它却支持时间的输入。 |
DurationField | 存储时间周期,用python的timedelta类型构建。 |
EmailField | 检查Email合法性的CharField。 |
FileField | 文件上传字段。在定义本字段是必须传入参数upload_to,用于保存上传下载文件的服务器文件系统的路径。这个路径必须包含strftime formatting,该格式将被上载文件的date/time替换 |
FilePathField | 按照目录的规则限制选择文件,定义本字段是必须传入参数path,用以限定目录。 |
FloatField | 浮点型字段。定义本字段时必须传入参数max_digits和decimal_places,用于定义总位数和小数位数。 |
ImageField | 类似于 FileField,同时验证上传的对象是否是一个合法图片,可选参数有2个:height_field和width_field,有这两个参数,图片按照规定的长和高进行存储。 |
IntegerField | 保存一个整数 |
IPAddressField | 一个字符串形式的IP地址 |
NullBooleanField | 类似于BooleanField,但是多出了一个None选项 |
PhoneNumberField | 带有美国风格的电话号码校验的CharField(格式为XXX-XXX-XXXX) |
PositiveIntegerField | 只能输入非负数的IntegerField |
SlugField | 只包含字母,数字,下划线和连字符的输入,通常同于URL |
SmallIntegerField | 类似于IntegerField,但是只有较小的输入范围,依赖所使用的数据库。 |
TimeField | 时间字段,类似于DateTimeField,但只能表达和输入时间。 |
URLField | 用于保存URL |
USStateField | 美国州名的缩写字段 |
XMLField | XML字符字段,是具有XML合法性验证的TextField |
|
|
4.1.4 常用字段类型的参数
每个字段类型都有一些特定的HTML标签和表单验证参数,比如heigth_field,path等,但同时每个字段都有可以设置的公告方法,比如primary_key参数设置的主键字段。
from django.db import models class Comment(models.Model): # 设置主键id id = models.AutoField(primary_key=True)
- null:定义是否允许相对应的数据字段为null,默认设置为false。除了上述说到的主键外,还有其他参数,如下:
- blank:定义字段是否可以为空。null是一个数据库中的非空约束;blank用于字段的HTML表单验证,即判断用户是否可以为空。
- choices:定义字段的可选值。该字段的值是一个包含二维元素的元祖。元祖中的第一个值是实际存储的值,第2个值是HTML页面中进行选择时显示的值。示例代码如下:
from django.db import models class Comment(models.Model): # 设置主键id id = models.AutoField(primary_key=True) # 定义选择元祖 levels = (('1','Very Good'),('2','Good'),('3','Normal'),('4','Bad')) level = models.CharField(max_length=10,choices=levels)
上述代码中定义了level字段用于让用户选择满意度,其中的1,2,3,4是在数据库中实际存储的数据,而very good、Good、Normal、Bad等是在HTML页面中供选择的选项。
- default:设定默认值,例如:default=”Good”。
- help_text:HTML页面中输入控件的帮助字符串。
- unique:定义字段是否为数据库的唯一约束。
除了这些名称的字段外,Django中的所有字段还有一个无名参数,可以设置该字段在HTML页面的显示名称,比如:
level = models.CharField('客户满意度选择',max_length=10,choices=levels)
4.2 模型类数据操作
4.2.1 数据查询
定义如下的Django models,用于演示Django的模型基本查询技术。
from django.db import models class Comment(models.Model): # 设置主键id id = models.AutoField(primary_key=True) # 定义选择元祖 levels = (('1','Very Good'),('2','Good'),('3','Normal'),('4','Bad')) level = models.CharField('客户满意度选择',max_length=10,choices=levels) headline = models.CharField(max_length=255) body_text = models.TextField() pub_date = models.DateTimeField() n_visits = models.IntegerField() def __str__(self): return self.headline
(1) Django有2中过滤器用于对记录进行刷选,如下:Django通过模型的objects对象查询模型的数据,例如下面的语句查询Comment模型的所有数据:Comment.objects.all()
- filter(**kwargs):返回符合条件的数据集。
- exclude(**kwargs):返回不符合条件的数据集。
- 例如下面的语句查询所有pub_date_year的字段是2015的数据: Comment.objects.filter(pub_date_year = 2015)
(2)连接查询
多个filter和exclude可以连在一起进行查询,比如查询所有2015年非1月的n_visits不为0的记录:Comment.objects.filter(pub_date_year == 2015).exclude(pub_date_month=1).exclude(n_visits_exact=0)。
我们可以看到代码中的pub_date_year,它并不是模型中定义的一个字段,而是Django定义的一种独特的字段查询方式,本例中该查询的含义是“pub_date字段的year属性为2015”。基本的表现形式为 字段名称_谓词,既由“双下划线连接的字段名称和谓词”来表达查询条件。类似的还有很多其他的谓词查询方式,完整的Django谓词列表如下标所示。
谓词 | 含义 | 示例 | 等价的sql语句 |
exact | 精确等于 | Com.objects.filter(id_exact=14) | Select * from Com where id = 14; |
iexact | 大小写不敏感的等于 | Com.objects.filter(headline_iexact=”I am ok”) | Select * from Com where upper(headline)=” I am ok” |
contains | 模糊匹配 | Com.objects.filter(headline_ contains =”good”) | Select * from Com where headline like ”%good%”; |
in | 包含 | Com.objects.filter(id_ in =[1,5,9]) | Select * from Com where id in [1,5,9]; |
gt | 大于 | Com.objects.filter(id_ gt=30) | Select * from Com where id > 30; |
gte | 大于等于 | ||
lt | 小于 | ||
lte | 小于等于 | ||
startwith | 以..开头 | Com.objects.filter(body_text_ startwith=”wl”) text_ endwith=”wl” | Select * from Com where body_text like ”good%” ”%good”; |
endwith | 以..结尾 | ||
range | 在..范围内 | Com.objects.filter(pub_date_range=(starttime,endtime)) | Select * from Com where pub_date between starttime and endtime; |
year | 年 | Com.objects.filter(pub_date_year == 2015) | Select * from Com where pub_date between “2015-1-1 00:00:00” and “2015-12-31 23:59:59”; |
month | 月 | ||
day | 日 | ||
Week_day | 星期几 | ||
isnull | 是否为空 | Com.objects.filter(pub_date_isnull = True) | Select * from Com where pub_date is Null; |
(3)除了all()、filter()、exclude()等返回数据集的函数,Django还提供了get()函数用于查询单挑记录,比如查询id为3的记录:
Com.objects.get(id_exact = 1)
(4)Django还提供了用于查询指定条数的小标操作,该特性使得Django模型能够支持SQL中的limit和offset。
- Com.objects.all()[:10]—>返回数据集,查询前10条记录。
- Com.objects.all()[10:20]—>返回数据集,查询10-20条记录。
- Com.objects.all()[1]—>返回数据集,查询第二条记录。
(5)Django还提供了order_by操作,示例如下:
Com.objects.order_by(“headline”):返回数据集,按照headline字段排序。
4.2.2 数据存储
和传统的sql相比,Django的一个较大的优势是定义了一个统一的方法save(),用于完成模型的insert和update操作。在执行模型的save()函数时,Django会根据模型的主键,判断记录是否存在,如果存在执行update操作,不存在执行insert操作。
# 新增记录 obj = Comment(headline='i am ok',body_text='...sdjk', pub_date = datetime.datetime().now(),n_visits=0) obj.save() print(obj.id) # 打印主键id22 # 修改记录数据 obj.body_text = "this is my world, I am god!!" obj.save() print(obj.id) # 打印主键id=22,和新增后的id相同。
4.2.3 数据删除
Django模型提供了delete()方法用于删除记录,该方法可以进行单条和批量删除。示例代码如下:
# 删除id为3的记录 Comment.objects.get(id=3).delete() # 删除2015年所有的记录 Comment.objects.filter(pub_date_year = 2015).delete()
4.3 关系操作
利用数据表之间的关系进行数据建模和业务开发是关系型数据库最主要的功能。Django的模型层对这三种关系模型(1:1,1:N;M:N)都有强大的支持。
4.3.1 一对一关系
在SQL语言中,一对一的关系通过在两个表之间定义相同的主键来完成。在Django的模型层,可以在任意一个模型中定义OneToOneField字段,并且定义相互之间的一对一关系。如下的代码在模型Account和Contact之间定义了一对一的关系。
class Account(models.Model): user_name = models.CharField(max_length=80) password = models.CharField(max_length=255) reg_date = models.DateTimeField() def __str__(self): return "Account:%s" % self.user_name class Contact(models.Model): account = models.OneToOneField(Account,on_delete=models.CASCADE,primary_key=True) zip_code = models.CharField(max_length=10) address = models.CharField(max_length=80) mobile = models.CharField(max_length=20) def __str__(self): return "%s,%s" % (self.account.user_name,self.mobile)
- 两个模型的关系通过Contact模型中的account字段进行定义。上述代码的解析如下:
- OneToOneField()函数中的第一个字段参数是被关联的模型名称。
- l on_delete参数定义当被关联模型(Account)的记录被删除是被模型的记录如何处理,models.CASCADE定义同样被删除。
- l 模型中的__str__函数用来定义函数模型的显示字符串,py3只能用str。
在上述一对一的关系模型中的开发代码如下所示。
a1 = Account(user_name='wltest') a1.save() # 保存一个Account记录 print(a1) # 打印a1:<Account:wltest> a2 = Account(user_name='zxlText') a2.save() # 保存一个Account记录 # 利用a1初始化Contact的account字段,并保存 a3 = Contact(account=a1,mobile='1223123123') a3.save() print(a3) # <Contact:wltest,1223123123> print(a1.contact) # <Contact:wltest,1223123123>,通过关系打印,于打印a3的结果相同 print(a3.account) # <account:wltest>,通过关系打印,于打印a1的结果相同 # a2没有和Contact建立过关系,所以没有contact字段。 a1.delete() # on_delete = models.CASCADE,所以删除a1时也删除了a3
4.3.2 一对多关系
在SQL语言中,1:N关系通过在“附表”中设置“主表”的外键引用来完成。在Django模型层,可以使用models.ForeignKey()来定义外键,代码如下:
from django.db import models class Account(models.Model): user_name = models.CharField(max_length=80) password = models.CharField(max_length=255) reg_date = models.DateTimeField() def __str__(self): return "Account:%s" % self.user_name class Contact(models.Model): # account = models.OneToOneField(Account,on_delete=models.CASCADE,primary_key=True) account = models.ForeignKey(Account, on_delete=models.CASCADE) zip_code = models.CharField(max_length=10) address = models.CharField(max_length=80) mobile = models.CharField(max_length=20) def __str__(self): return "%s,%s" % (self.account.user_name,self.mobile)
上述代码于一对一关系唯一不同的地方在于models.ForeignKey定义了Contact模型中的account字段。这样一来,每个Account对象就可以与多个Contact对象关联了。模型的使用代码如下:
def welcome(request): a1 = Account(user_name='wl2',password='12343') a1.save() # 保存一条Account记录 # 为a1建立两个Contact关联的对象 c1 = Contact(account=a1,address='c1',mobile='12312321412') c1.save() # 保存C1记录 c2 = Contact(account=a1, address='c2', mobile='12312321412') c2.save() # 保存C2记录 data = {c1.address:str(c1.account), c2.address:str(c2.account),'a1':str([a1.contact_set,a1.contact_set.count()])} a1.delete() return JsonResponse({'status':200,'data':data})
在一对多关系中,每个主模型对象可以关联多个子对象,所以本例中从主模型Account对象寻找附属模型Contact的属性时contact_set,通过一个集合返回关联结果。
说明:XXX_set是Django设定的通过主模型对象访问附属模型的对象集合的属性名。
4.3.3 多对多关系
在SQL语言中,M:N关系通过建立一个中间关系表来实现,该中间表中定义了到2个主表的外键。所以在Django模型层中,开发者也可以选择用这两个1:N关系来定义M:N的关系。这种方式是通过models.ForeignKey来实现。
另外一种方式是在django的模型层中定义一个models.ManyToManyField字段来实现的,多对多关系的模型定义代码如下:
from django.db import models class Account(models.Model): user_name = models.CharField(max_length=80) password = models.CharField(max_length=255) # reg_date = models.DateTimeField() def __str__(self): return "Account:%s" % self.user_name class Contact(models.Model): account = models.ManyToManyField(Account) zip_code = models.CharField(max_length=10) address = models.CharField(max_length=80) mobile = models.CharField(max_length=20) def __str__(self): return "%s,%s" % (self.account.user_name,self.mobile)
上述代码通过在Contact中定义引用Account的ManyToManyField,实现了2个模型的多对多关联,对此模型的定义操作演示如下:
# 分别建立并保存Account和Contact的对象 a1 = Account(user_name='ww') a1.save() c1 = Contact(mobile='weeee3') c1.save() # t通过Contact对象建立关系 c1.account.add(a1) a2 = Account(user_name='jjj') a2.save() a2.contact_set.add(c1) # 通过Account对象建立关系 a3 = Account(user_name='jjj') # 对象未保存出错 a3.contact_set.add(c1) # 通过Account对象建立关系 a1.contact_set.remove() # 取消单个对象的关联 a1.contact_set.clear() # 取消所有对象的关联
4.4 面向对象ORM
Django模型层ORM的一个强大之处是对模型继承的支持,该技术将python面向对象的编程方法与数据库面向关系表的数据有机的结合。支持三种风格的模型集成。相面来介绍这三种继承方法。
4.4.1 抽象继承
抽象类继承是指父类继承models.Model,但不会在底层的数据库中生成相应的数据表,父类的相关属性字段存储在子类的数据表中。
它的作用是在多个表有想干的字段时,可以使得开发者将这些字段统一定义在抽象基类中,免于重复定义这些字段。抽象基类的定义通过在模型的Meta中定义属性abstract=True来实现,示例代码如下:
class MessageBase(models.Model): id = models.AutoField() content = models.CharField(max_length=100) user = models.CharField(max_length=20) pub_date = models.DateTimeField() class Meta: # 定义本类为抽象基类 abstract = True class Mon(MessageBase): headline = models.CharField(max_length=50) class Com(MessageBase): life = models.CharField(max_length=30,default='wl',choices=(('1','2'),('sjdk','idi')))
在子类模型的编程中,可以直接使用父类定义的字段,例如:上述代码中定义了一个抽象基类MessageBase,用于保存消息的4个字段。子类Mom和Com继承自MessageBase,并分别定义了自己的一个字段。这3个类映射到数据后,会别定义成2个数据库表。每个表都有5个字段
m1 = Mon(user='ds',headline='sds') m1.content = 'sddhjk' m1.save()
4.4.2 多表继承
多表继承的模型类都在底层的数据库中生成了相应的数据表管理数据。无论是父表还是子表都会用数据库中相对应的数据表维护模型的数据,父类中的字段不会重复的在多个子表中进行定义。从这方面来讲,多表继承才是真正面向对象的ORM技术。
多表继承不需要特殊的关键字。在Django内部通过在父类和子类之间建立一对一关系来实现多表继承,示例代码如下。
from django.db import models class MessageBase(models.Model): id = models.AutoField() content = models.CharField(max_length=100) user = models.CharField(max_length=20) pub_date = models.DateTimeField() class Mon(MessageBase): headline = models.CharField(max_length=50) class Com(MessageBase): life = models.CharField(max_length=30,default='wl',choices=(('1','2'),('sjdk','idi')))
仍然可以直接引用父类定义的字段,同时子类可以通过父类对象引用访问父类的示例。上述代码在数据库中会实际生成3个数据表。同时在对模型的编程过程中,子类
# 新建Mon对象,直接在子类中引用父类字段 m1 = Mon(user='ds',headline='sds') m1.content = 'sddhjk' m1.save() # 通过父类引用父类字段 print(m1.messagebase.content)
4.4.3 代理继承
代理模型继承指父类用于在底层数据库中管理数据表,而子类不定义数据列。只定义查询数据集的排序方式等元数据。前两种继承模型中都有实际存储数据的作用,而代理模式继承中子类只用于管理父类的数据,而不实际存储数据。设置的方法是在Meta中定义proxy=True属性来实现,示例代码如下:
class MessageBase(models.Model): id = models.AutoField() content = models.CharField(max_length=100) user = models.CharField(max_length=20) pub_date = models.DateTimeField() class Mon(MessageBase): class Meta: proxy = True ordering = ["-pub_date"]
五.Django视图层
Django视图层的主要作用是衔接HTML模板、python程序、HTTP请求等。
5.1 URL映射
URL分发映射配置可以被看做Django项目的入口配置,通过URL分发可以指定用户的每一个访问的后台python处理函数是什么。
5.1.1 普通URL映射
每一个django项目都有一个urls.py文件用于维护自身的URL dispatcher,该文件的基本内容如下:
from django.contrib import admin from django.urls import path from django.conf.urls import url from . import views urlpatterns = [ url(r'^welcome',views.welcome), url(r'^moments_input/',views.moments_input), ]
程序通过维护urlpatterns列表中的元素完成URL的映射,每个元素都是一个django.conf.urls.url的实例,函数url()的第1个参数是HTTP路径,第2个参数是该路径映射到的python函数名。
5.1.2 正则表达式
正则表达式速查表
符号 | 说明 | 例子 |
\ |
将下一个字符标记为特殊字符 | “\n”匹配一个换行符 “\\”匹配一个“\” “\(”匹配一个“(” |
^ | 字符串的开始位置 | “^abc”:以abc开头 |
$ | 字符串的结束位置 | “abc$”:以abc结尾 |
* | 前面的子表达式0或者多次 | “2*”匹配“”“2”“2222”等 |
+ | 前面的子表达式1或者多次 | “2+”匹配“1”“2”“222”等 |
? | 前面的子表达式0或者1次 | “3?”匹配“”或者“3” |
. | 除\n以外的任意单个字符 |
|
{n} | 匹配n次 | “o{2}”:匹配food中的两个o |
{n,} | 至少匹配n次 |
|
{n,m} | 匹配n到m次 |
|
x|y | 匹配x或者y | Asd|sdf:匹配Asd或者sdf |
[xyz] | 匹配xyz中任一字符 | [123]匹配1,2或者3 |
[a-z] | 匹配a-z范围内的字符 |
|
[^a-z] | 匹配不在a-z范围内的字符 |
|
5.1.3 命名URL映射
在普通的URL映射中,Django将URL中的变量参数按照路径中的出现顺序传递给被调用函数。而命名URL参数映射使得开发者可以定义这些被传递参数的名称,命名URL参数的定义方式为 ?p<param_name>pattern,示例代码如下:
from django.conf.urls import url from . import views urlpatterns = [ url(r'^year/2015/$',views.year2015), url(r'^year/?p<year>([0-9]{4})/$',views.year), url(r'^year/?p<year>([0-9]{4})/?p<month>([0-9]{2})/$',views.month), ]
上述代码中的后两个url()使用命名参数进行了定义,他们调用Views.py文件中的相关函数,调用的方式为year(request,year=xxxx)和month(request,year=xxxx,month=xx)。
当多个URL映射到一个地址是,只选择第一个映射。
5.1.4 分布式URL映射
在实际的大型项目开发中,一个项目可能包含多个Django应用,而每一个都有自己的URL映射规则。这个时候讲所有的URL映射都保存在一个文件中非常不利于对项目进行维护,所以Django提供了include()函数来进行分布式的URL映射。在项目djangotest/djangotest/urls/py的文件中引用其他应用的URL映射文件的代码如下:
from django.contrib import admin from django.urls import path from django.conf.urls import include,url urlpatterns = [ url('admin/', admin.site.urls), url(r'^app/', include('app.urls')), ]
5.1.5 反向URL映射
除了以上介绍的几种映射方式,Django还提供了反向的从映射名到URL地址的解析功能。URL反向解析使得开发者可以用映射名代替很多需要写绝对URL路径的地方,提高了代码的可维护性。
待续。。。非常重要
5.2 视图函数
视图函数是Django开发者处理HTTP请求的python函数。在通常的情况下,视图函数的功能是通过模型层对象处理数据,然后用如下所示的一种方式返回数据。
- 直接构造HTTP body
- 用数据渲染HTML模板文件。
- 如果有逻辑错误,则直接返回HTTP错误或者其他状态。
5.2.1 直接构造html页面
对于以下简单的页面,可以直接在视图函数中构造返回给客户端的字符串,该功能是通过HttpResponse()函数封装返回。
from django.http import HttpResponse def index(request): HttpResponse('<h2>我是你爸爸!</h2>')
5.2.2 用数据渲染HTML模板
由于模板文件可以包含丰富的HTML标签内容,所以使用渲染模板文件的方法返回数据时一种常用的技术。该功能通过render()函数来实现。
from django.shortcuts import render def index(request): render(request,'index.html')
render()函数的第一个参数是HTTP Request,第二个参数是模板文件名,第三个参数是想模板文件中返回的数据,是字典的形式。
5.2.3 返回HTTP错误
HTTP的错误通过HTTP头中的status来进行表述,通过给httpResponse函数传递status参数可以返回HTTP的错误或者状态。如:
from django.http import HttpResponseRedirect,HttpResponse def index(request): HttpResponse(status=404)
通过上述的代码可以返回HTTP 404错误。但是为了方便开发者的使用,Django对常用的status状态进行了进一步的封装,具体的实现如下所示:
- HttpResponseRedirect:返回的状态是302,作用是URL的重定向,需要将目标的地址作为参数进行传递。
- HttpResponseNotModified:返回状态304,指示浏览器显示上次请求的缓存结果页面。
- HttpResponsePermanentRedirect:返回状态301,和HttpResponseRedirect类似,只不过这是一个永久的重定向。
- HttpResponseBadRequest:返回状态400,请求内容错误。
- HttpResponseForbidden:返回403,禁止访问的错误。
- HttpResponseNotFound:返回404,不能访问的错误。
- HttpResponseNotAllowed:返回405,访问方法(get,post)错误。
- HttpResponseServerError:返回500,服务器内部错误。
5.3 模板语法
模板文件是一种文本文件,主要又目标文件的内容组成,然后辅以模板的特殊语法用于替换动态内容。下面的代码是一个功能较为齐全的模板文件。
{% extend "base.html" %} # 继承基类模板 {% block title %}title 内容{% endblock %} {% block content %} <h1>{{ section.title }}</h1> {% for i in ins %} <h3> {{ i.code | upper }} </h3> {% endfor %} {% endblock %}
5.3.1 流程控制
Django模板提供基本的流程控制功能,使用{% for %}语句实现循环逻辑控制以及使用{% if %}语句实现逻辑判断功能,示例代码如下:
{% for moment in moments %} <h3> {{ moment.user | upper }} </h3> {% endfor %} {% if moment.id < 10 %} <h3>{{ moment.headline }}</h3> {% elif moment.id < 20 %} <h3>{{ moment.headline }}</h3> {% else %} <h3>{{ moment.headline }}</h3> {% endif %}
5.3.2 模板继承
模板的继承功能使得网站前端的设计者可以将多个页面的的公用部分编写在一个模板文件中,然后在其他的模板文件中继承,共享这部分内容。所以模板文件可以分为两种类型。
- 父模板文件:保存功用部分的内容,同时制定页面的整体结构。父模板文件一般包括页面头、导航栏、页脚、ICP声明等。
- 子模板文件:用于实现具体的页面展示,实现自身特有内容。
5.3.3 过滤器
过滤器在模板中是放在变量后面用于对变量显示的结果进行控制的技术,变量和过滤器之间用管道符号进行连接。具体的过滤器如下图所示。
过滤器 | 说明 |
|
|
|
|