Web应用程序的本质:
- 接收并解析HTTP请求,获取具体的请求信息
- 处理本次HTTP请求,即完成本次请求的业务逻辑处理
- 构造并返回处理结果——HTTP响应
Web应用框架就是有助于减轻网页开发时共通性活动的工作负荷,例如许多框架提供数据库访问接口、标准样板以及会话管理等,可提升代码的可再用性。
一、Django介绍
Django 采用了 MVT 的软件设计模式:
- 模型(Model)
- 视图(View)
- 模板(Template)
MTV模式并非Django首创,在其他语言中也有类似的MVC设计模式,甚至可以说django里面的MVT事实上是借鉴了MVC模式衍生出来的。
- M,Model,模型,是用于完成操作数据库的。
- V,View,视图,里面的代码就是用于展示给客户端的页面效果。
- C,Controller,控制器,是一个类或者函数,里面的代码就是用于项目功能逻辑的,一般用于调用模型来获取数据,获取到的数据通过调用视图文件返回给客户端。
而Django中的MTV指的是:
- M全拼为Model,与MVC中的M功能相同,负责和数据库交互,进行数据处理。
- V全拼为View,与MVC中的C功能相同,接收请求,进行业务处理,返回应答。
- T全拼为Template,与MVC中的V功能相同,负责封装构造要返回的html。
MTV模型的工作流程
路由控制器将请求转发给对应的视图函数,完成业务逻辑,视图函数将从model中获取的数据嵌入到template的中模板文件(html)渲染成一个页面字符串,返回给客户端的流程。
所以我们学习Django重点是四个部分:url路由器+MVT
二、Django的下载与运行
1.Django的下载
# 下载
pip install django==3.2 -i https://pypi.douban.com/simple # 豆瓣源
# 查看django版本
django-admin --version
2.Django的创建项目及启动运行
# 1.创建名为myproject的django项目;会在命令执行对应目录下创建myproject文件夹
django-admin startproject myproject
# 2.启动运行django;在pycharm的terminal中输入如下命令;ip:port为空时,默认为本机的8000端口
python manage.py runserver ip:port
注意:酷狗音乐会占用8000端口,注意django与酷狗音乐的端口冲突
访问地址:http://127.0.0.1:8090/ 如下,则表示正确安装了
注意:runserver默认启动的wsgi.py文件作为web服务器接口
3.创建应用
# 创建应用
python manage.py startapp 子应用名称
4.基于IDE 创建Django项目
(1).创建项目及应用
在pycharm里:File-->New Project-->Django下
(2).启动项目
Edit Configurations编辑IP、端口后,启动即可
进入配置页面
5.Django完整的目录结构
│─ manage.py # 终端脚本命令,提供了一系列用于生成文件或者目录的命令,也叫脚手架
└─ HelloWorld/ # 主应用开发目录,保存了项目中的所有开发人员编写的代码, 目录是生成项目时指定的
│- asgi.py # django3.0以后新增的,用于让django运行在异步编程模式的一个web应用对象
│- settings.py # 默认开发配置文件
│- urls.py # 路由列表目录,用于绑定视图和url的映射关系
│- wsgi.py # wsgi就是项目运行在wsgi服务器时的入口文件
└- __init__.py
└─ app01 # 子应用
│- models # 该应用的模型类模块
│- views # 该应用的视图模块
│- tests # 该应用的单元测试模块
│- apps # 该应用的一些配置,自动生成
│- admin.py # 该应用的后台管理系统配置
6.案例:返回一个当前时间页面
7.案例:登录
三、路由控制器
Route路由, 是一种映射关系!路由是把客户端请求的url路径和用户请求的应用程序[这里意指django里面的视图进行绑定映射的一种关系。
注意:请求路径和视图函数不是一对一映射关系!
在django运行中,当客户端发送了一个http请求到服务端,服务端的web服务器则会从http协议中提取url地址, 从程序内部找到项目中添加到urlpatterns里面的所有路由信息的url进行遍历匹配。如果相等或者匹配成功,则调用当前url对象的视图方法。
在给urlpatterns路由列表添加路由的过程中,django一共提供了2个函数给开发者注册路由.
from django.urls import path # 字符串路由
from django.urls import re_path # 正则路由,会把url地址看成一个正则模式与客户端的请求url地址进行正则匹配
# path和re_path 使用参数一致.仅仅在url参数和接收参数时写法不一样
1.基本使用
path('login/', login), # path路由默认会添加^$,该规则等同于path('^login/$', login),
re_path('article/(\d{4})/(\d+)', article_detail), # 正则匹配的两个参数以位置参数的形式传入article_detail函数
re_path(r'article/(?P<y>\d{4})/(?P<m>\d+)', article_achive), # 正则匹配的两个参数分别以y,m关键字传参
2.路由分发
from django.urls import include
# 路由分发
path('app01/', include('app01.urls'))
主应用下的urls.py总路由中添加如上的路由分发;在子应用的urls.py路由中像步骤1一样添加即可;
3.路由转发器
4.反向解析
四、视图
django的视图主要有2种,分别是函数视图(FBV)和类视图(CBV).
1.请求方式
2.请求对象
django将请求报文中的请求行、首部信息、内容主体封装成 HttpRequest 类中的属性。 除了特殊说明的之外,其他均为只读的。
1.请求方式
print(request.method)
2.请求数据
print(request.GET) # 一个类似于字典的QueryDict对象,包含 HTTP GET 的所有参数。
print(request.POST) #一个类似于字典的QueryDict对象,包含请求中的表单数据。post请求参数以urlencoded格式发送时,通过该方式读取比较方便
print(request.body) # 一个字符串,代表请求报文的请求体的原数据。
注意:request.GET和request.POST返回的dict键值对中,值为一个时,request.GET.get(‘key’)或request.POST.get(‘key’)获取到值;当值为多个时,获取到值中的最后一个;也可以使用request.GET.getlist(‘key’)或request.POST.getlist(‘key’)获取值列表list;
3.请求路径
print(request.path) #表示请求的路径组件,不包含get参数
print(request.get_full_path()) #包含get参数路径
4.请求头
print(request.META) #一个标准的Python 字典,包含所有的HTTP 首部。具体的头部信息取决于客户端和服务器
3.响应对象
响应对象主要有三种形式:
- HttpResponse()
- render()
- redirect()
1.HttpResponse()
Django服务器接收到客户端发送过来的请求后,会将提交上来的这些数据封装成一个 HttpRequest 对象传给视图函数。那么视图函数在处理完相关的逻辑后,也需要返回一个响应给浏览器。而这个响应,我们必须返回 HttpResponseBase 或者他的子类的对象。而 HttpResponse 则是 HttpResponseBase 用得最多的子类。
常用属性:
- content:返回的内容。
- status:返回的HTTP响应状态码。
- content_type:返回的数据的MIME类型,默认为 text/html 。浏览器会根据这个属性,来显示数据。如果是 text/html ,那么就会解析这个字符串,如果 text/plain ,那么就会显示一个纯文本。
- 设置响应头: response[‘X-Access-Token’] = ‘xxxx’ 。
JsonResponse类:
用来将对象dump成json字符串,然后返回将 json 字符串封装成 Response 对象返回给浏览器。并且他的 Content-Type 是 application/json 。示例代码如下:
from django.http import JsonResponse
def index(request):
return JsonResponse({"title":"三国演义","price":199})
默认情况下 JsonResponse 只能对字典进行 dump ,如果想要对非字典的数据进行 dump ,那么需要给 JsonResponse 传递一个 safe=False 参数。
2.render()
render方法就是将一个模板页面中的模板语法进行渲染,最终渲染成一个html页面作为响应体。
render(request, template_name[, context])
#结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的 HttpResponse 对象。
参数:
/*
request: 用于生成响应的请求对象。
template_name:要使用的模板的完整名称,可选的参数
context:添加到模板上下文的一个字典,
默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。
*/
3.redirect()
参数可以是:
- 一个绝对的或相对的URL, 将原封不动的作为重定向的位置.
- 一个url的别名: 可以使用reverse来反向解析url
def my_view(request):
...
return redirect("/some/url/") # 传递要重定向到的一个具体的网址
return redirect("http://www.baidu.com") # 或重定向到一个完整网址
传递一个视图的名称:
def my_view(request):
...
return redirect(reverse("url的别名"))
APPEND_SLASH的实现就是基于redirect
4.登录验证案例
五、模板语法
模板引擎是一种可以让开发者把服务端数据填充到html网页中完成渲染效果的技术。它实现了把前端代码和服务端代码分离的作用,让项目中的业务逻辑代码和数据表现代码分离,让前端开发者和服务端开发者可以更好的完成协同开发。
- 静态网页:页面上的数据都是写死的,万年不变
- 动态网页:页面上的数据是从后端动态获取的(比如后端获取当前时间;后端获取数据库数据然后传递给前端页面)
Django框架中内置了web开发领域非常出名的一个DjangoTemplate模板引擎(DTL)。
要在django框架中使用模板引擎把视图中的数据更好的展示给客户端,需要完成3个步骤:
- 在项目配置文件中指定保存模板文件的模板目录。一般模板目录都是设置在项目根目录或者主应用目录下。
- 在视图中基于django提供的渲染函数绑定模板文件和需要展示的数据变量
- 在模板目录下创建对应的模板文件,并根据模板引擎内置的模板语法,填写输出视图传递过来的数据。
配置模板目录:在当前项目根目录下创建了模板目录templates. 然后在settings.py, 模板相关配置,找到TEMPLATES配置项,填写DIRS设置模板目录。
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [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',
],
},
},
]
1.简单案例
2.render函数内部本质
3.模板语法
1.变量渲染之深度查询
视图函数传给DTL模板,列表list或字典dict的变量时,通过点.实现深度查询;
<p>列表成员</p>
<p>{{ lve }}</p>
<p>{{ lve.0 }}</p>
<p>{{ lve | last }}</p>
<p>字典成员</p>
<p>id={{ bookinfo.id }}</p>
<p>price={{ bookinfo.price }}</p>
<p>name={{ bookinfo.name }}</p>
2.变量渲染之内置过滤器
语法:
{{obj|过滤器名称:过滤器参数}}
内置过滤器:
过滤器 | 用法 | 代码 |
---|---|---|
last | 获取列表/元组的最后一个成员 | {{liast | last}} |
first | 获取列表/元组的第一个成员 | {{list|first}} |
length | 获取数据的长度 | {{list | length}} |
defualt | 当变量没有值的情况下, 系统输出默认值, | {{str|default=“默认值”}} |
safe | 让系统不要对内容中的html代码进行实体转义 | {{htmlcontent| safe}} |
upper | 字母转换成大写 | {{str | upper}} |
lower | 字母转换成小写 | {{str | lower}} |
title | 每个单词首字母转换成大写 | {{str | title}} |
date | 日期时间格式转换 | {{ value| date:"D d M Y" }} |
cut | 从内容中截取掉同样字符的内容 | {{content | cut:“hello”}} |
list | 把内容转换成列表格式 | {{content | list}} |
add | 加法 | {{num| add}} |
filesizeformat | 把文件大小的数值转换成单位表示 | {{filesize | filesizeformat}} |
join | 按指定字符拼接内容 | {{list| join("-")}} |
random | 随机提取某个成员 | {list | random}} |
slice | 按切片提取成员 | {{list | slice:":-2"}} |
truncatechars | 按字符长度截取内容 | {{content | truncatechars:30}} |
truncatewords | 按单词长度截取内容 | 同上 |
过滤器的使用:
<p>当前书籍: {{ books|default:'没有符合条件的书籍' }}</p>
<p>文件大小: {{ fileSize|filesizeformat }}</p>
<p>出生日期: {{ birthday|date:'Y-m-d H:i:s' }}</p>
<p>字符串截取: {{ s|truncatechars:8 }}</p>
<p>字符串转义: {{ content|safe }}</p>
<p>求值计算: {{ num|add:20 }}</p>
3.自定义过滤器
4.标签
1.if标签
{% if num == 100 %}
<p>num的值为100</p>
{% elif num > 100 %}
<p>num的值大于100</p>
{% else %}
<p>num小于100</p>
{% endif %}
2.for标签
{% for i in books %}
<p>书名:{{ i.name }} 价格:{{ i.price }}</p>
{% endfor %}
循环中, 模板引擎提供的forloop对象,用于给开发者获取循环次数或者判断循环过程的.
属性 | 描述 |
---|---|
forloop.counter | 显示循环的次数,从1开始 |
forloop.counter0 | 显示循环的次数,从0开始 |
forloop.revcounter0 | 倒数显示循环的次数,从0开始 |
forloop.revcounter | 倒数显示循环的次数,从1开始 |
forloop.first | 判断如果本次是循环的第一次,则结果为True |
forloop.last | 判断如果本次是循环的最后一次,则结果为True |
forloop.parentloop | 在嵌套循环中,指向当前循环的上级循环 |
5.模板嵌套继承
传统的模板分离技术,依靠{% include “模板文件名”%}实现,这种方式,虽然达到了页面代码复用的效果,但是由此也会带来大量的碎片化模板,导致维护模板的成本上升.因此, Django框架中除了提供这种模板分离技术以外,还并行的提供了 模板继承给开发者.
{% include "模板文件名"%} # 模板嵌入
{% extends "base.html" %} # 模板继承
1.继承父模板的公共内容
{% extends 'app02/base.html' %} # 继承base.html父模板文件
2.个性展示不同于父模板的内容
在父模板中添加block,如下:block名称为block_name,和默认内容;
{% block block_name %} #父模板base.html中名为block_name的内容;
block_default_content
{% endblock %}
在子模板temp.html文件中,添加如下:
{% extends 'app02/base.html' %} # 继承父模板base.html的内容
{% block block_name %} #重写父模板base.html中block_name的内容,若没有重写,则展示父模板的内容
{{ block.super }} # 展示父模板base.html的block_name的内容
temp
{% endblock %}
- 如果你在模版中使用
{% extends %}
标签,它必须是模版中的第一个标签。其他的任何情况下,模版继承都将无法工作。 - 在base模版中设置越多的
{% block %}
标签越好。请记住,子模版不必定义全部父模版中的blocks,所以,你可以在大多数blocks中填充合理的默认内容,然后,只定义你需要的那一个。多一点钩子总比少一点好。 - 为了更好的可读性,你也可以给你的
{% endblock %}
标签一个 名字 。例如:{``%
block content``%``}``...``{``%
endblock content``%``},
在大型模版中,这个方法帮你清楚的看到哪一个{% block %}
标签被关闭了。 - 不能在一个模版中定义多个相同名字的
block
标签。
4.静态文件
六、模型层(ORM)
Django中内嵌了ORM框架,不需要直接编写SQL语句进行数据库操作,而是通过定义模型类,操作模型类来完成对数据库中表的增删改查和创建等操作。
- O是object,也就类对象的意思。
- R是relation,翻译成中文是关系,也就是关系数据库中数据表的意思。
- M是mapping,是映射的意思。
映射
- 类:sql语句table表
- 类成员变量:table表中的字段、类型和约束
- 类对象:sql表的表记录
ORM的优点
-
数据模型类都在一个地方定义,更容易更新和维护,也利于重用代码。
-
ORM 有现成的工具,很多功能都可以自动完成,比如数据消除、预处理、事务等等。
-
它迫使你使用 MVC 架构,ORM 就是天然的 Model,最终使代码更清晰。
-
基于 ORM 的业务代码比较简单,代码量少,语义性好,容易理解。
-
新手对于复杂业务容易写出性能不佳的 SQL,有了ORM不必编写复杂的SQL语句, 只需要通过操作模型对象即可同步修改数据表中的数据.
-
开发中应用ORM将来如果要切换数据库.只需要切换ORM底层对接数据库的驱动【修改配置文件的连接地址即可】
ORM的缺点
- ORM 库不是轻量级工具,需要花很多精力学习和设置,甚至不同的框架,会存在不同操作的ORM。
- 对于复杂的业务查询,ORM表达起来比原生的SQL要更加困难和复杂。
- ORM操作数据库的性能要比使用原生的SQL差。
- ORM 抽象掉了数据库层,开发者无法了解底层的数据库操作,也无法定制一些特殊的 SQL。【自己使用pymysql另外操作即可,用了ORM并不表示当前项目不能使用别的数据库操作工具了。】
1.配置数据库连接
在settings.py中保存了数据库的连接配置信息,Django默认初始配置使用sqlite数据库。
1.安装pymysql
2.在Django的工程同名子目录的__init__
.py文件中添加如下语句
from pymysql import install_as_MySQLdb
install_as_MySQLdb() # 让pymysql以MySQLDB的运行模式和Django的ORM对接运行
作用是让Django的ORM能以mysqldb的方式来调用PyMySQL。
3.修改DATABASES配置信息
在Django同名子目录下settings.py文件中修改DATABASE配置信息
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'HOST': '127.0.0.1', # 数据库主机
'PORT': 3306, # 数据库端口
'USER': 'root', # 数据库用户名
'PASSWORD': '123', # 数据库用户密码
'NAME': 'student' # 数据库名字
}
}
4.在mysql中创建数据库
create database student; # mysql8.0默认就是utf8mb4;
create database student default charset=utf8mb4; # mysql8.0之前的版本
5.注意3: 如果想打印orm转换过程中的sql,需要在settings中进行如下配置:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
2.定义模型类
- 模型类被定义在"子应用/models.py"文件中。
- 模型类必须直接或者间接继承自django.db.models.Model类。
在models.py中,定义模型类
from django.db import models
from datetime import datetime
# 模型类必须要直接或者间接继承于 models.Model
class BaseModel(models.Model):
"""公共模型[公共方法和公共字段]"""
# created_time = models.IntegerField(default=0, verbose_name="创建时间")
created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
# auto_now_add 当数据添加时设置当前时间为默认值
# auto_now= 当数据添加/更新时, 设置当前时间为默认值
updated_time = models.DateTimeField(auto_now=True)
class Meta(object):
abstract = True # 设置当前模型为抽象模型, 当系统运行时, 不会认为这是一个数据表对应的模型.
class Student(BaseModel):
"""Student模型类"""
#1. 字段[数据库表字段对应]
SEX_CHOICES = (
(0,"女"),
(1,"男"),
(2,"保密"),
)
# 字段名 = models.数据类型(约束选项1,约束选项2, verbose_name="注释")
# SQL: id bigint primary_key auto_increment not null comment="主键",
# id = models.AutoField(primary_key=True, null=False, verbose_name="主键") # django会自动在创建数据表的时候生成id主键/还设置了一个调用别名 pk
# SQL: name varchar(20) not null comment="姓名"
# SQL: key(name),
name = models.CharField(max_length=20, db_index=True, verbose_name="姓名" )
# SQL: age smallint not null comment="年龄"
age = models.SmallIntegerField(verbose_name="年龄")
# SQL: sex tinyint not null comment="性别"
# sex = models.BooleanField(verbose_name="性别")
sex = models.SmallIntegerField(choices=SEX_CHOICES, default=2)
# SQL: class varchar(5) not null comment="班级"
# SQL: key(class)
classmate = models.CharField(db_column="class", max_length=5, db_index=True, verbose_name="班级")
# SQL: description longtext default "" not null comment="个性签名"
description = models.TextField(default="", verbose_name="个性签名")
#2. 数据表结构信息
class Meta:
db_table = 'tb_student' # 指明数据库表名,如果没有指定表明,则默认为子应用目录名_模型名称,例如: users_student
verbose_name = '学生信息表' # 在admin站点中显示的名称
verbose_name_plural = verbose_name # 显示的复数名称
#3. 自定义数据库操作方法
def __str__(self):
"""定义每个数据对象的显示信息"""
return "<User %s>" % self.name
1.数据库表名
模型类如果未指明表名db_table,Django默认以 小写app应用名_小写模型类名 为数据库表名。
可通过db_table 指明数据库表名。
2.关于主键
django会为表创建自动增长的主键列id,每个模型只能有一个主键列。
如果使用选项设置某个字段的约束属性为主键列(primary_key)后,django不会再创建自动增长的主键列。
3.属性命名机制
- 不能是python的保留关键字。
- 不允许使用连续的2个下划线,这是由django的查询方式决定的。__ 是关键字来的,不能使用!!!
- 定义属性时需要指定字段类型,通过字段类型的参数指定选项,语法如下:
属性名 = models.字段类型(约束选项, verbose_name="注释")
4.字段类型
类型 | 说明 |
---|---|
AutoField | 自动增长的IntegerField,通常不用指定,不指定时Django会自动创建属性名为id的自动增长属性 |
BooleanField | 布尔字段,值为True或False |
NullBooleanField | 支持Null、True、False三种值 |
CharField | 字符串,参数max_length表示最大字符个数,对应mysql中的varchar |
TextField | 大文本字段,一般大段文本(超过4000个字符)才使用。 |
IntegerField | 整数 |
DecimalField | 十进制浮点数, 参数max_digits表示总位数, 参数decimal_places表示小数位数,常用于表示分数和价格 Decimal(max_digits=7, decimal_places=2) ==> 99999.99~ 0.00 |
FloatField | 浮点数 |
DateField | 日期参数auto_now表示每次保存对象时,自动设置该字段为当前时间。参数auto_now_add表示当对象第一次被创建时自动设置当前。参数auto_now_add和auto_now是相互排斥的,一起使用会发生错误。 |
TimeField | 时间,参数同DateField |
DateTimeField | 日期时间,参数同DateField |
FileField | 上传文件字段,django在文件字段中内置了文件上传保存类, django可以通过模型的字段存储自动保存上传文件, 但是, 在数据库中本质上保存的仅仅是文件在项目中的存储路径!! |
ImageField | 继承于FileField,对上传的内容进行校验,确保是有效的图片 |
5.约束选项
选项 | 说明 |
---|---|
null | 如果为True,表示允许为空,默认值是False。相当于python的None |
blank | 如果为True,则该字段允许为空白,默认值是False。 相当于python的空字符串,“” |
db_column | 字段的名称,如果未指定,则使用属性的名称。 |
db_index | 若值为True, 则在表中会为此字段创建索引,默认值是False。 相当于SQL语句中的key |
default | 默认值,当不填写数据时,使用该选项的值作为数据的默认值。 |
primary_key | 如果为True,则该字段会成为模型的主键,默认值是False,一般不用设置,系统默认设置。 |
unique | 如果为True,则该字段在表中必须有唯一值,默认值是False。相当于SQL语句中的unique |
注意:null是数据库范畴的概念,blank是表单验证范畴的
6.外键
3.数据迁移
将模型类定义表架构的代码转换成SQL同步到数据库中,这个过程就是数据迁移。django中的数据迁移,就是一个类,这个类提供了一系列的终端命令,帮我们完成数据迁移的工作。
1.生成迁移文件
所谓的迁移文件, 是类似模型类的迁移类,主要是描述了数据表结构的类文件.
python manage.py makemigrations
2.同步到数据库中
python manage.py migrate
补充:在django内部提供了一系列的功能,这些功能也会使用到数据库,所以在项目搭建以后第一次数据迁移的时候,会看到django项目中其他的数据表被创建了。其中就有一个django内置的admin站点管理。
# admin站点默认是开启状态的,我们可以通过http://127.0.0.1:8000/admin
# 这个站点必须有个管理员账号登录,所以我们可以在第一次数据迁移,有了数据表以后,就可以通过以下终端命令来创建一个超级管理员账号。
python manage.py createsuperuser
4.数据库基本操作
1.添加记录
# 方式1:save方法
# 通过创建模型类对象,执行对象的save()方法保存到数据库中。
student = Student(name='张三', age=12, classmate='s12')
student.save() # 调用save方法会转换成sql的insert语句
print(student.id)
# 方式2(推荐)
# 通过模型类.objects.create()保存,返回生成的模型类对象。
student = Student.objects.create(name='李四', age=14, classmate='s22')
print(student.id)
2.基础查询
ORM中针对查询结果的限制,提供了一个查询集[QuerySet].这个QuerySet,是ORM中针对查询结果进行保存数据的一个类型,我们可以通过了解这个QuerySet进行使用,达到查询优化,或者限制查询结果数量的作用。
1.all()
查询所有对象,返回queryset对象。查询集,也称查询结果集、QuerySet,表示从数据库中获取的对象集合。
students = Student.objects.all()
print("students:",students)
2.filter()
筛选条件相匹配的对象,返回queryset对象。
# 查询所有的女生
students = Student.objects.filter(sex=0)
print(students)
3.get()
返回与所给筛选条件相匹配的对象,返回结果有且只有一个, 如果符合筛选条件的对象超过一个或者没有都会抛出错误。
student = Student.objects.get(pk=1)
print(student)
print(student.description)
get使用过程中的注意点:get是根据条件返回多个结果或者没有结果,都会报错
try:
student = Student.objects.get(name="刘德华")
print(student)
print(student.description)
except Student.MultipleObjectsReturned:
print("查询得到多个结果!")
except Student.DoesNotExist:
print("查询结果不存在!")
4.first()、last()
分别为查询集的第一条和最后一条记录;
# 没有结果返回none,如果有多个结果,则返回模型对象
students = Student.objects.all()
# print(students.name)
print(students[0].name)
stu01 = Student.objects.first()
stu02 = Student.objects.last()
print(stu01.name)
print(stu02.name)
5.exclude()
筛选条件不匹配的对象,返回queryset对象。
students = Student.objects.exclude(name='张三')
print(students)
6.order_by()
对查询结果排序
# order_by("字段") # 按指定字段正序显示,相当于 asc 从小到大
# order_by("-字段") # 按字段倒序排列,相当于 desc 从大到小
# order_by("第一排序","第二排序",...)
# 查询所有的男学生按年龄从高到低展示
# students = Student.objects.all().order_by("-age","-id")
students = Student.objects.filter(sex=1).order_by("-age", "-id")
print(students)
7.count()
查询集中对象的个数
# 查询所有男生的个数
count = Student.objects.filter(sex=1).count()
print(count)
8.exists()
判断查询集中是否有数据,如果有则返回True,没有则返回False
# 查询Student表中是否存在学生
print(Student.objects.exists())
9.values()、values_list()
value()
把结果集中的模型对象转换成字典,并可以设置转换的字段列表,达到减少内存损耗,提高性能- |
values_list()
: 把结果集中的模型对象转换成列表,并可以设置转换的字段列表(元祖),达到减少内存损耗,提高性能
# values 把查询结果中模型对象转换成字典
student_list = Student.objects.filter(classmate="301")
student_list = student_list.order_by("-age")
student_list = student_list.filter(sex=1)
ret1 = student_list.values() # 默认把所有字段全部转换并返回
ret2 = student_list.values("id","name","age") # 可以通过参数设置要转换的字段并返回
ret3 = student_list.values_list() # 默认把所有字段全部转换并返回
ret4 = student_list.values_list("id","name","age") # 可以通过参数设置要转换的字段并返回
print(ret4)
return JsonResponse({},safe=False)
10.distinct()
从返回结果中剔除重复纪录。返回queryset。
# 查询所有学生出现过的年龄
print(Student.objects.values("age").distinct())
3.模糊查询
(1)模糊查询之contains
说明:如果要包含%无需转义,直接写即可。
Student.objects.filter(name__contains='华')
(2)模糊查询之startswith、endswith
Student.objects.filter(name__endswith='文')
注意:以上运算符都区分大小写,在这些运算符前加上i表示不区分大小写,如iexact、icontains、istartswith、iendswith.
(3)模糊查询之isnull
Student.objects.filter(description__isnull=True)
(4)模糊查询之in
Student.objects.filter(sex__in=[1, 0])
(5)模糊查询之比较查询
- gt 大于 (greater then)
- gte 大于等于 (greater then equal)
- lt 小于 (less then)
- lte 小于等于 (less then equal)
Student.objects.filter(id__gte=4)
(6)模糊查询之range
Student.objects.filter(age__range=(12, 23))
注意:此处的range()是左闭右闭;
(7)模糊查询之日期查询
year、month、day、week_day、hour、minute、second:对日期时间类型的属性进行运算。
Student.objects.filter(birthday__year=2021)
Student.objects.filter(birthday__year__gte=2022)
Student.objects.filter(birthday__week_day=1)
(8)exact
4.进阶查询
1.F查询:用于两个字段之间的比较
from django.db.models import F
student_list = Student.objects.exclude(created_time=F("updated_time"))
print(student_list)
2.Q查询:
多个过滤器逐个调用表示逻辑与关系,同sql语句中where部分的and关键字。
Student.objects.filter(age__gt=20,id__lt=30)
或
Student.filter(age__gt=20).filter(id__lt=30)
如果需要实现逻辑或or的查询,需要使用Q()对象结合|运算符,Q对象被义在django.db.models中。语法如下:
Q(属性名__运算符=值)
Q(属性名__运算符=值) | Q(属性名__运算符=值)
Q对象可以使用&、|连接,&表示逻辑与,|表示逻辑或
Q对象左边可以使用~操作符,表示非not。但是工作中,我们只会使用Q对象进行或者的操作,只有多种嵌套复杂的查询条件才会使用&和~进行与和非得操作
。
3.聚合查询
使用aggregate()过滤器调用聚合函数。聚合函数包括:Avg 平均,Count 数量,Max 最大,Min 最小,Sum 求和,被定义在django.db.models中。例:求学生的平均年龄
from django.db.models import Sum,Count,Avg,Max,Min
Student.objects.aggregate(Avg('age'))
注意:aggregate的返回值是一个字典类型,格式如下:
{'属性名__聚合类小写':值}
使用count时一般不使用aggregate()过滤器。例如:查询学生总数
Student.objects.count() # count函数的返回值是一个数字。
4.分组查询
# select class,avg('age') from tb_student group by class
Student.objects.values('classmate').annotate(Avg('age'))
Student.objects.values('classmate').annotate(v = Avg('age') #重命名返回字段名
# SQL原生语句中分组之后可以使用having过滤,在django中并没有提供having对应的方法,但是可以使用filter对分组结果进行过滤
# 所以filter在annotate之前,表示where,在annotate之后代表having
# 同理,values在annotate之前,代表分组的字段,在annotate之后代表数据查询结果返回的字段
5.原生查询
ret = Student.objects.raw("SELECT id,name,age FROM db_student") # student 可以是任意一个模型
# 这样执行获取的结果无法通过QuerySet进行操作读取,只能循环提取
print(ret,type(ret))
for item in ret:
print(item,type(item))
5.修改记录
1.方式1:模型类对象调用save方法
student = Student.objects.filter(name='刘德华').first()
print(student)
student.age = 19
student.classmate = "303"
# save之所以能提供给我们添加数据的同时,还可以更新数据的原因?
# save会找到模型的字段的主键id的值,
# 主键id的值如果是none,则表示当前数据没有被数据库,所以save会自动变成添加操作
# 主键id有值,则表示当前数据在数据库中已经存在,所以save会自动变成更新数据操作
student.save()
2.方式2:模型类.objects.filter().update();会返回受影响得到行数
# update是全局更新,只要符合更新的条件,则全部更新,因此强烈建议加上条件!!!
student = Student.objects.filter(name="赵华",age=22).update(name="刘芙蓉",sex=True)
print(student)
5.删除记录
1.方式1:删除模型类对象
student = Student.objects.get(id=13)
student.delete()
2.方式2:模型类.object.filter().delete()
Student.objects.filter(id=14).delete()
# 务必写上条件,否则变成了清空表了。Student.objects.filter().delete()