Django框架(二)
一、类视图
1.类视图的介绍
以函数的方式定义的视图称为函数视图,函数视图便于理解。但是如果有一个视图对应的路径提供了多种不同的HTTP请求方式的支持时,便需要在一个函数中编写不同的业务逻辑,代码可读性和复用性都不是很好。
def register(request):
"""处理注册功能"""
if request.method =='GET':
return HttpResponse("这是实现GET方式的注册")
else:
return HttpResponse("这是实现POST方式的注册")
在Django中也可以使用类来定义一个视图,称为类视图
使用类视图可以将视图对一个的不同的请求方式以类中的不同方法来区别定义。
from django.views.generic import View
class RegisterView(View):
"""类视图:处理注册功能"""
def get(self,request):
"""处理get方式请求"""
return HttpResponse("这里是GET方式的注册")
def post(self,request):
"""处理POST方式请求"""
return HttpResponse("这里是POST方式的注册")
类视图的好处:
- 代码可读性好
- 使用类视图相对于函数视图有更高的复用性,如果其他地方需要用到某个类属兔的某个特定逻辑,可以直接继承该类视图即可。
2.类视图的使用
定义类视图需要继承来自Django模块的父类View,可以使用from django.views.generic import View
导入,定义方式就是上面所示的代码
配置路由的时候,使用类视图的 as_view() 方法来添加
urlpatterns = [
# 视图函数:注册
# url(r'^register/$', views.register, name='register'),
# 类视图:注册
url(r'^register/$', views.RegisterView.as_view(), name='register'),
]
3.类视图的底层原理
@classonlymethod
def as_view(cls, **initkwargs):
"""
Main entry point for a request-response process.
"""
...省略代码...
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
# 调用dispatch方法,按照不同请求方式调用不同请求方法
return self.dispatch(request, *args, **kwargs)
...省略代码...
# 返回真正的函数视图
return view
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
说明:其实就是通过请求方式来调用类视图中的不同方法
先实例化对象,然后通过if判断是哪种请求方式,然后使用gettar(“对象”,字符串)来调用不同方法
4.类视图使用装饰器的方式
为类视图添加装饰器,可以使用两种方法
为了理解,我们先定义一个为视图函数准备的装饰器(在设计装饰器的时候基本都以函数视图作为考虑的被装饰对象),及一个要被装饰的类视图
def my_decorator(func):
def wrapper(request, *args, **kwargs):
print('自定义装饰器被调用了')
print('请求路径%s' % request.path)
return func(request, *args, **kwargs)
return wrapper
class DemoView(View):
def get(self, request):
print('get方法')
return HttpResponse('ok')
def post(self, request):
print('post方法')
return HttpResponse('ok')
第一种方式:在URL配置中装饰
urlpatterns = [
url(r'^demo/$',my_decorator(DemoView.as_view()))
]
此种方式最简单,但因装饰行为被放置到了url配置中,单看视图的时候无法知道此视图被添加了装饰器,不利于代码的完整性和可阅读行,不建议使用
此种方式会为类视图中的所有请求方法都加上装饰行为(因为是在视图入口出,发出请求方式之前)
第二种方式:在类视图中装饰
在类视图中使用函数视图准备的装饰器的时,不能直接添加装饰器,需要使用method_decorator将其转换为适用于类视图方法的装饰器。
method_decorator装饰器使用name参数指明被装饰的方法
# 为全部请求方法加装饰器
@method_decorator(my_decorator, name = "dispatch")
class DemoView(View):
def get(self, request):
print('get方法')
return HttpResponse('ok')
def post(self, request):
print('post方法')
return HttpResponse('ok')
# 为特定请求方法添加装饰器
@method_decorator(my_decorator, name = 'get')
class DemoView(View):
def get(self, request):
print('get方法')
return HttpResponse('ok')
def post(self, request):
print('post方法')
return HttpResponse('ok')
如果需要为类视图的多个方法添加装饰器,但又不是所有的方法(为所有方法添加装饰器参考上面例子),可以直接在需要添加装饰器的方法上使用method_decorator,如下所示
from django.utils.decorators import method_decoator
# 为特定请求方法添加装饰器
class DemoView(View):
@method_decorator(my_decorator) # 为get方法添加了装饰器
def get(self, request):
print('get方法')
return HttpResponse('ok')
@method_decorator(my_decorator) # 为post方法添加了装饰器
def post(self, request):
print('post方法')
return HttpResponse('ok')
def put(self, request): # 没有为put方法添加装饰器
print('put方法')
return HttpResponse('ok')
5.类视图Mixin扩展类
使用面向对象多继承的特性,可以通过定义父类(作为扩展类),在父类中定义想要向类视图中补充的方法,类视图继承这些扩展父类,便可实现代码复用。
定义的扩展父类名称通常以Mixin结尾
例:
class MyDecoratorMixin(object):
@classmthod
def as_view(cls, *args, **kwargs):
view = super().as_views(*args,**kwargs)
view = my_decorator(view)
return view
class DemoView(MyDecoratorMixin,View):
def get(self,reuqest):
print("get方法!!!")
return HttpResponse("这是GET方法奥")
def post(self,request):
print("POST方法!!!")
return HttpResponse("这是POST方法奥")
class ListModelMixin(object):
"""
list扩展类
"""
def list(self, request, *args, **kwargs):
...
class CreateModelMixin(object):
"""
create扩展类
"""
def create(self, request, *args, **kwargs):
...
class BooksView(CreateModelMixin, ListModelMixin, View):
"""
同时继承两个扩展类,复用list和create方法
"""
def get(self, request):
self.list(request)
...
def post(self, request):
self.create(request)
...
二、中间件
1.中间件的介绍
Django中的中间件是一个轻量级、底层的插件系统,可以介入Django的请求和响应处理过程,修改Django的输入和输出。中间件的设计是为开发者提供了一种无侵入式的开发方式,增强了Django的健壮性。
我们可以使用中间件,在Django处理视图的不同阶段对输入和输出进行干预。
2.中间件的定义方法
定义一个中间件工厂函数,然后返回一个可以被调用的中间件。
中间件工厂函数需要接收一个和可以调用的get_response对象。
返回的中间件也是一个可以被调用的秀爱那个,并且像视图一样需要接收一个request对象参数,返回一个response对象。
def simple_middleware(get_response)
# 此处编写的代码仅在Django第一次配置和初始化的使用执行 一 次
def middleware(request):
# 此处编写的代码会在每个请求处理视图之前被调用
response = get_respnse(request)
# 此处编写的代码会在每个请求处理视图之后被调用
return response
return middleware
# 其实它就是一个装饰器
定义好中间件后,需要在settings.py文件中添加注册中间件
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'users.middleware.my_middleware', # 添加中间件
]
定义一个视图进行测试
def demo_view(request):
print('view 视图被调用')
return HttpResponse('OK')
运行结果:
注意:Django运行在调试模式下,中间的init部分有可能被调用两次
3.多个中间件的执行顺序
- 在请求视图被处理之前,中间件由上至下依次执行
- 在请求视图被处理之后,中间件由下至上依次执行
- 而初始化时候只执行一次的内容,会先从下至上依次执行
三、Django自带模板的使用
1.配置
在项目工程中创建模板目录templates
在setting.py配置文件中修改TEMPLATES配置项的DIRS值:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')], # 此处修改为找的到这个模板文件的路径
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
2.定义模板
在templates目录中新建一个模板文件,如index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>{{ city }}</h1>
</body>
</html>
3.模板渲染
Django提供了一个函数render实现模板渲染
使用格式:render(request对象,模板文件路径,模板数据字典)
from django.shortcuts import render
def index(request):
context = {'city':'阜新'}
return render(request,'index.html',context)
4.模板语法
-
1.模板变量
变量名必须由字母、数字、点和下划线(不能以下划线开头)组成。
语法如下:
{{变量}}
模板变量可以使用python的内建类型,也可以是对象。
视图函数内容:
def index(request):
context = {
'city': '北京',
'adict': {
'name': '西游记',
'author': '吴承恩'
},
'alist': [1, 2, 3, 4, 5]
}
return render(request, 'index.html', context)
模板文件的内容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>{{ city }}</h1>
<h1>{{ adict }}</h1>
<h1>{{ adict.name }}</h1> 注意字典的取值方法
<h1>{{ alist }}</h1>
<h1>{{ alist.0 }}</h1> 注意列表的取值方法
</body>
</html>
- 2.模板语句
1)for循环:
{% for item in 列表 %}
循环逻辑
{{forloop.counter}}表示当前是第几次循环,从1开始
{%empty%} 列表为空或不存在时执行此逻辑
{% endfor %}
2)if条件:
{% if ... %}
逻辑1
{% elif ... %}
逻辑2
{% else %}
逻辑3
{% endif %}
比较运算符:
==
!=
<
>
<=
>=
布尔运算符如下:
and
or
not
注意:运算符左右两侧不能紧挨变量或常量,必须有空格。
{% if a == 1 %} # 正确
{% if a==1 %} # 错误
与python中的语法格式几乎相同。
5.过滤器
语法如下:
1)是管道符号 | 来应用过滤器,用于计算、转换操作,可以使用在变量、标签中。
2)如果过滤器需要参数,则可以使用冒号:传递参数
变量|过滤器:参数
列举自带过滤器几个如下:
-
safe,禁用转义,告诉模板这个变量是安全的,可以解释执行
-
length,长度,返回字符串包含字符的个数,或列表、元组、字典的元素个数。
-
default,默认值,如果变量不存在时则返回默认值。
data|default:'默认值'
-
date,日期,用于对日期类型的值进行字符串格式化,常用的格式化字符如下:
- Y表示年,格式为4位,y表示两位的年。
- m表示月,格式为01,02,12等。
- d表示日, 格式为01,02等。
- j表示日,格式为1,2等。
- H表示时,24进制,h表示12进制的时。
- i表示分,为0-59。
- s表示秒,为0-59。
value|date:"Y年m月j日 H时i分s
如果觉得template提供的内置过滤器不够用,不够灵活,满组不了要求,就可以自己定义一个过滤器
-
1.在自己的app里建一个templatetags包,在包里面创建一个后面要在HTML文件中引用的py文件。注意:这个包名不可以更改
-
2.在py文件中,先导入from django import template
实例化对象 register = template.Library() 创建一个template能认识的函数 然后对于创建的每一个过滤器,都需要加上装饰器
from django import template resgister = template.Librart() @register.filter def odd(x): return x * 100
-
3.在HTML文件中引用
{% load oddfilter %} # 先导入过滤器,这个名字就是之前在templatetags包下面创建的py文件名
- 注意点: templatetags包 要在各自的应用内创建
6.模板继承
模板继承和类的继承的含义是一样的,主要是为了提高代码的复用性,减轻开发人员的工作量
- 父模板
如果发现多个模板中某些内容相同,那就应该把这段内容定义到父模板中
标签:block:用于在父模板中预留区域,留给字模板填充差异性的内容,名字不能相同。为了更好的可读性,建议给endbloack标签协商名字,这个名字与对应的block名字相同。父模板也可以使用上下文中传递过来的数据
{% block 名称 %}
预留区域,可以写内容,也可以不写内容,这里面的内容是属于选择继承的,如果字模板中也继承了这个block则可以重写这个父模板中的内容。而在这个标签外面写的内容,通过文件继承的方式会在子模板中出现
{% endblock 名称 %}
- 子模板
标签extends:继承,写在子模板文件的第一行
{% extends "父模板路径" %}
字幕版不用填充父模板中的所有预留区域,如果子模板没有填充,则使用父模板定义的默认值。
填充父模板中指定名称的预留区域
{% block 名称 %}
实际填充内容
{{ block.super }} 用于获取父模板中的block内容
{% endbolck 名称 %}
7.模板注释
1)单行注释语法如下:
{#...#}
2)多行注释使用comment标签,语法如下:
{% comment %}
...
{% endcomment %}
四、Django中的jinja2模板
由于Django默认模板的引擎功能不齐全,速度曼,所欲我们也可以在Django中使用jinja2,jinja2宣称比Django默认模板引擎速度快10到20倍
1.安装jinja2模块
pip3 install jinja2
2.Django中配置jinja2
- 在项目文件中创建jinja2_env.py文件
from jinja2 import Environment
def environment(**options):
env = Environment(**options)
return env
- 在settings.py文件中配置
TEMPLATES = [
{
'BACKEND': 'django.template.backends.jinja2.Jinja2',#修改1
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS':True,
'OPTIONS':{
'environment': 'jinja2_env.environment',# 修改2
'context_processors':[
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
3.jinja2模板的使用
其实jinja2的模板使用与Django默认模板的使用百分之90都是一样的
1.for循环有差异
2.Django中的empty是判断是否为空的意思,为空则执行,而jinja2中则使用else来进行判断
4.jinja2自定义过滤器
在jinja2_env.py文件中自定义过滤器
from jinja2 import Environment
def environment(**options):
env = Environment(**options)
# 2 将自定义的过滤器添加到环境中
env.filters['my_filter'] = my_filter
return env
# 1 自定义过滤器
def my_filter(x):
return x * 100
五、CSRF
1.CSRF的介绍
- CSRF全拼为Cross Site Request Forgery ,译为驿站请求伪造
- CSRF指攻击者盗用了你的身份,以你的名义发送恶意请求
- 造成的问题:个人财产损失以及财产安全
CSRF攻击示意图
- 客户端访问服务器时没有同服务器做安全验证
防止 CSRF 攻击
步骤
- 在客户端向后端请求界面数据的时候,后端会往响应中的 cookie 中设置 csrf_token 的值
- 在 Form 表单中添加一个隐藏的的字段,值也是 csrf_token
- 在用户点击提交的时候,会带上这两个值向后台发起请求
- 后端接受到请求,会产生以下几件事件:
- 从 cookie中取出 csrf_token
- 从 表单数据中取出来隐藏的 csrf_token 的值
- 进行对比
- 如果比较之后两值一样,那么代表是正常的请求,如果没取到或者比较不一样,代表不是正常的请求,不执行下一步操作
五、数据库
ORM框架
O是object,是类对象的意思,R是relation,是关系的意思,也就是关系数据库中数据表的意思,M是mapping,是映射的意思。在ORM框架中,它帮我们把类和数据库的表进行了一个映射,可以让我们通过类和类对象就能操作它所对应的表格数据.ORM框架还有一个功能,它可以根据我们设计的类自动帮我们生成数据表格,省去了我们自己建表的过程。
django内嵌了ORM框架,不需要直接面向数据库编程,而是定义模型类,通过模型类和对象完成数据库表的增删改查草错。
使用django进行数据库开发的步骤:
- 1.配置数据库连接信息
- 2.在models.py文件中定义模型类
- 3.数据库迁移
- 4.通过类和对象完成数据增删改的操作
ORM的作用
1.数据库的配置
在settings.py文件中保存了数据库的连接配置信息,Django默认初始化使用但是qlite数据库
1)使用MySQL数据库首先要安装包
pip install PyMySQL
2)在Django的工程同名子目录的init.py文件中添加如下语句
from pymysql import install_as_MySQLdb
install_as_MySQLdb
# 作用是让Django的ORM能以mysqldb的方式来调用PyMySQL
3)在Mysql中创建数据库
create database django_demo charset=utf8;
4)修改DATABASES配置信息
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'HOST': '127.0.0.1', # 数据库主机
'PORT': 3306, # 数据库端口
'USER': 'root', # 数据库用户名
'PASSWORD': 'mysql', # 数据库用户密码
'NAME': 'django_demo' # 数据库名字
}
}
2.定义模型类
- 模型类被定义在子应用中的models.py文件中
- 模型类必须继承自Model类,位于包django.db.models中
1)定义
创建应用booktest,在models.py 文件中定义模型类。
from django.db import models
#定义图书模型类BookInfo
class BookInfo(models.Model):
btitle = models.CharField(max_length=20, verbose_name='名称')
bpub_date = models.DateField(verbose_name='发布日期')
bread = models.IntegerField(default=0, verbose_name='阅读量')
bcomment = models.IntegerField(default=0, verbose_name='评论量')
is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')
class Meta:
db_table = 'tb_books' # 指明数据库表名
verbose_name = '图书' # 在admin站点中显示的名称
verbose_name_plural = verbose_name # 显示的复数名称
def __str__(self):
"""定义每个数据对象的显示信息"""
return self.btitle
#定义英雄模型类HeroInfo
class HeroInfo(models.Model):
GENDER_CHOICES = (
(0, 'female'),
(1, 'male')
)
hname = models.CharField(max_length=20, verbose_name='名称')
hgender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0, verbose_name='性别')
hcomment = models.CharField(max_length=200, null=True, verbose_name='描述信息')
hbook = models.ForeignKey(BookInfo, on_delete=models.CASCADE, verbose_name='图书') # 外键
is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')
class Meta:
db_table = 'tb_heros'
verbose_name = '英雄'
verbose_name_plural = verbose_name
def __str__(self):
return self.hname
-
数据库表名
模型类如果未指明表名,Django默认以 小写app应用_小写模型类名 为数据库表名
可以通过如上的Meta类中的db_table来设置表名
-
关于主键
django会为表创建自动增长的主键列,每个模型只能有一个主键列,如果使用选项设置某属性为主键列后,django将不会再创建自动增长的主键列
默认创建的主键列属性为id,可以使用pk代替,就是primary key。
-
属性命名限制
- 不能是pyrthon的保留关键字
- 不允许使用连续的下划线
- 定义属性的时候需要指明字段类型,通过字段类型的参数指明选项,语法如下:
属性 = models.字段类型(选项参数)
-
字段类型
类型 说明 AutoField 自动增长的IntegerField,通常不用指定,不指定时Django会自动创建属性名为id的自动增长属性 BooleanField 布尔字段,值为True或False NullBooleanField 支持Null、True、False三种值 CharField 字符串,参数max_length表示最大字符个数 TextField 大文本字段,一般超过4000个字符时使用 IntegerField 整数 DecimalField 十进制浮点数, 参数max_digits表示总位数, 参数decimal_places表示小数位数 FloatField 浮点数 DateField 日期, 参数auto_now表示每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,默认为False; 参数auto_now_add表示当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为False; 参数auto_now_add和auto_now是相互排斥的,组合将会发生错误 TimeField 时间,参数同DateField DateTimeField 日期时间,参数同DateField FileField 上传文件字段 ImageField 继承于FileField,对上传的内容进行校验,确保是有效的图片 -
选项
选项 说明 null 如果为True,表示允许为空,默认值是False blank 如果为True,则该字段允许为空白,默认值是False db_column 字段的名称,如果未指定,则使用属性的名称 db_index 若值为True, 则在表中会为此字段创建索引,默认值是False default 默认 primary_key 若为True,则该字段会成为模型的主键字段,默认值是False,一般作为AutoField的选项使用 unique 如果为True, 这个字段在表中必须有唯一值,默认值是False related_name 在关联查询中,代替单一对象查找多对象 对象名小写_set(book.heroinfo_set.all() 的写法 auto_now_add 只在数据添加的时候,记录时间 auto_now 数据添加和更新的时候,记录时间 null是数据库范畴的概念,blank是表单验证范畴的
-
外键
在设置外键的时候,需要通过on_delete选项指明主表删除数据的时候,对于外键引用表数据如何处理,在django.db.models中包含了可选常量:
- CASCADE 级联,删除主表数据时连通一起删除外键表中数据
- PROTECT 保护,通过抛出ProtectedError异常,来阻止删除主表中被外键应用的数据
- SET_NULL 设置为NULL,仅在该字段null=True允许为null时可用
- SET_DEFAULT 设置为默认值,仅在该字段设置了默认值时可用
- DO_NOTHING 不做任何操作,如果数据库前置指明级联性,此选项会抛出IntegrityError异常
- SET() 设置为特定值或者调用特定方法,如
from django.conf import settings from django.contrib.auth import get_user_model from django.db import models def get_sentinel_user(): return get_user_model().objects.get_or_create(username='deleted')[0] class MyModel(models.Model): user = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.SET(get_sentinel_user), )
2)数据库迁移
将模型类同步到数据库中。
-
1.生成迁移文件
python manage.py makemigrations;
-
2.同步到数据库
python manage.py migrate;
3)添加book表和hero表的测试数据
insert into tb_books(btitle,bpub_date,bread,bcomment,is_delete) values
('射雕英雄传','1980-5-1',12,34,0),
('天龙八部','1986-7-24',36,40,0),
('笑傲江湖','1995-12-24',20,80,0),
('雪山飞狐','1987-11-11',58,24,0);
insert into tb_heros(hname,hgender,hbook_id,hcomment,is_delete) values
('郭靖',1,1,'降龙十八掌',0),
('黄蓉',0,1,'打狗棍法',0),
('黄药师',1,1,'弹指神通',0),
('欧阳锋',1,1,'蛤蟆功',0),
('梅超风',0,1,'九阴白骨爪',0),
('乔峰',1,2,'降龙十八掌',0),
('段誉',1,2,'六脉神剑',0),
('虚竹',1,2,'天山六阳掌',0),
('王语嫣',0,2,'神仙姐姐',0),
('令狐冲',1,3,'独孤九剑',0),
('任盈盈',0,3,'弹琴',0),
('岳不群',1,3,'华山剑法',0),
('东方不败',0,3,'葵花宝典',0),
('胡斐',1,4,'胡家刀法',0),
('苗若兰',0,4,'黄衣',0),
('程灵素',0,4,'医术',0),
('袁紫衣',0,4,'六合拳',0);
3.演示工具的使用
1.shell工具、
Django的manage工具提供了shell命令,可以帮助我们配置好当前工程的运行环境,以变可以直接在终端中执行测试python语句
通过以下的命令进入shell
python manage.py shell
导入两个模型类
from booktest.models import BookInfo, HeroInfo
2.查看MySQL数据库日志
查看mysql数据库日志可以查看对数据库的操作记录。 mysql日志文件默认没有产生,需要做如下配置:
sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf
把68,69行前面的#去除,然后保存并使用如下命令重启mysql服务。
sudo service mysql restart
使用如下命令打开mysql日志文件。
tail -f /var/log/mysql/mysql.log # 可以实时查看数据库的日志内容
# 如提示需要sudo权限,执行
# sudo tail -f /var/log/mysql/mysql.log
注:因内容较多,数据库的增删改查操作请见本人另一张博客(有详细介绍)
六、查询集QuerySet
1.查询集的介绍
Django的ORM中存在查询集的概念
查询集也称查询结果集,QuerySet,表示从数据库中获取的对象集合
当调用如下过滤器方法时,Django会返回查询集(而不是简单的列表):
- all():返回所有数据。
- filter():返回满足条件的数据。
- exclude():返回满足条件之外的数据。
- order_by():对结果进行排序。
对查询集可以再次调用过滤器进行过滤,如:
BookInfo.objects.filter(bread__gt=30).order_by('bpub_date')
也就意味这查询集可以有0个,1个或者多个过滤器,过滤器基于所给的参数查询限制查询的结果。
从SQL角度来看,查询集与select语句等价,过滤器像是where,limit,orderby子句。
判断一个查询集中是否有数据:
- exists():判断查询集中是否有数据,如果有则返回True,没有则返回False。
2.查询集的两大特性
-
惰性执行
创建查询集不会访问数据库,直到调用数据的时候,才会访问数据库,调用的情况包括迭代,序列化,与if合用
例如,当执行如下语句时,并未进行数据库查询,只是创建了一个查询集qs
qs = BookInfo.objects.all()
继续执行遍历迭代操作后,才真正的进行了数据库的查询
for book in qs:
print(book.btitle)
-
缓存
使用同一个查询集,第一次使用的时候会发生数据库的查询,然后Django会把结果缓存下来,再次使用这个结果集的时候会使用缓存的数据,减少了数据的查询次数。
限制查询集
可以对查询集的结果进行取下标或者切片操作,等同于sql中的limit和offset子句
但是不支持负数索引
对查询集进行切片后会返回一个新的查询集,不会立即执行查询
如果获取一个对象,直接使用[0],等价于[0:1].get(),但是如果没有数据,[0]会引发IndexError异常,[0:1].get()如果没有数据会引发DoesNotEexist异常
示例:获取第1、2项,运行查看。
qs = BookInfo.objects.all()[0:2]
七、管理器Manager
管理器是Django的模型进行数据库操作的接口,Django应用的每个模型类都至少拥有一个管理器。
我们在通过模型类objects属性提供的方法操作数据库的时候,即是在使用一个管理器对象objects。当没有为模型定义管理器的时候,Django会为每一个模型类生成一个名为objects的管理器,它是modesl.Manager类的对象。
- 自定义管理器
我们可以自定义管理器,并且应用到我们的模型类上面。
注意:一旦自定义了管理器,之前的objects就不好使了。
自定义管理器类主要用于两种情况:
1.打开booktest/models.py文件,定义类BookInfoManager
# 图书的管理器
class BookInfoManager(models.Manager):
def all(self):
# 默认的object的all方法会查询所有信息
# 现在我们想只查询未被逻辑删除的所有信息
return super().filter(is_delete = Flase)
2.在模型类BookInfo中定义管理器
class BookInfo(models.Model):
books = BookInfoMnager()
3.使用方法
# 在定义的类视图的查询Get方法中使用,或者在shell命令中使用
BookInfo.boos.all()
在管理器中补充定义新的方法
1.打开booktest/models.py文件,定义方法create
# 图书的管理器
class BookInfoManager(models.Manager):
# 创建模型类,接受参数为属性名
def create_book(self,title,pub_date):
# 创建模型类对象self.model可以获得模型类
book = self.model()
book.title = title
book.bpub_date = pub_date
# 将数据插入表
book.save()
return book
2.为模型类BookInfo定义管理器books
class BookInfo(models.Model):
books = BookInfoManager()
3.调用方法
book = BookInfo.books.create_book("wtt",date(1998,09,01))
关注我,持续更新奥!
一起学习!