django路由层/视图层/模板层/模型层
django路由层
1.路由匹配
django版本对路由匹配有不同的方式
在1.x版本 路由匹配也就是 url('参数为正则表达式')
在2.x版本path('第一个参数写什么就匹配什么')
无论什么版本的django都自带添加斜杠后缀的功能
我们也可以在项目settings文件中添加配置
'''APPEND_SLASH'''
来取消自动添加斜杠后缀的功能
2.转换器
正常情况下,网站会有很多相似的网址:
但是我们如果每一个网址都开设一个路由去匹配的话
会显得极为不合理!
django2.x及以上版本路由动态匹可以通过'转换器'
str:匹配除路径分隔符外的任何非空字符串。
int:匹配0或者任意正整数。
slug:匹配任意一个由字母或数字组成的字符串。
uuid:匹配格式化后的UUID。
path:能够匹配完整的URL路径
# urls.py文件写法:
# path('路径/<转换器:变量名>',视图函数)
#也支持自定义转换器(自己写正则表达式匹配更加细化的内容)
# 转换器 将对应位置匹配到的数据转换成固定的数据类型
path('index/<str:info>/', views.index_func), #视图函数 index_func(实参request对象,info='转换器匹配到的类型转换之后的内容')
path('index/<str:info>/<int:id>/', views.index_func) #视图函数 index_func(实参request对象,info='转换器匹配到的类型转换之后的内容',id='转换器匹配到的类型转换之后的内容')
3.正则匹配
django2.x版本以上有一个re_path
我们可以通过re_path来编写正则表达式来进行匹配
匹配的本质:
只要第一个正则表达式能够从用户输入的路由中匹配到数据就算匹配成功
会立即停止路由曾其他的匹配
并会直接执行相对应的视图函数
re_path(!'home/$',views.home)
django1.x路由匹配使用的是url() 功能与re_path() 一致!
4.正则匹配的无名有名分组
无名分组
re_path(!'home/$',views.home,views.home)
会将括号内正则匹配到的内容当做位置参数传递给视图函数
有名分组
re_path('^test/(?P<year>\d{4})/', views.test)
会将括号内正则匹配到的内容当做关键字参数传递给视图函数
注意上述的分组不能混合使用!!!
反向解析
通过一个名字可以反向解析出一个结果
该结果可以访问到莫格对应的路由
基本使用
1.路由匹配关系起别名
path('login001/', views.login, name='login_view')
2.反向解析语法
html页面上模板语法 {% url 'login_view' %}
后端语法 reverse('login_view')
动态路由的反向解析
path('func1/<str:others>/', views.func1_func, name='func1_view')
html页面上模板语法 {% url 'func1_view' 'jason' %}
后端语法 reverse('func1_view', args=('嘿嘿嘿',))
django请求生命周期流程图
1.浏览器
发送请求(HTTP协议)
2.web服务网关接口:
1.请求来的时候解析封装,响应走的时候打包处理
2.django默认的wsgiref模块不能承受高并发,只能支持不到1000的并发量
3.在公司中一般会将默认的wsgiref模块更换为并发量更高的uwsgi来增加并发量
4.WSGI是协议,wsgiref,uwsgi是实现该协议的功能模块
3.django后端
1.django中间件
相当于django的保安
2.urls.py 路由层
识别路由并匹配对应的视图函数
3.views.py 视图层
网站整体的业务逻辑
4.templates 目录
模板语法>>>网站所有的html文件
5。models.py 模型层
ORM>>>数据库
4.数据库
ORM>>>模型层
创建django项目:
- 1.win+r 打开 cmd
- 2.进入到你想要创建django项目的目录
- 输入指令创建django项目
django-admin startproject 项目名称
- 在cmd输入 cd djangotest 进入项目目录
- 测试创建是否成功 输入指令
python3.8 manage.py runserver
如图显示即成功
- 访问 http://127.0.0.1:8000/ 地址
显示此界面即成功
- 在cmd中输入指令 创建app
在此之前需退出运行状态,在cmd界面 中 ctrl+c 即可停止运行
python3.8 manage.py startapp user
- 到此结束 我们通过pycharm打开这个djangotest01项目!
至此我们需要做一些准备工作!
找到 与项目目录同名的djangotest01文件中的 配置文件settings - 注册app
- 创建static文件夹(用于放置静态文件)
可以开始放东西进来了~
-
放置文件
-
连接数据库
-
配置数据库
-
修改默认配置
-
models.py中创建表
-
输入 python3.8 manage.py makemigrations 命令
-
输入 python3.8 manage.py migrate 命令
-
在views视图层编写逻辑代码
...省略
路由分发
django 支持每个应用都可与有自己独立的路由层,静态文件、模板层。
基于该特性多人开发项目就可以完全解耦合
多个应用都有很多路由与视图函数的对应关系
在这个时候可以拆分到各自的路由层中
使用路由分发之前,总路由直接写路由与视图函数的匹配
path('index/',index)
使用路由分发之后,总路由只按照应用名来分配匹配方向
path('app01/',include('app01.urls'))
路由分发例子:
1.创建多个应用,并记得去配置文件中注册
# 创建应用
tools
run manage.py task
startapp app01
startapp app02
# 配置应用
INSTALLED_APPS = [
'app01',
'app02'
]
2.在多个应用中编写相同的路由
# app01
urlpatterns = [
url(r'^index/',views.index111)
]
# app02
urlpatterns = [
url(r'^index/',views.index)
]
3.路由分发
# 总路由 urls.py
from django.conf.urls import url, include
from app01 import urls as app01_urls
from app02 import urls as app02_urls
url(r'^app01/',include(app01_urls)),
url(r'^app02/',include(app02_urls))
"""总路由只负责分发 不负责视图函数对应"""
# 上述代码还可以简写
from django.conf.urls import url, include
url(r'^app01/',include('app01.urls')),
url(r'^app02/',include('app02.urls'))
总路由负责把你送到指定的地方。 总路由只按照应用名分配匹配方向。
路由层名称空间
路由分发之后,针对相同别名能否自动反向解析出不同的应用前缀?
在默认情况下是无法直接识别应用前缀的!
如果想要正常识别,那可以通过以下两种方式
方式1:名称空间
总路由
path('appo01/',include(('app01.urls','app01'),namespace='app01')),
path('app01/',include(('app02.urls','app02'),namespace='app02'))
反向解析:
reverse('app01':index_view)
方式2:别名不冲突即可
多个应用别名不冲突即可用应用名作为别名的前缀
path('index/',view.index,name='app01_index_view')
path('index/'view.index,name='app02_index_view')
虚拟环境
项目1
django2.2 pymysql3.3 requests1.1
项目2
django1.1
项目3
flask
问:如何让诸多项目使用不同环境并且无障碍地打开运行?
方式1:下载所有模块,如果有相同模块不同版本每次都重新下载替换 >>> 不合理
方式2:提前准备好多个解释器环境,针对不同的项目切换即可 >>> 虚拟环境
# 创建虚拟环境
相当于在下载一个全新的解释器
new project
pure python
new environment using
选择解释器版本
不勾选 Inherit global site-packages(继承本地解释器环境)
勾选 Make avaiable to all projects(让所有项目使用该环境)
create
# 识别虚拟环境
文件目录中有一个venv文件夹
# 如何切换环境
选择不同的解释器即可,全文不要再次勾选new environment using
视图层三板斧
render、redirec、HttpResponse
只要是用于处理请求的视图函数都必须返回HttpResponse对象
如果不返回HttpResponse对象就会报错!
视图层之JsonResponse对象
使用JsonResponse对象需要先导入
django中的JsonResponse模块相当于对json序列化的数据类型的范围做了扩充,查看源码,其实还是使用的原始的json模块
from django.http import JsonResponse
# JsonResponse源码
class JsonResponse(HttpResponse):
def __init__(self, data, encoder=DjangoJSONEncoder, safe=True,
json_dumps_params=None, **kwargs):
if safe and not isinstance(data, dict):
raise TypeError(
'In order to allow non-dict objects to be serialized set the '
'safe parameter to False.'
)
if json_dumps_params is None:
json_dumps_params = {}
kwargs.setdefault('content_type', 'application/json')
data = json.dumps(data, cls=encoder, **json_dumps_params) # 还是使用json方法
super(JsonResponse, self).__init__(content=data, **kwargs)
代码验证
def ab_json(request):
1.HttpResponse传递字典 >>> 无法json序列化
user_dict = {"name":'jason','pwd':123,'hobby':'好好学习'}
dict_json = json.dumps(user_dict,ensure_ascii=False)
return HttpResponse(dict_json)
2.JsonResponse传递字典
user_dict = {'name': 'xiaoming', 'pwd': 123, 'hobby': '好好学习'}
return JsonResponse(user_dict,json_dumps_params={'ensure_ascii':False})
# 如果不想展示出来的中文乱码,需要设置json_dumps_params
3.JsonResponse传递列表
user_list = [11, 22, 33, 44, 55]
return JsonResponse(user_list, safe=False) # 防止报错,需要设置safe=False
视图层之request对象获取文件
form表单携带的文件类型的数据需要做到以下几点:
1.html页面请求方式 method 必须为Post
'''<form action="" method="post" enctype="multipart/form-data">'''
2.enctype必须为 multipart/form-data
'''<form action="" method="post" enctype="multipart/form-data">
<p>username:
<input type="text" name="username" class="form-control">
</p>
<p>files:
<input type="file" name="my_file" class="form-control" multiple(上传多个文件)>
</p>
<input type="submit" class="btn btn-success btn-block">
</form>'''
django后端则需要通过request.FILES来获取文件类型的数据
def ab_form(request):
if request.method == 'POST':
print(request.POST) # 不能获取文件数据,只能获取文件名字
print(request.FILES) # 专门获取文件数据
file_obj = request.FILES.get('my_file') # 'my_file'是input标签的name值
print(file_obj.name) # 查看文件名
with open(file_obj.name,'wb') as f:
for line in file_obj: # 相当于粘贴文件到当前文件夹
f.write(line)
request的其他方法
request.method
request.POST
request.GET
request.FILES
request.body # 存放的是接收过来的最原始的二进制数据
ps:request.POST、request.GET、request.FILES这些获取数据的方法其实都从body中获取数据并解析存放的;
request.path
获取路径即url
request.path_info
获取路径即url
request.get_full_path()
获取路径(url)并且还可以获取到路径(?...)后面携带的参数
视图层FBV与CBV
FBV
基于函数的视图
CBV
基于类的视图
# 视图层
from django import views
class MyLoginView(views.View):
def get(self, request):
return HttpResponse("from CBV get view")
def post(self, request):
return HttpResponse("from CBV post view")
# 路由层
path('login/', views.MyLoginView.as_view())
"""
如果请求方式是GET,则会自动执行类里面的get方法;
如果请求方式是POST,则会自动执行类里面的post方法;
"""
CBV源码剖析
1.路由层创建关系
path(r'login/', views.MyLoginView.as_view()) # MyLoginView是即将要创建的类名
分析:
as_view可以是普通的静态方法;
as_view可以是绑定给类的方法;
项目启动后,访问此路由,由于函数加括号调用优先级最高,会先执行as_view(),可以看成一个返回值;
2.在视图层中创建类
class MyLoginView(views.View):
def get(self, request):
return HttpResponse("from CBV get view")
def post(self, request):
return HttpResponse("from CBV post view")
# 使用get请求后,结果为:from CBV get view
# 使用post请求后,结果为:from CBV post view
分析:
类MyLoginView并没有相关方法能够识别get和post请求,那么必定在继承的父类中,我们接下来查看View源码
3.查看View源码
class View(object):
# 1、请求方式
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] # 共八种
# 2.as_view函数
@classonlymethod
def as_view(cls, **initkwargs):
... # 跳过部分代码
def view(request, *args, **kwargs):
... # 跳过部分代码
return self.dispatch(request, *args, **kwargs)
return view
def dispatch(self, request, *args, **kwargs):
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)
分析:
运行步骤:
(1)url调用views.MyLoginView.as_view(),先执行as_view;
(2)as_view()的返回值为view,因此:
views.MyLoginView.as_view() == views.MyLoginView.view;
(3)路由匹配成功后执行view,执行类的dispatch方法(self.dispatch())
(4)执行dispatch,判断如果请求方式在八种方式中,获取该请求方式的对应字符(handler)后,并加括号运行该函数:return handler()
因此:
as_view() == view;
view() == dispatch() == handler();
总结:
views.MyLoginView.as_view() == handler(request, *args, **kwargs) # handler相当于不同的请求方式 >>> post,get...
模板层传值语法
"""
django提供的模板语法只有两个符号
{{}}:主要用于变量相关操作(引用)
{%%}:主要用于逻辑相关操作(循环、判断)
"""
1.传值的两种方式
# 传值方式1:指名道姓的传,适用于数据量较少的情况,节省资源
return render(request, 'ab.html', {'name':name})
# 传值方式2:打包传值,适用于数据量较多的情况(偷懒),浪费资源
'''locals() 将当前名称空间中所有的名字全部传递给html页面'''
return render(request, 'ab_temp.html', locals())
2.传值的范围
基本数据类型都可以;
函数名:
模板语法会自动加括号执行并将函数的返回值展示到页面上;
不支持传参(模板语法会自动忽略有参函数);
文件名:
直接显示文件IO对象;
类名:
自动加括号实例化成对象;
对象名:
直接显示对象的地址,并且具备调用属性和方法的能力;
# django模板语法针对容器类型的取值 只有一种方式>>>:句点符
data1 = {'info':{'pro':[11, 22, 33, {'name':'jason','msg':'努力就有收获'}]}};
既可以点key值也可以点索引,django内部会自动识别;
{{ data1.info.pro.3.msg }}
模板层语法-过滤器
{{ 数据对象|过滤器名称:参数 }} 过滤器最多只能额外传输一个参数
eg: <p>统计数据的长度:{{ s1|length }}</p>
过滤器 | 用法 | 代码 |
---|---|---|
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 |
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 |
按单词长度截取内容 | 同上 |
模板语法-标签(类似于流程控制)
语法结构
{% 名字 ...%}
{% end名字 %}
if判断与for循环
<!-- if判断 -->
{% if b %}
<p>你好啊</p>
{% elif s1 %}
<p>他好呀</p>
{% else %}
<p>大家好</p>
{% endif %}
<!-- for循环 -->
{% for i in l1 %}
<p>{{i}}</p>
{% endfor %}
提供了forloop关键字,可以在for循环前提下使用,forloop结果如下:
结果x,{'parentloop': {}, 'counter0': 0, 'counter': 1, 'revcounter': 4, 'revcounter0': 3, 'first': True, 'last': False} <!-- 这是forloop的一条记录 -->
counter0:从零开始计算索引,当前元素的索引值
counter:从1开始计算索引,当前元素的索引值
revcounter:从1开始计算索引,将索引值反过来后当前元素的索引值
revcounter0:从0开始计算索引,将索引值反过来后当前元素的索引值
first:表示当前元素是否是第一个元素
last:表示当前元素是否是最后一个元素
...
<!-- for循环中forloop使用+if判断 -->
{% for i in l1 %}
{% if forloop.first %}
<p>这是第一次循环</p>
{% elif forloop.last %}
<p>这是最后一次循环</p>
{% else %}
<p>中间循环</p>
{% endif %}
{% empty %} <!-- for循环对象为空时执行下面代码 -->
<p>for循环对象为空 自动执行</p>
{% endfor %}
ps:针对字典同样提供了keys、values、items方法
{% for foo in user_info %}
{{ foo }} <!-- 默认取字典k值 -->
{% endfor %}
{% for foo in user_info.keys %}
{{ foo }} <!-- 取字典所有k值 -->
{% endfor %}
{% for foo in user_info.values %}
{{ foo }} <!-- 取字典所有v值 -->
{% endfor %}
{% for foo in user_info.items %}
{{ foo }} <!-- 取字典所有键值对,元组形式 -->
{% endfor %}
自定义过滤器、标签及inclusion_tag
"""
如果想要自定义一些模板语法 需要先完成下列的三步走战略
1.在应用下创建一个名字必须叫templatetags的目录
2.在上述目录下创建任意名称的py文件
3.在上述py文件内先编写两行固定的代码
from django import template
register = template.Library()
"""
# 自定义过滤器(最大只能接收两个参数)
@register.filter(name='myadd')
def func1(a, b):
return a + b
{% load mytags %}
<p>{{ i|myadd:1 }}</p>
# 自定义标签(参数没有限制)
@register.simple_tag(name='mytag')
def func2(a, b, c, d, e):
return f'{a}-{b}-{c}-{d}-{e}'
{% load mytags %}
{% mytag 'jason' 'kevin' 'oscar' 'tony' 'lili' %}
# 自定义inclusion_tag(局部的html代码)
@register.inclusion_tag('menu.html',name='mymenu')
def func3(n):
html = []
for i in range(n):
html.append('<li>第%s页</li>'%i)
return locals()
{% load mytags %}
{% mymenu 20 %}
模板的继承与导入
母板(模板)的继承
多个页面有很多相似的地方 我们可以采取下列方式
方式1:传统的复制粘贴 (代码重复性太高!浪费资源,而且不方便)
方式2:模板的继承
1.在母板中使用block划定子板可以修改的区域
语法:
{% block 区域名称 %}
{% endblock %}
2.子板继承模板
html页面清空
首行编写: { % extend '母版名称.html' % }
{ % block 区域名称% }
子板的内容
{ % end block % }
3.完全体
在母版中 应该至少划分三个区域
1.页面内容区域
2.CSS样式区
3.JS代码区
子板可以重复使用母版的内容
{{block.super}}
模板的导入(了解)
将某个html的部分提前写好 之后很多html页面都想使用就可以导入
{% include 'myform.html' %}
模型层-前期准备
sqlite3数据不敏感
自带的sqlite3数据库对时间字段并不敏感 有时候会展示错乱,所以我们习惯切换成常见的数据库:
比如:MySQL django中的orm并不会自动帮我们创建库,所以需要提前准备好!
2.单独测试django某个功能层
默认不允许单独测试某个py文件,如果想要测试(主要是测试modles.py)
测试方式1:pycharm提供的python console
测试环境2:自己搭建(自带的test或者自己创建)
1.拷贝manage.py前四行
2.自己再加两行
import django
django.setup()
orm底层sql语句
在django中的orm底层还是通过SQL语句与数据库进行沟通,那么请我们是可以查看到这些语句的
我们手中如果有QuerySet对象,那么就可以通过句点符点出Query来查看SQL语句
如果想查看所有orm底层的SQL语句也可以在配置文件添加日志记录、
放置此代码在Django项目的settings.py文件中
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
ORM常用关键字
数据准备
# models.User.objects.create(name='jason',age=18)
# models.User.objects.create(name='tony',age=28)
# models.User.objects.create(name='kevin',age=28)
# models.User.objects.create(name='kerry',age=38)
# models.User.objects.create(name='jack',age=38)
# models.User.objects.create(name='tom',age=28)
# models.User.objects.create(name='oscar',age=18)
1.fifter()筛选条件
res = models.User.objects.all() # 查询所有的数据
结果QuerySet可以看成是列表套对象
res = models.User.objects.filter() # 括号内填写筛选条件,不写相当于all()
结果QuerySet可以看成是列表套对象
res = models.User.objects.filter(pk=1) # 想通过主键筛选数据,可以直接写pk,会自动定位到当前表的主键字段,无需你自己查看具体字段名称
es = models.User.objects.filter(pk=1)[0] # 直接获取数据对象,结果中第一个对象
res = models.User.objects.filter(pk=1).first() # 获取结果集中第一个对象
res = models.User.objects.filter().last() # 获取结果集中最后一个对象
QuerySet支持索引取值,但是django不推荐使用,因为索引不存在会直接报错,推荐使用first(),last()
res = models.User.objects.filter(pk=1, name='kevin').first() # 括号内支持填写多个筛选条件,默认是and关系
res = models.User.objects.filter().filter().filter().filter().filter() # 只要是QuerySet对象就可以继续点对象方法(类似于jQuery链式操作)
2.values()取值
res = models.User.objects.all().values('name','age') # QuerySet,可以看成是列表套字典
res = models.User.objects.values('name','age') # QuerySet,可以看成是列表套字典,指定字段,all不写也表示从所有数据中操作
res = models.User.objects.filter(pk=2).values('name') # 可以看成是对结果集进行字段的筛选
res = models.User.objects.all().values_list('name', 'age') # QuerySet,可以看成是列表套元组
3.distinct() 去重
distinct() # 去重,必须完全一样的数据才可以去重!
res = models.User.objects.all().distinct() # 数据对象中如果包含主键,不可能去重
res = models.User.objects.values('name').distinct() # 可以先获取具体数据,然后去重
4.order_by() 排序
order_by() # 默认排序升序!
res = models.User.objects.order_by('age')
print(res)
SQL语句:
SELECT `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age` FROM `app01_user` ORDER BY `app01_user`.`age` ASC LIMIT 21;
打印结果:
<QuerySet [<User: User object (1)>, <User: User object (7)>, <User: User object (2)>, <User: User object (3)>, <User: User object (6)>, <User: User object (4)>, <User: User object (5)>]>
5.exclude() 取反操作
# 取反操作
res = models.User.objects.exclude(name='xiaoming')
print(res) # 取除了筛选条件外的所有值,(取反操作)
SQL语句:
SELECT `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age` FROM `app01_user` WHERE NOT (`app01_user`.`name` = 'xiaoming') LIMIT 21;
打印结果:
<QuerySet [<User: User object (2)>, <User: User object (3)>, <User: User object (4)>, <User: User object (5)>, <User: User object (6)>, <User: User object (7)>]>
6.reverse() 颠倒顺序
res = models.User.objects.reverse() # 不起作用
res1 = models.User.objects.order_by('age').reverse() # 只有在order_by排序之后才可以
这个比较鸡肋 不用!
7.count() 计数
res = models.User.objects.count() # 统计结果集的个数
8.xist() 判断是否存在
exists() 判断结果是否返回了值
9.get() 获取数据对象
get() 获取数据对象
res = models.User.objects.all()
print(res)
print(res.query) # get()获取的不是QuerySet对象
打印结果:
User object (1) <class 'app01.models.User'>
SQ语句:SELECT `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age` FROM `app01_user` WHERE `app01_user`.`id` = 1;
10.all() 获取所有数据对象
all() 查询所有数据对象
res = models.User.objects.all()
print(res)
print(res.query) # 如果有QuerySet对象就可以对结果点query方法获取SQL语句
打印结果:
sql语句:
SELECT `app01_user`.`id`, `app01_user`.`name`, `app01_user`.`age` FROM `app01_user`
<QuerySet [<User: User object (1)>, <User: User object (2)>, <User: User object (3)>, <User: User object (4)>, <User: User object (5)>, <User: User object (6)>, <User: User object (7)>]>
ORM执行SQL语句
有时候 ORM的操作效率可能偏低,我们是可以自己编写SQL的
方式1:
modles.User.object.raw('sql语句')
res=models.User.objects.raw('select * from app01_user')
for i in res:# i为 res 获取到的结果 是对象
print(i.name)
方式2:
from django.db import connection
cursor = connection.cursor()
cursor.execute('SQL语句')
print(cursor.fetchall())
返回的为:元组 套 元组
((1, 'xiaoming', 12), (2, 'tony', 28),)
神奇的双下划线查询方法
'''只要还是QuerySet对象就可以无限制的点QuerySet对象的方法'''
__gt大于
# res = models.User.objects.filter(age__gt=22)
# print(res.values('name'))
__lt小于
# res = models.User.objects.filter(age__lte=30)
# print(res.values('name'))
__gte大于等于
# res = models.User.objects.filter(age__gte=30)
# print(res.values('name'))
__lte小于等于
# res = models.User.objects.filter(age__lte=22)
# print(res.values('name'))
___in(18,28,38) 查询年龄是1或者20或者30的数据
res = models.User.objects.filter(age__in=(1,20,30))
for i in res:
print(i.name)
__range(30,40) # 在30-40之间 range(范围,范围) 包含30和40
res = models.User.objects.filter(age__range=(30,40))
print(res.values('name'))
__contains='i' # 区分大小写/查询名字包含字母i的名字
res = models.User.objects.filter(name__contains='i')
print(res.values('name'))
__incontains='i' # 不区分大小写/查询名字包含字母i的名字
res = models.User.objects.filter(name__incontains='i')
print(res.values('name'))
__year=2022 # 筛选年份为2022年的
res = models.User.objects.filter(reg_time__year=2022)
print(res.values('name'))
__month=11 # 筛选月份
res = models.User.objects.filter(reg_time__month=10)
print(res.values('name'))
'''针对django框架的时区问题 是需要配置文件中修改的!'''
__startwith 以**开头
__endswith 以**结尾
__regex 正则
外键字段相关操作
'''也可以在MYSQL可视化界面插入数据,先从副表插入再在主表进行插入!'''
对一对多 插入数据可以直接填写表中的实际字段
# models.Book.objects.create(title='红楼梦', price=888.88, publish_id=1)
# models.Book.objects.create(title='千金', price=777.55, publish_id=1)
# 针对一对多 插入数据也可以填写表中的类中字段名
# publish_obj = models.Publish.objects.filter(pk=1).first()
# models.Book.objects.create(title='水浒传', price=555.66, publish=publish_obj)
多对多关系插入
# book_obj = models.Book.objects.filter(pk=2).first()
# book_obj = models.Book.objects.filter(pk=7).first()
# book_obj.author.add(1,3)
针对多对多关系绑定
# book_obj = models.Book.objects.filter(pk=1).first()
# book_obj.authors.add(1) # 在第三张关系表中给当前书籍绑定作者
# book_obj.authors.add(2, 3)
# book_obj = models.Book.objects.filter(pk=4).first()
# author_obj1 = models.Author.objects.filter(pk=1).first()
# author_obj2 = models.Author.objects.filter(pk=2).first()
# book_obj.authors.add(author_obj1)
# book_obj.authors.add(author_obj1, author_obj2)
book_obj = models.Book.objects.filter(pk=1).first()
# book_obj.authors.set((1, 3)) # 修改关系
# book_obj.authors.set([2, ]) # 修改关系
# author_obj1 = models.Author.objects.filter(pk=1).first()
# author_obj2 = models.Author.objects.filter(pk=2).first()
# book_obj.authors.set((author_obj1,))
# book_obj.authors.set((author_obj1, author_obj2))
# book_obj.authors.remove(2) 移除关系
# book_obj.authors.remove(1, 3)
# book_obj.authors.remove(author_obj1,)
# book_obj.authors.remove(author_obj1,author_obj2)
book_obj.authors.clear() # 清除全部关系
add()\remove() 多个位置参数(数字 对象)
set() 可迭代对象(元组 列表) 数字 对象
clear() 情况当前数据对象的关系
ORM外键字段的创建(重点!)
"""
MySQL多表查询思路
子查询
将SQL语句用括号括起来当做条件使用
连表操作
inner join\left join\right join\union
django orm本质还是使用的上述两种方法
子查询>>>:基于对象的跨表查询
连表操作>>>:基于双下划线的跨表查询
"""
Django orm部分
# 创建以下几张表
图书表
出版社表
作者表
作者详情表
# 关系分析
书与出版社 >>> 一对多关系
外键名 = models.ForeignKey(to='表名')
书与作者 >>> 多对多关系
外键名 = models.ManyToManyField(to='表名')
作者与详情 >>> 一对一关系
外键名 = models.OneToOneField(to='表名')
ps:三个关键字里面的参数
to用于指定跟哪张表有关系 自动关联主键
to_field\to_fields 也可以自己指定关联字段
# 字段位置
一对多关系 >>> 外键字段建在多的一方
多对多关系 >>> 外键字段建在其中一个中,第三张关系自动创建
一对一关系 >>> 外键字段建在任意一方都可以,但是推荐建在查询频率较高的表中
# ManyToManyField不会在表中创建实际的字段,而是告诉django orm自动创建第三张关系表;
# ForeignKey、OneToOneField会在字段的后面自动添加_id后缀,如果你在定义模型类的时候自己添加了该后缀那么迁移的时候还会再次添加_id_id,所以不要自己加_id后缀;
ORM跨表查询的口诀(重要)
正向查询按外键字段
反向查询按表名小写
基于对象的跨表查询
# 1.查询主键为2的书籍对应的出版社名称
# res = models.Book.objects.filter(pk=2).first().publish.name
# print(res)
#2.查询主键为4的书籍对应的作者姓名
# book_obj = models.Book.objects.filter(pk=4).first()
# print(book_obj.author.all().values('name'))
#3. 查询xiaoming的电话号码
# res = models.Author.objects.filter(name='xiaoming').first().author_detail.phone
# print(res)
# 4.查询北方出版社出版过的书籍
# res = models.Publish.objects.filter(name='北方出版社').first().book_set.all().values('name')
# print(res)
#5.查询xiaohua写过的书籍
# res = models.Author.objects.filter(name='xiaohua').first().book_set.all().values('name')
# print(res)
# 查询电话号码是110的作者姓名
# res = models.AuthorDetail.objects.filter(phone=110).first().author.name
# print(res) # 1.查询主键为2的书籍对应的出版社名称
# res = models.Book.objects.filter(pk=2).first().publish.name
# print(res)
#2.查询主键为4的书籍对应的作者姓名
# book_obj = models.Book.objects.filter(pk=4).first()
# print(book_obj.author.all().values('name'))
#3. 查询xiaoming的电话号码
# res = models.Author.objects.filter(name='xiaoming').first().author_detail.phone
# print(res)
# 4.查询北方出版社出版过的书籍
# res = models.Publish.objects.filter(name='北方出版社').first().book_set.all().values('name')
# print(res)
#5.查询xiaohua写过的书籍
# res = models.Author.objects.filter(name='xiaohua').first().book_set.all().values('name')
# print(res)
# 查询电话号码是110的作者姓名
# res = models.AuthorDetail.objects.filter(phone=110).first().author.name
# print(res)
基于上下划线的跨表查询
# 1.查询主键为2的书籍对应的出版社名称
# res = models.Book.objects.filter(pk=2).values('publish__name')
# print(res)
#2.查询主键为4的书籍对应的作者姓名
# res = models.Book.objects.filter(pk=4).values('author__name')
# print(res)
#3.查询xiaoming的电话号码
# res = models.Author.objects.filter(name='xiaoming').values('author_detail__phone')
# print(res)
#4.查询北方出版社出版过的书籍
# res = models.Publish.objects.filter(name='南方出版社').values('book__name')
# print(res)
# 查询xiaohua写过的书籍
# res = models.Author.objects.filter(name='xiaohua').values('book__name')
# print(res)
#查询电话号码是110的作者姓名
# res = models.AuthorDetail.objects.filter(phone=110).values('author__name')
# print(res)
进阶操作
# 查询主键为2的书籍对应的出版社名称
# res = models.Publish.objects.filter(book__pk=2).values('name')
# print(res)
# 查询主键为4的书籍对应的作者姓名
# res = models.Author.objects.filter(book__pk=4).values('name')
# print(res)
# 查询xiaoming的电话号码
# res = models.AuthorDetail.objects.filter(author__name='xiaoming').values('phone')
# print(res)
# 查询北方出版社出版过的书籍
# res = models.Book.objects.filter(publish__name='北方出版社').values('name')
# print(res)
#查询xiaohua写过的书籍
# res = models.Book.objects.filter(author__name='xiaohua').values('name')
# print(res)
#查询电话号码是110的作者姓名
# res = models.Author.objects.filter(author_detail__phone=110).values('name')
# print(res)
聚合查询
聚合函数:Max 、 Min Sum Counte Avg
在ORM中支持单独使用聚合函数 关键字>>>> aggregate
from django.db.models import Max, Min, Sum, Count, Avg
res = models.Book.objects.aggregate((Max('price'), Count('pk'), 最小价格=Min('price'), allPrice=Sum('price'),平均价格=Avg('price')),
分组查询
"""
如果执行orm分组查询报错 并且有关键字sql_mode strict mode
移除sql_mode中的only_full_group_by
"""
# 分组查询
# 统计每一本书的作者个数
# res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num')
# print(res)
# 统计出每个出版社卖的最便宜的书的价格
# res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
# print(res)
# 统计不止一个作者的图书
# 1.先统计每本书的作者个数
# res = models.Book.objects.annotate(author_num=Count('authors__pk'))
# 2.筛选出作者个数大于1的数据
# res = models.Book.objects.annotate(author_num=Count('authors__pk')).filter(author_num__gt=1).values('title',
# 'author_num')
# print(res)
# 查询每个作者出的书的总价格
# res = models.Author.objects.annotate(总价=Sum('book__price'),count_book=Count('book__pk')).values('name','总价','count_book')
# print(res)
"""
models.表名.objects.annotate() 按照表分组
models.表名.objects.values('字段名').annotate() 按照values括号内指定的字段分组
"""
res = models.Book.objects.values('publish_id').annotate(count_pk=Count('pk')).values('publish_id', 'count_pk')
print(res)
F与Q查询
# 1.查询库存数大于卖出数的书籍
'''当查询条件不是明确的 也需要从数据库中获取 就需要使用F查询'''
from django.db.models import F
# res = models.Book.objects.filter(kucun__gt=F('maichu'))
# print(res)
# 2.将所有书的价格涨800
# models.Book.objects.update(price=F('price') + 800)
# 3.将所有书的名称后面追加爆款
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.update(title=Concat(F('title'), Value('新款')))
# 查询主键是1或者价格大于2000的书籍
# res = models.Book.objects.filter(pk=1, price__gt=2000) # 逗号默认是and关系
from django.db.models import Q
# res = models.Book.objects.filter(Q(pk=1), Q(price__gt=2000)) # 逗号是and
# res = models.Book.objects.filter(Q(pk=1) | Q(price__gt=2000)) # |是or
res = models.Book.objects.filter(~Q(pk=1) | Q(price__gt=2000)) # ~是not
print(res.query)
Q查询的进阶操作
Q查询 可以将查询条件左边的名字变成字符串
可以跟前端进行交互 一位内前端传递道后端的数据都是字符串形式,
通过Q查询即可完成前端选择后端筛选条件的作用
from django.db.models import Q
q_obj = Q() # 1.产生q对象
q_obj = connector = 'or' # 默认多个条件的链接是and可以修改为 or
q_obj.children.append('pk',1) # 添加查询条件
q_obj.children.append('price__gt',2000) # 支持添加多个查询条件
res = modles.Book.objects.filter(q_obj) # 查询支持直接填写q对象
print(res)
ORM查询优化
'''优化本质>>>>SQL执行次数/SQL执行时间'''
1.ORM的查询默认都是惰性查询 # 如果通过ORM的查询结果没有使用,不会执行SQL语句
2.ORM的查询自带分页处理 (LIMIT21)
3.查询优化关键字(only、defer、select_related、prefetch_related)
'''数据对象+ 含有指定字段对应的数据'''
# only
res = models.Book.objects.only('title','price')
print(res) # queryset [数据对象、数据对象]
# for obj in res:
# print(obj.title) # 通过句点符点出括号内填写的字段 不走SQL查询语句
# print(obj.price)
# print(obj.publish_time) # 可以通过句点符点出括号内没有的字段获取数据 但是会走SQL查询语句
# defer
res = models.Book.objects.defer('title', 'price')
# print(res) # queryset [数据对象、数据对象]
for obj in res:
# print(obj.title) # 通过句点符点出括号内填写的字段 走SQL查询语句
# print(obj.price)
print(obj.publish_time) # 通过句点符点出括号内没有的字段获取数据 不走SQL查询语句
# select_related、
select_related('外键字段') # 连表查询,inner join 不支持多对多字段!!
先进行连表 后查询封装
在点名字的时候就不需要在走 sql语句了
res = models.Book.objects.all()
for obj in res:
print(obj.publish.name) # 每次查询都需要走SQL语句
res = models.Book.objects.select_related('authors') # 先连表后查询封装
res1 = models.Author.objects.select_related('author_detail') # 括号内不支持多对多字段 其他两个都可以
print(res1)
for obj in res:
print(obj.publish.name) # 不再走SQL查询
#prefetch_related('外键字段')
子查询 执行两条SQL语句
res = models.Book.objects.prefetch_related('publish') # 子查询
for obj in res:
print(obj.publish.name)
ORM事务操作
django 中 orm 提供了至少三种开启事务的方式:
方式1:配置文件数据库相关添加键值对 作用范围:全局有效
我们只需要在settings.py文件中 添加
'ATOMIC_REQUESTS':True
这样每次请求所涉及到的orm就同属于一个事务!
方式2:
装饰器开启事务 作用范围:当前视图函数有效
from django.db import transaction
@transaction.atomic
def index(requeqst):pass
方式3:
with 上下文管理 作用范围 针对with语法下的代码部分开启事务
from django.db import transaction
def reg(request):
with transaction.atomic()
pass
ORM常用字段类型
自增长字段
# 自增长
id = models.AutoField(primary_key=True) # 系统会默认添加此字段,无需用户自己特地添加
id = models.BigAutoField()
自增长字段的意思是,数据表中每增加一条记录,这个字段的值就会自动加1。字段的类型默认为Int整型。下面的BigAutoField字段可容纳比较大的数,比如说十亿。
但是需要注意的一点是,其实我们在定义数据库表结构的时候并不需要特地定义这样一个字段,因为Django会在每个表中自动添加一个id字段,且这个字段的类型正是自增长型。
整形字段
# 整型
PositiveSmallInteger = models.PositiveSmallIntegerField() # 5个字节 正数
SmallInteger = models.SmallIntegerField() # 6个字节 正负数
PositiveInteger = models.PositiveIntegerField() # 10个字节 正数
Integer = models.IntegerField() # 11个字节
BigInteger = models.BigIntegerField() # 20个字节
Django提供了5种不同的整型字段,可以按照两个标准来进行分类,一个是按正负数来分(Positive),一个是按数值大小来分(Small、Big)
字符字段
Char = models.CharField() # varchar
Text = models.TextField() # longtext
字符串类型的字段分为两种,上面这种对应数据库中的varchar,需要在参数max_length中指定字符串的大小。下面这种对应数据库中的longtext类型,无需指定字符串长度,想写多长就写多长。
布尔型字段
# 布尔型
Boolean = models.BooleanField()
NullBoolean = models.NullBooleanField()
jango提供了两种布尔类型的字段,BooleanField不能为空,NullBoolean的字段值可以为空。
时间日期类型
Date = models.DateField() # 年月日
DateTime = models.DateTimeField() # 年月日时分秒
Duration = models.DurationField() # int, Python timedelta实现
时间类型分为三种,Date是年月日的形式,DateTime是年月日时分秒的形式,第三种表示一段时间,在数据表中是Int类型,它的底层是通过python的timedelta实现的。
浮点型
# 浮点型
Float = models.FloatField()
Decimal = models.DecimalField() # 11.22, 16.34
浮点型也有两种,其中第二种Decimal比较特殊,需要在参数中指定整数有多少位,小数有多少位。
其他类型
# 其它字段
Email = models.EmailField() # 邮箱
Image = models.ImageField() # 图片
File = models.FileField() # 文件
FilePath = models.FilePathField() # 文件路径
URL = models.URLField() # URL地址
UUID = models.UUIDField() # UID
GenericIPAddress = models.GenericIPAddressField() #IP地址,IPV4或者IPV6
Django还为我们封装了其他更加高级的字段,从上往下依次是邮箱类型、图片、文件、文件路径、浏览器地址中输入的URL、UUID、Ip地址(IPV6或者是IPV4)
字段参数
在原生SQL定义数据表的时候我们常常需要给字段设定一些参数,
在Django字段的参数分如下三种情况
django常用字段参数
primary_key 主键
verbose_name 注释
max_length 字段长度
max_digits 小数总共多少位
decimal_places 小数点后面的位数
auto_now 每次操作数据自动更新事件
auto_now_add 首次创建自动更新事件后续不自动更新
null 允许字段为空
default 字段默认值
unique 唯一值
db_index 给字段添加索引
choices 当某个字段的可能性能够被列举完全的情况下使用
所有字段都具有的参数
1.更改字段名:db_colum=''
2.设置主键:primary_key=True,默认为False
3.给字段设置别名(备注):verbose_name=''
4.字段的唯一键属性:unique=True,设置之后,这个字段的没一条记录的每个值是唯一的
5.允许字段为空:null=True(数据库中字段可以为空),blank=True(网页表单提交内容可以为空),切记不可以将null设置为Fasle的同时还把blank设置为True。会报错的。
6.给字段建立索引:db_index=True
7.在表单中显示说明:help_text=''
8.字段值不允许更改:editable=False,默认是True,可以更改。
个别字段才拥有的参数
1.CharField(max_length=100):字段长度为utf8编码的100个字符串
2.DateField(unique_for_date=True):这个字段的时间必须唯一
3.DateField(auto_now=True):对这条记录内容更新的时间
4.DateField(auto_now_add=True):插入这条记录的时间
5.DecimalField(max_digits=4, decimal_places=2):前者表示整数和小数总共多少数,后者表示小数点的位数.
关系型字段的参数
1、models.CASCADE
级联操作,当主表中被连接的一条数据删除时,从表中所有与之关联的数据同时被删除
2、models.SET_NULL
当主表中的一行数据删除时,从表中所有与之关联的数据的相关字段设置为null,此时注意定义外键时,这个字段必须可以允许为空
3、models.PROTECT
当主表中的一行数据删除时,由于从表中相关字段是受保护的外键,所以都不允许删除
4、models.SET_DEFAULT
当主表中的一行数据删除时,从表中所有相关的数据的关联字段设置为默认值,此时注意定义外键时,这个外键字段应该有一个默认值
5、models.SET()
当主表中的一条数据删除时,从表中所有的关联数据字段设置为SET()中设置的值,与models.SET_DEFAULT相似,只不过此时从表中的相关字段不需要设置default参数
6、models.DO_NOTHING
什么都不做,一切都看数据库级别的约束,注数据库级别的默认约束为RESTRICT,这个约束与django中的models.PROTECT相似
自关联字段参数
需要在第一个参数中添加'self'字符串,或写上它自己的表名(模型类名)
多对多字段的三种创建方式
1.全自动创建
class Book(models.Model):
title = models.CharField(max_length=32)
authors = models.ManyToManyField(to='Author')
class Author(models.Model):
name = models.CharField(max_length=32)
优势:自动创建第三张表 并且提供了add、remove、set、clear四种操作
劣势:第三张表无法创建更多的字段 扩展性较差
2.纯手动创建
class Book(models.Model):
title = models.CharField(max_length=32)
class Author(models.Model):
name = models.CharField(max_length=32)
class Book2Author(models.Model):
book = models.ForeignKey(to='Book')
author = models.ForeignKey(to='Author')
others = models.CharField(max_length=32)
join_time = models.DateField(auto_now_add=True)
优势:第三张表完全由自己创建 扩展性强
劣势:编写繁琐 并且不再支持add、remove、set、clear以及正反向概念
3.半自动创建
class Book(models.Model):
title = models.CharField(max_length=32)
authors = models.ManyToManyField(to='Author',
through='Book2Author', through_fields=('book','author')
)
class Author(models.Model):
name = models.CharField(max_length=32)
class Book2Author(models.Model):
book = models.ForeignKey(to='Book', on_delete=models.CASCADE)
author = models.ForeignKey(to='Author', on_delete=models.CASCADE)
others = models.CharField(max_length=32)
join_time = models.DateField(auto_now_add=True)
优势:第三张表完全由自己创建 扩展性强 正反向概念依然清晰可用
劣势:编写繁琐不再支持add、remove、set、clear