Djiango 使用教程
目录
- Djiango 使用教程
-
一. 视图层
- 1.1 HttpResponse,render,redirect
- 1.2 JsonResponse
- 1.3 form表单上传文件及后端保存
- 1.4. request 方法
-
二. FBV和CBV
- 2.1 CBV语法格式
- 2.2 CBV 实现原理
-
三. 模版层
- 3.1 模版语法传值
- 3.2 过滤器
- 3.3 转义
-
3.4 标签逻辑系列
- 1)for循环
- 2)if判断
- 3)if+for组合
- 4)with别名
- 3.5 自定义标签,过滤器,inclusion_tag
- 3.6 模版的继承
- 3.7 模版的导入
-
四. 模型层-增删改查
- 4.1 连接数据库
- 4.2 创建表&准备测试环境
- 4.3 单表增删改
- 4.4 单表操作必会13条
- 4.5 查看内部SQL语句
- 4.6 双下划线查询
- 4.7 多表操作&环境创建
- 4.8 一对多外键增删改查
- 4.9 多对多外键增删改查
- 4.10 多表查询 正向和反向概念
- 4.11 子查询(基于对象的跨表查询)
- 4.12 联表查询(基于双下划线的跨表查询)
-
五. 模型层-聚合函数
- 5.1 聚合查询
- 5.2 分组查询
- 5.3 F和Q查询
- 5.4 djiango中开启事务
- 5.5 orm中常用字段及参数
-
六. 数据库查询优化
- 6.1 单表查询优化
- 6.2 连表查询优化
-
七. choices参数
- 7.1 数据库字段设计常见
- 7.2 创建环境
- 7.3 取choices数据
- 八. MTV与MVC模型
- 九. 多对多三种创建方式
一. 视图层
1.1 HttpResponse,render,redirect
- HttpResponse
- render
- redirect
"""
HttpResponse
返回字符串类型
render
返回html页面 并且在返回给浏览器之前还可以给html文件传值
redirect
重定向
"""
#01 正常写法
def func(repuest):
return HttpResponse('func')
return render(request,'home.html')
return redirect('https://www.baidu.com/')
return redirect('/home/') #重定向本项目
#02 如何不返回则报错
def index(request):
pass
#03 结论:视图函数必须要返回一个HttpResponse对象 正确 研究三者的源码即可得处结论
The view app01.views.index didn't return an HttpResponse object. It returned None instead.
视图app01.views.index未返回HttpResponse对象。它返回None。
#04 render简单内部原理
from django.template import Template,Context
res = Template('<h1>{{ user }}</h1>')
con = Context({'user':{'username':'jason','password':123}})
ret = res.render(con)
print(ret)
return HttpResponse(ret)
1.2 JsonResponse
"""
json格式的数据有什么用?
前后端数据交互需要使用到json作为过渡 实现跨语言传输数据
前端序列化
JSON.stringify() json.dumps()
JSON.parse() json.loads()
"""
#01 代码 json方式
import json
def ab_json(request):
user_dict = {'username':'张宇宙 fufu','password':123445}
#转为json 字符串 ensure_ascii=False 非ASCII字符直接保存到json格式中 显示中文 不被解析
json_str = json.dumps(user_dict,ensure_ascii=False)
#将字符串返回
return HttpResponse(json_str)
#02 代码 JsonResponse方式传值 字典
from django.http import JsonResponse
def ab_json(request):
user_dict = {'username':'张宇宙 fufu','password':123445}
#JsonResponse 方法 默认只能传字典 json_dumps_params = {'ensure_ascii':False} 显示中文不被解析
return JsonResponse(user_dict,json_dumps_params = {'ensure_ascii':False})
#03 列表JsonResponse方式传值
from django.http import JsonResponse
def ab_json(request):
l = [111,222,444]
# 传列表需要设置 safe=False
return JsonResponse(l, safe=False)
##默认只能序列化字典 序列化其他需要加safe参数
1.3 form表单上传文件及后端保存
"""
form表单上传文件类型的数据
1.method必须指定成post
2.enctype必须换成formdata
"""
#01 视图层
def ab_file(request):
if request.method == 'POST':
print(request.POST) #只能获取普通的简直对数据 文件不行
print(request.FILES) #获取文件数据
# 获取文件对象 取出 file
# <MultiValueDict: {'file': [<TemporaryUploadedFile: IMG_6543.JPG (image/jpeg)>]}>
file_obj = request.FILES.get('file')
#获取文件名称
print(file_obj.name)
#保存文件 ../file_obj.name 路径 path+文件名称 默认在当前html 同级目录
with open(file_obj.name,'wb') as f:
#.chunks() 切片保存,不写也一样
for line in file_obj.chunks():
f.write(line)
return render(request,'file.html')
#02 前端页面
<body>
<form action="" method="post" enctype="multipart/form-data" >
<p>username:<input type="text" name="username" ></p>
<p>file:<input type="file" name="file"></p>
<input type="submit" >
</form>
</body>
1.4. request 方法
"""
request.method #获取请求方法
request.POST #获取post请求数据
request.GET #获取get请求
request.FILES #获取文件数据
request.body #原生的浏览器发过来的二进制数据 后面详细的讲
request.path
request.path_info
request.get_full_path() 能过获取完整的url及问号后面的参数
"""
print(request.path) # /app01/ab_file/
print(request.path_info) # /app01/ab_file/
print(request.get_full_path()) # /app01/ab_file/?username=jason
二. FBV和CBV
2.1 CBV语法格式
#01 FBV是视图层的函数 以上皆为FBV
def index(request):
return HttpResponse('index')
#02 CBV是类函数
1)CBV路由层 MyLogin是类名称
url(r'^login/',views.MyLogin.as_view()),
2)视图层 需要import View模块
from django.views import View
class MyLogin(View):
def get(self,request):
return render(request,"file.html")
def post(self,request):
return HttpResponse("post方法")
##结论 方式该路径时候 CBV会自动判断方法类型 get会走get函数 post会走post函数
"""
FBV和CBV各有千秋
CBV特点
能够直接根据请求方式的不同直接匹配到对应的方法执行
内部到底是怎么实现的?
CBV内部源码(******)
"""
2.2 CBV 实现原理
- URL classonlymethod装饰方法
- 反射机制
#01 路由层
url(r'^login/',views.MyLogin.as_view())
1)as_view代码
@classonlymethod
def as_view(cls, **initkwargs):
"""
cls就是我们自己写的类 MyCBV
Main entry point for a request-response process.
"""
def view(request, *args, **kwargs):
self = cls(**initkwargs) # cls是我们自己写的类
# self = MyLogin(**initkwargs) 产生一个我们自己写的类的对象
return self.dispatch(request, *args, **kwargs)
"""
以后你们会经常需要看源码 但是在看python源码的时候 一定要时刻提醒自己面向对象属性方法查找顺序
先从对象自己找
再去产生对象的类里面找
之后再去父类找
...
总结:看源码只要看到了self点一个东西 一定要问你自己当前这个self到底是谁
这里调用了 dispatch 函数 (对象--对象类--父类里查找)
"""
return view
"""
结论:发现 as_view是闭包函数 并被classonlymethod装饰过 并非返回 view函数名称
url(r'^login/',views.MyLogin.as_view()) 等同于 url(r'^login/',views.view) FBV一模一样
"""
#02 dispatch方法
def dispatch(self, request, *args, **kwargs):
"""
获取当前请求的小写格式 然后比对当前请求方式是否合法
get请求为例
post请求
内置8个方法 http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
判断当前请求是否 包含在内置8个合法请求内
"""
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
"""
反射:通过字符串来操作对象的属性或者方法
handler = getattr(自己写的类产生的对象,'get',当找不到get属性或者方法的时候就会用第三个参数)
handler = 我们自己写的类里面的get方法
"""
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
"""
自动调用get方法
"""
三. 模版层
3.1 模版语法传值
- {{}} 变量相关
- {%%} 逻辑相关
#01 视图层 列表字段元祖集合 类对象等传值
def index(request):
n = 123
f = 11.11
s = "我在想付付"
b = True
l = ['付付',123,'小爱同学']
t = ('111','222','333')
d = {'username': '付付','age':18,'info':'此情可待成追忆'}
se = {'嘤嘤嘤','嘻嘻','哈哈哈'}
def func():
print('我被执行了')
return '只是当时已惘然'
class MyClass(object):
def get_self(self):
return 'self'
@staticmethod
def get_func():
return 'func'
@classmethod
def get_class(cls):
return 'cls'
def __str__(self):
return '嘻嘻嘻嘻'
obj = MyClass()
return render(request,'index.html',locals())
#02 前端取值 {{ 名称 }}
<p>{{ n }}</p>
<p>{{ f }}</p>
<p>{{ s }}</p>
<p>{{ b }}</p>
<p>{{ l }}</p>
<p>{{ t }}</p>
<p>{{ d }}</p>
<p>{{ se }}</p>
<p>传递函数名会自动加括号调用 但是模版语法不支持给函数传额外的参数:{{ func }}</p>
<p>传类名的时候也会自动加括号调用(实例化){{ MyClass }}</p>
<p>内部能够自动判断出当前的变量名是否可以加括号调用 如果可以就会自动执行 针对的是函数名和类名</p>
<p>{{ obj.get_class }}</p>
<p>{{ obj.get_func }}</p>
<p>{{ obj.get_self }}</p>
#注意:对象加点 可以直接调用内部方法运行,前端的结果是 return的值,传对象和类 会自动帮你加括号调用
#03 模版语法取值
1) 视图层
l = ['付付',123,'小爱同学']
d = {'username': '付付','age':18,'info':'此情可待成追忆','hobby':[111,222,{'info':'love'}]}
2)前端取值
<p>字典取值:{{ d.username }}</p> #付付
<p>列表取值:{{ l.2 }}</p> #小爱同学
<p>字典取值: {{ d.hobby.2.info }}</p> #love
#结论:django模版语法的取值 是固定的格式 只能采用“句点符” 即可以点键也可以点索引 还可以两者混用
3.2 过滤器
#01 语法格式
{{数据|过滤器:参数}}
#02 过滤器类型
<p>统计长度:{{ d|length }}</p>
<p>默认值(第一个参数布尔值是True就展示第一个参数的值否在展示冒号后面的值):{{ b|default:'前面是假的 会显示这个' }}</p>
<p>文件大小:{{ filesize | filesizeformat }}</p>
<p>时间格式化(时区相差6小时):{{ current_time | date:'Y-m-d H:i:s' }}</p>
<p>切片操作(支持步长 包含三个点):{{ l|slice:'0:8:2' }}</p>
<p>切取字符串(包含三个点):{{ info|truncatechars:9 }}</p>
<p>切取单词(中英文 默认分隔符是空格 3): {{ info|truncatewords:3 }}</p>
<p>切取单词(中英文 默认分隔符是空格 6): {{ egl |truncatewords:6 }}</p>
<p>移除特定字符(问好):{{ msg|cut:'?' }}</p>
<p>移除特定字符(空格):{{ msg|cut:' ' }}</p>
<p>拼接操作:{{ l|join:'$^' }}</p>
<p>拼接操作(数字相加){{ n|add:10 }}</p>
<p>拼接操作(字符串相加){{ s|add:'msg' }}</p>
#03 视图层
n = 123
f = 11.11
s = "我在想付付"
b = True
l = ['付付',123,'小爱同学',333,444,555,666]
t = ('111','222','333')
d = {'username': '付付','age':18,'info':'此情可待成追忆','hobby':[111,222,{'info':'love'}]}
se = {'嘤嘤嘤','嘻嘻','哈哈哈'}
info = 'Django 框架系列 目录Django 框架系列一HttpResponse,render,red'
egl = 'my name is zhangyuzhou my age is 18 i am from china'
msg = 'fufu i love you is who?'
n = 100
#04 页面结果
3.3 转义
#01 语法结构 把html js语法转义到页面
{{ 变量名称 |safe }}
#03 后端转义
hhh = '<h2>付付</h2>'
sss = '<script>alert(123)</script>'
from django.utils.safestring import mark_safe
res = mark_safe('<h2>付付2.0</h2>') #使用mark_safe方法 之前传值给前端即可
#02 前端转义 safe
<p>转义html:{{ hhh|safe }}</p>
<p>转义js:{{ sss|safe }}</p>
<p>后端转义:{{ res }}</p>
3.4 标签逻辑系列
1)for循环
#01 语法结构( for + table键 自动补全)
{% for name in l %}
<p>{{ name }}</p> #循环的元素
{% endfor %}
#02 for循环内置模版变量
{% for foo in l %}
<p>{{ forloop }}</p>
{% endfor %}
{'parentloop': {}, 'counter0': 0, 'counter': 1, 'revcounter': 7, 'revcounter0': 6, 'first': True, 'last': False}
"""
forloop.counter 知识for循环已经循环了多少次,从 1 开始计数
forloop.counter() 和forloop.counter一样,只是从 0 开始计数
forloop.revcounter 从倒数开始数,循环的次数,从 1 开始计数
forloop.revcounter()从倒数开始数,循环的次数,从 0 开始计数
forloop.first 是否是第一次循环,如果是返回 True 。否则返回 False
forloop.last 是否是最后一次循环,如果是返回 True 。否则返回 False
forloop.parentloop 嵌套循环时,是一个指向当前循环的上一级循环的forloop对象的引用。如 forloop.parentloop.counter 表示上一级循环循环了多少次
"""
2)if判断
#01 语法格式(if+tab 自动补全)
{% if b %} #判断是否为真
<p>baby</p>
{% elif s %} #判断
<p>爱了</p>
{% else %} #否侧
<p>否侧你完了</p>
{% endif %}
#02 视图层数据
s = "我在想付付"
b = False
#03 结果
爱了
3)if+for组合
#01 语法结构
l = ['付付','付付2.0','小爱同学','付付3.0',444,555,666]
{% for foo in l %}
{% if forloop.first %}
<p>这是我的第一次</p>
{% elif forloop.last %}
<p>这是最后一次了</p>
{% else %}
<p>{{ foo }}</p>
{% endif %}
{% empty %}
<p>for循环的可迭代对象内部没有元素,无法循环</p>
{% endfor %}
#解释下:empty for循环的可迭代对象内部没有元素,无法循环则会打印这个
###循环字典方法
d = {'username': '付付','age':18,'info':'此情可待成追忆','hobby':[111,222,{'info':'love'}]}
#03 默认循环的是 d.keys
{% for foo in d %}
<p>{{ foo }}</p>
{% endfor %}
<hr>
#04 循环字典的key
{% for key in d.keys %}
<p>{{ key }}</p>
{% endfor %}
#05 循环字典的值
{% for value in d.values %}
<p>{{ value }}</p>
{% endfor %}
#06循环字典的k+v
{% for item in d.items %}
<p>{{ item }}</p>
{% endfor %}
4)with别名
#01 语法结构 给love起别名(with+tab)
d = {'username': '付付','age':18,'info':'此情可待成追忆','hobby':[111,222,{'info':'love'}]}
{% with d.hobby.2.info as fufu %}
<p>{{ fufu }}</p>
{% endwith %}
3.5 自定义标签,过滤器,inclusion_tag
#01 自定义过滤器(最多两个参数)
1)定义
"""
先三步走
1.在应用下创建一个名字”必须“叫templatetags文件夹
2.在该文件夹内创建“任意”名称的py文件 eg:mytag.py
3.在该py文件内"必须"先书写下面两句话(单词一个都不能错)
from django import template
register = template.Library()
"""
2)语法格式
from django import template
register = template.Library()
3)案例数字相加
@register.filter(name='fu')
def my_sum(v1, v2):
return v1 + v2
4)示例:将文本转换为大写
@register.filter(name='my_filter')
def my_filter(value):
return value.upper()
5)前端使用
{% load mytag %}
<p>{{ n |fu:666 }}</p>
<p>{{ 1000 |fu:666 }}</p>
<p>{{ "hello fuuf" | my_filter }}</p>
#02 自定义标签(参数可以有多个)
1) 后端
@register.simple_tag(name='puls')
def puls(a,s,d,f):
return '%s-%s-%s-%s'%(a,s,d,f)
2)前端
{% load mytag %}
{% puls 'fufu' 123 'love' 334 %}
3)结果
fufu-123-love-334
#03 自定义inclusion_tag装饰器
1) 后端
@register.inclusion_tag('left.html') #作用html页面 left
def left(n):
data = [f'第{i}项' for i in range(n)]
return locals()
2)前端作用页面
<ul>
{% for foo in data %} # 接收data 并循环
<li>{{ foo }}</li>
{% endfor %}
</ul>
3)调用
{% load mytag %}
{% left 10 %}
- Inclusion_tag
3.6 模版的继承
"""
# 模版的继承 你自己先选好一个你要想继承的模版页面
{% extends 'home.html' %}
# 继承了之后子页面跟模版页面长的是一模一样的 你需要在模版页面上提前划定可以被修改的区域
{% block content %}
模版内容
{% endblock %}
# 子页面就可以声明想要修改哪块划定了的区域
{% block content %}
子页面内容
{% endblock %}
"""
#01 案例书写个网站页面 要求导航条和侧边栏保持不动 主题内容根据功能改变
#02 路由层
url(r'^home/',views.home),
url(r'^loginu/', views.loginu),
url(r'^reg/', views.reg),
#03 视图层
def home(request):
return render(request,'home.html')
def loginu(request):
return render(request,'loginu.html')
def reg(request):
return render(request,'reg.html')
#04 html文件 给需要修改的区域添加 标签 block confufu
{% block css %}
{% endblock %}
{% block confufu %}
<div class="panel-body">
<div class="jumbotron">
<h1>Hello, world!</h1>
<p>...</p>
<p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p>
</div>
</div>
{% endblock %}
{% block js %}
{% endblock %}
#05 模版的继承
{% extends 'home.html' %}
{% block confufu %}
<h1>登入页面</h1>
<form action="">
<p>username: <input type="text" class="form-control"></p>
<p>password: <input type="text" class="form-control"></p>
<input type="submit" class="btn btn-success">
</form>
{% endblock %}
#06 一般情况下模版页面上应该至少有三块可以被修改的区域
1.css区域
2.html区域
3.js区域
{% block css %}
{% endblock %}
{% block content %}
{% endblock %}
{% block js %}
{% endblock %}
# 每一个子页面就都可以有自己独有的css代码 html代码 js代码
-------案例------
{% extends 'home.html' %}
{% block css %}
<style>
.h {
color: #31708f;
}
</style>
{% endblock %}
{% block confufu %}
<h1 class="h">注册页面</h1>
<form action="">
<p>username: <input type="text" class="form-control"></p>
<p>password: <input type="text" class="form-control"></p>
<input type="submit" class="btn btn-danger">
</form>
{% include 'love.html' %}
{% endblock %}
{% block js %}
<script>
alert(1234)
</script>
{% endblock %}
- 图示
3.7 模版的导入
"""
将页面的某一个局部当成模块的形式
哪个地方需要就可以直接导入使用即可
"""
{% include 'wasai.html' %}
四. 模型层-增删改查
4.1 连接数据库
#01 修改配置文件 settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'day64',
'HOST': '192.168.5.9',
'USER': 'root',
'PASSWORD': '0x00NF2001',
'POST': 3306,
'CHARSET': 'utf8'
}
}
#02 主项目下 __init__文件内声明该模块
import pymysql
pymysql.install_as_MySQLdb()
#03 配置文件templates
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR,'templates')]
##其它方式连接信息(和本代码无关 只单纯记录)
conn = pymysql.connect(
user='root',
host='192.168.5.9',
password='0x00NF2001',
port=3306,
database='note',
charset='utf8',
autocommit=True
)
4.2 创建表&准备测试环境
#01 创建表
class User(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
register_time = models.DateField() #年月日
uptime = models.DateTimeField(auto_now=True) #年月日时分秒
"""
DateTimeField 年月日 时分秒
DateField 年月日
两个重要参数
auto_now: 每次操作数据的时候 该字段会自动将当前时间更新
auto_now_add: 在创建数据的时候会自动将当前时间记录下来 之后只要不人为修改 那么一直不变
"""
#02 运行manage任务 创建表
makemigrations
migrate
#03 准备测试环境 tests.py
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
import django
django.setup()
#下方可以写Djiango 查询语句,需要查什么模版 导入即可
from app01 import models
models.User.objects.all() #查询
"""
当你只是想测试django中的某一个py文件内容 那么你可以不用书写前后端交互的形式
而是直接写一个测试脚本即可
脚本代码无论是写在应用下的tests.py还是自己单独开设py文件都可以
测试环境的准备 去manage.py中拷贝前四行代码 然后自己写两行
"""
4.3 单表增删改
#01 增加数据
#1.1 方式1
models.User.objects.create(name='付付',age='18',register_time='2000-11-06')
#1.2 方式2
import datetime
ctime = datetime.datetime.now()
obj = models.User(name='zhangyuzhou',age='20',register_time=ctime) #支持变量方式
print(obj)
obj.save()
#02 删除数据
#2.1 方式1
models.User.objects.filter(pk=2).delete()
models.User.objects.filter(age=18).delete()
"""
pk=当前表的主键值
pk会自动查找到当前表的主键字段 指代的就是当前表的主键字段
filter(过滤条件)
"""
#2.2 方式2 获取当前数据对象
user_obj = models.User.objects.filter(pk=6).first()
print(user_obj)
user_obj.delete()
#03 修改数据
#3.1 方式1
res = models.User.objects.filter(pk=5).update(name='love')
#3.2 方式2
user_obj = models.User.objects.filter(pk=5).first()
user_obj.name = '爱情'
user_obj.save()
#3.3 方式3
user_obj = models.User.objects.get(pk=5)
user_obj.age = 19
user_obj.save()
"""
get方法返回的直接就是当前数据对象
但是该方法不推荐使用
一旦数据不存在该方法会直接报错
而filter则不会
所以我们还是用filter
"""
4.4 单表操作必会13条
#01 all() 查询所有数据
res = models.User.objects.all()
print(res)
#02 filte() 带有过滤条件的查询
res = models.User.objects.filter(pk=5).first()
print(res)
#03 get() 直接拿数据对象 但是条件不存在直接报错
user_obj = models.User.objects.get(pk=5)
user_obj.age = 19
user_obj.save()
#04 拿queryset里面第一个元素
res = models.User.objects.all().first()
print(res)
#05 last() 拿queryset里面最后第一个元素
res = models.User.objects.all().last()
print(res)
#06 values() 可以指定获取的数据字段 select name,age from ... 列表套字典
res = models.User.objects.values('name','age')
print(res)
print(res.query) #查看SQL语句 只针对QuerySet对象
<QuerySet [{'name': 'zhangyuzhou', 'age': 20}, {'name': '爱情', 'age': 19}]>
#07 values_list() 列表套元祖
res = models.User.objects.values_list('name','age')
print(res)
<QuerySet [('zhangyuzhou', 20), ('爱情', 19), ('付付', 18), ('汤姆', 33)]>
#08 distinct() 去重 并非从表内删除
res = models.User.objects.values('name','age').distinct()
print(res)
"""
去重一定要是一模一样的数据
如果带有主键那么肯定不一样 你在往后的查询中一定不要忽略主键
"""
#09 order_by() 排序
res = models.User.objects.order_by('age') #正序
res_re = models.User.objects.order_by('-age') #倒序
print(res)
print(res_re)
#10 reverse() 反转的前提是 数据已经排过序了 order_by()
res = models.User.objects.order_by('age').reverse()
print(res)
#11 count() 统计当前数据个数 可以加条件filter()
res = models.User.objects.filter(name='付付').count()
res = models.User.objects.count()
print(res)
#12 排除 exclude() 排除在外
res = models.User.objects.exclude(name='付付')
print(res)
#13. exists() 基本用不到因为数据本身就自带布尔值 返回的是布尔值
res = models.User.objects.filter(pk=1).exists()
print(res)
4.5 查看内部SQL语句
#01 方式一 queryset对象才能够点击query查看内部的sql语句
res = models.User.objects.values('name','age')
print(res)
结果如下:
<QuerySet [{'name': 'zhangyuzhou', 'age': 20}, {'name': '爱情', 'age': 19}]>
print(res.query) #查看SQL语句 只针对QuerySet对象
结果如下:
SELECT `app01_user`.`name`, `app01_user`.`age` FROM `app01_user`
#02 修改配置文件 匹配所有查询SQL语句
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
查询会自动打印SQL语句
res = models.User.objects.values('name','age')
print(res)
结果如下:
(0.005) SELECT `app01_user`.`name`, `app01_user`.`age` FROM `app01_user` LIMIT 21; args=()
4.6 双下划线查询
#01 年龄大于20
res = models.User.objects.filter(age__gt=20)
print(res)
#02 年龄大于等于20
res = models.User.objects.filter(age__gte=20)
print(res)
#03 年龄小于20
res = models.User.objects.filter(age__lt=20)
print(res)
#04 年龄小于等于20 并且name等于付付的
res = models.User.objects.filter(age__lte=20,name='付付')
print(res)
#05 年龄是 16或者19 或者33的 (在列表) 并按照年龄排序
res = models.User.objects.filter(age__in=[16,19,33]).order_by('age')
print(res)
#06 年龄在18到33之间的 并且只取name,age字段 首尾都要
res = models.User.objects.filter(age__range=[18,33]).values('name','age')
#07 查出名字内包含 F的
res = models.User.objects.filter(name__contains='F') #__contains 区分大小写
res1 = models.User.objects.filter(name__icontains='F') #__icontains 忽略大小写
#08 查询以什么开头或者结尾的
res = models.User.objects.filter(name__istartswith='F') #__startswith 区分大小写 查询以什么开头
res1 = models.User.objects.filter(name__startswith='F') #__istartswith 忽略大小写 查询以什么开头
res = models.User.objects.filter(name__endswith='U') #__endswith 区分大小写 查询以什么结尾
res1 = models.User.objects.filter(name__iendswith='U') #__endswith 忽略大小写 查询以什么结尾
#09 日期查询 查询出注册时间是 2000 11月
res = models.User.objects.filter(register_time__month=11,register_time__year=2000)
print(res)
"""
register_time__month = 月
register_time__year = 年
register_time__day = 天
"""
4.7 多表操作&环境创建
#图书表
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8,decimal_places=2)
publish_data = models.DateField(auto_now_add=True)
#书籍和出版社关系表 一对多
publish = models.ForeignKey(to='Publish')
#多对多 书籍和作者 多个书籍可以有多个作者
authors = models.ManyToManyField(to='Author')
#出版社
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=64)
email = models.EmailField() #varchar(254) 该字段不是给model看的 而是用于校验组件
#作者表
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
#一对一 作者对应作者详情
author_dateil = models.OneToOneField(to='AuthorDetail')
#作者详情表
class AuthorDetail(models.Model):
phone = models.BigIntegerField() #电话号码
addr = models.CharField(max_length=64)
##运行语句
makemigrations
migrate
4.8 一对多外键增删改查
#01 增加数据 书籍名称 价格 对应的出版社
#1.1 方式1 直接写出版社 ID主键
models.Book.objects.create(title='论语',price=899.11,publish_id=1)
models.Book.objects.create(title='大学',price=799.22,publish_id=2)
models.Book.objects.create(title='中庸',price=699.33,publish_id=1)
#1.2 方式2 虚拟字段对象, 先查出出版社数据对象 然后在书籍表内添加
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.create(title='红楼梦',price=19.98,publish=publish_obj)
#02 删除操作 删除出版社id=1的数据
models.Book.objects.filter(publish_id=1).delete()
#03 修改数据
# 3.1 方式一 修改主键是12的书籍表 修改内容:publish_id=2
models.Book.objects.filter(pk=12).update(publish_id=2)
# 3.2 方式二 查出出版社对象 然后在书籍表内更新
obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.filter(pk=13).update(publish=obj)
4.9 多对多外键增删改查
#01 通过虚拟字段获取第三张表的数据对象
book_obj = models.Book.objects.filter(pk=12).first()
print(book_obj.authors) #就类似于你已经到了第三张关系表了
#02 给书籍增加作者 无则添加,有则不动
book_obj.authors.add(1) #直接写ID方式
book_obj.authors.add(1,2)
authors_obj = models.Author.objects.filter(pk=1).first() #虚拟对象方式
authors_obj1 = models.Author.objects.filter(pk=2).first()
authors_obj2 = models.Author.objects.filter(pk=3).first()
book_obj.authors.add(authors_obj)
book_obj.authors.add(authors_obj1,authors_obj2)
"""
add给第三张关系表添加数据
括号内既可以传数字也可以传对象 并且都支持多个
"""
#03 删除 书籍表内作者ID等于1的 数据
book_obj.authors.remove(1)
book_obj.authors.remove(2,3)
# 删除书籍ID=12 对着作者ID=2的
# 获取作者ID
author_obj = models.Author.objects.filter(pk=2).first()
# 调用方法 在书籍表内删除对应ID是2的作者
book_obj.authors.remove(author_obj)
"""
remove
括号内既可以传数字也可以传对象 并且都支持多个
"""
#04 修改 修改书籍表ID是12的关系表 对应作者ID是 1和1 ,有则修改 无则添加 多则删除
book_obj.authors.set([1,2])
book_obj.authors.set([3])
author_obj = models.Author.objects.filter(pk=2).first()
author_obj1 = models.Author.objects.filter(pk=3).first()
book_obj.authors.set([author_obj,author_obj1]) # 括号内必须给一个可迭代对象
"""
set
括号内必须传一个可迭代对象,该对象内既可以数字也可以对象 并且都支持多个
"""
#05 清空 在第三张关系表中清空某个书籍与作者的绑定关系
book_obj.authors.clear()
"""
clear
括号内不要加任何参数
"""
4.10 多表查询 正向和反向概念
# 正向
# 反向
外键字段在我手上那么,我查你就是正向
外键字段如果不在手上,我查你就是反向
book >>>外键字段在书那儿(正向)>>> publish
publish >>>外键字段在书那儿(反向)>>>book
一对一和多对多正反向的判断也是如此
"""
正向查询按字段
反向查询按表名小写
_set
...
"""
4.11 子查询(基于对象的跨表查询)
"""
在书写orm语句的时候跟写sql语句一样的
不要企图一次性将orm语句写完 如果比较复杂 就写一点看一点
正向什么时候需要加.all()
当你的结果可能有多个的时候就需要加.all()
如果是一个则直接拿到数据对象
book_obj.publish
book_obj.authors.all()
author_obj.author_detail
"""
------------正向查询按照外键字段---------------
#01 查询书籍主键为11的出版社 正向查询
book_obj = models.Book.objects.filter(pk=11).first()
res = book_obj.publish
print(res)
#02 查询书籍主键为12的作者
# 这里结果有多个 需要加all()
book_obj = models.Book.objects.filter(pk=12).first()
res = book_obj.authors.all()
for i in res:
print(i.name)
#03 查询作者 fufu 的电话号码
"""
author_obj.name 作者名称
res.phone 电话号码
"""
author_obj = models.Author.objects.filter(name='fufu').first()
res = author_obj.author_dateil #外键字段 author_dateil
print(author_obj.name,res.phone)
------------反向查询按照表名---------------
##反向查询
#01 查询出版社是东方出版社的书
publish_obj = models.Publish.objects.filter(name='东方出版社').first()
res = publish_obj.book_set.all() #这里用出版社对象.书籍表名_set.all()
print(res) #结果是多个 需要在表名称后加_set.all()
#02 查询fufu作者 写过的书
author_obj = models.Author.objects.filter(name='fufu').first()
res = author_obj.book_set.all()
for i in res:
print(author_obj.name,i.title)
#03 查询手机号是1314的作者姓名
author_detail_obj = models.AuthorDetail.objects.filter(phone=1314).first()
res = author_detail_obj.author #这里结果只有一个 所以不需要加 _set.all()
print(res.name)
"""
基于对象
反向查询的时候
当你的查询结果可以有多个的时候 就必须加_set.all()
当你的结果只有一个的时候 不需要加_set.all()
"""
4.12 联表查询(基于双下划线的跨表查询)
------------------正向查询基于字段----------------------
# 01 查询fufu的手机号和 作者姓名 地址(正向查询 作者表-->>作者详情表)
res = models.Author.objects.filter(name='fufu').values(
'author_dateil__addr',
'author_dateil__phone',
'name')
print(res)
#02 查询书籍名称是红楼梦的 出版社姓名和作者
"""
用书籍表去查询 出版社表和作者表
"""
res = models.Book.objects.filter(title='红楼梦').values('publish__name','authors__name','title')
print(res)
#03 查询书籍主键是12的 书籍名称 作者姓名和电话
res = models.Book.objects.filter(pk=12).values(
'title',
'authors__name',
'authors__author_dateil__phone')
------------------反向查询基于表名----------------------
#01 查询手机号是 1314的作者
"""
根据作者详情反向查询作者
反向查询 写表名称
"""
res = models.AuthorDetail.objects.filter(phone=1314).values(
'author__name','phone')
print(res)
#02 出版社发布过红楼梦的 出版社名称和书的名称 及书的作者
"""
根据出版社表 反向查询 书籍表和作者表
反向查询 写表名称
"""
res = models.Publish.objects.filter(book__title='红楼梦').values(
'name','book__title','book__authors__name')
print(res)
#03 查询书籍主键为12的作者姓名和地址
res = models.Author.objects.filter(book__id=12).values(
'book__title','name','author_dateil__addr')
print(res)
#04 查询书籍主键为11的作者姓名和地址 book主主要查询
"""
book关联作者表
作者表关联作者详情
authors__author_dateil__phone
"""
res = models.Book.objects.filter(pk=12).values(
'authors__author_dateil__phone','authors__name','title')
print(res)
五. 模型层-聚合函数
5.1 聚合查询
- 单独查询需要使用:aggregate
- Max,Min,Count,Avg,Sum
## 导入聚合模块
from django.db.models import Max,Min,Count,Avg,Sum
from app01 import models
"""
聚合查询通常情况下都是配合分组一起使用的
只要是跟数据库相关的模块
基本上都在django.db.models里面
如果上述没有那么应该在django.db里面
"""
#01 统计书籍的平均价格
res = models.Book.objects.aggregate(Avg('price'))
print(res)
#02 统计所有书籍 最大值 最小值 个数 平均值 求和
res = models.Book.objects.aggregate(
Avg('price'),Min('price'),Max('price'),
Sum('price'),Count('price'))
print(res)
{'price__avg': 604.41, 'price__min': Decimal('19.98'), 'price__max': Decimal('899.11'), 'price__sum': Decimal('2417.64'), 'price__count': 4}
5.2 分组查询
- annotate
##聚合查询 导入功能模块
from django.db.models import Max,Min,Count,Avg,Sum
#01 统计每一本书的作者数量
#res = models.Book.objects.annotate() # models后面点什么 就是按什么分组
res = models.Book.objects.annotate(authors_num=Count('authors')).values('title','authors_num')
print(res)
"""
authors_num 是我们自己定义的字段 用接收聚合的结果
authors 书籍表的外键字段 对应的是作者表
values 取值展示
"""
#02 统计每个出版社卖的最便宜的书的价格, 每个出版社 最便宜的书价格
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price')
print(res)
#03 找出图书的作者是多个的
"""
先找出多个作者的书籍
然后进行过滤 找出大于1的
只要你的orm语句得出的结果还是一个queryset对象
那么它就可以继续无限制的点queryset对象封装的方法
"""
res = models.Book.objects.annotate(count_author=Count('authors')).filter(count_author__gt=1).values(
'title','count_author'
)
print(res)
#04 查询每个作者出的书的 总价格(作者名称 书籍价格)
res= models.Author.objects.annotate(sum_book=Sum('book__price')).values('name','sum_book','count_book')
print(res)
"""
如果我想按照指定的字段分组该如何处理呢?
models.Book.objects.values('price').annotate()
后续BBS作业会使用
你们的机器上如果出现分组查询报错的情况
你需要修改数据库严格模式
"""
5.3 F和Q查询
- F查询
## F 查询需要先倒入功能模块
"""
能够帮助你直接获取到表中某个字段对应的数据
在操作字符类型的数据的时候 F不能够直接做到字符串的拼接
"""
##字段数据准备
#图书表
class Book(models.Model):
# 库存
kucun = models.IntegerField(default=1000)
# 卖出
maichu = models.IntegerField(default=100)
def __str__(self):
return self.title
#01 查询卖出大于库存的书籍
from django.db.models import F
res = models.Book.objects.filter(maichu__gt=F('kucun'))
print(res)
#02 将书籍的价格提升500块
res = models.Book.objects.update(price=F('price') + 500)
#03 将所有书名称加上爆款两个字
"""
在操作字符类型的数据的时候 F不能够直接做到字符串的拼接
需要使用
from django.db.models.functions import Concat
from django.db.models import Value
"""
from django.db.models.functions import Concat
from django.db.models import Value
res = models.Book.objects.update(title=Concat(F('title'),Value('爆款')))
- Q查询
## Q查询 导入功能模块
from django.db.models import Q
#01 查询书籍卖出数量大于100 或者小于600的
# res = models.Book.objects.filter(Q(price__gt=100),Q(price__lt=600)) # , 默认是 and关系 与
# res = models.Book.objects.filter(Q(price__gt=100)|Q(price__lt=600)) # | 是或者 or 关系 或
# res = models.Book.objects.filter(~Q(price__gt=100)|Q(price__lt=600)) # ~ NOT 前面条件取反 非
print(res)
#02 Q的高阶用法 能够将查询条件的左边也变成字符串的形式
q = Q()
q.connector = 'or' #默认是 default = AND 并且关系
q.children.append(('maichu__gt',100))
q.children.append(('price__lt', 600))
res = models.Book.objects.filter(q)
print(res)
5.4 djiango中开启事务
##事务的特点
"""
事务
ACID
原子性
不可分割的最小单位
一致性
跟原子性是相辅相成
隔离性
事务之间互相不干扰
持久性
事务一旦确认永久生效
msyql事务的回滚
rollback
msyql事务的确认
commit
"""
#开启事务 需要导入功能模块 transaction
from django.db import transaction
try:
with transaction.atomic():
"""
sql1
sql2
在with代码快内书写的所有orm操作都是属于同一个事务
"""
pass
except Exception as e:
print(e)
print('执行其他操作')
5.5 orm中常用字段及参数
AutoField
主键字段 primary_key=True
CharField varchar
verbose_name 字段的注释
max_length 长度
IntegerField int
BigIntegerField bigint
DecimalField
max_digits=8
decimal_places=2
EmailFiled varchar(254)
DateField date
DateTimeField datetime
auto_now:每次修改数据的时候都会自动更新当前时间
auto_now_add:只在创建数据的时候记录创建时间后续不会自动修改了
BooleanField(Field) - 布尔值类型
该字段传布尔值(False/True) 数据库里面存0/1
TextField(Field) - 文本类型
该字段可以用来存大段内容(文章、博客...) 没有字数限制
后面的bbs作业 文章字段用的就是TextField
FileField(Field) - 字符类型
upload_to = "/data"
给该字段传一个文件对象,会自动将文件保存到/data目录下然后将文件路径保存到数据库中
/data/a.txt
后面bbs作业也会涉及
# 更多字段
直接参考博客: https://www.cnblogs.com/Dominic-Ji/p/9203990.html
# django除了给你提供了很多字段类型之外 还支持你自定义字段
class MyCharField(models.Field):
def __init__(self,max_length,*args,**kwargs):
self.max_length = max_length
# 调用父类的init方法
super().__init__(max_length=max_length,*args,**kwargs) # 一定要是关键字的形式传入
def db_type(self, connection):
"""
返回真正的数据类型及各种约束条件
:param connection:
:return:
"""
return 'char(%s)'%self.max_length
# 自定义字段使用
myfield = MyCharField(max_length=16,null=True)
# 外键字段及参数
unique=True
ForeignKey(unique=True) === OneToOneField()
# 你在用前面字段创建一对一 orm会有一个提示信息 orm推荐你使用后者但是前者也能用
db_index
如果db_index=True 则代表着为此字段设置索引
(复习索引是什么)
to_field
设置要关联的表的字段 默认不写关联的就是另外一张的主键字段
on_delete
当删除关联表中的数据时,当前表与其关联的行的行为。
"""
django2.X及以上版本 需要你自己指定外键字段的级联更新级联删除
"""
六. 数据库查询优化
6.1 单表查询优化
- only与defer
"""
only与defer
select_related与prefetch_related
orm语句的特点:
惰性查询
如果你仅仅只是书写了orm语句 在后面根本没有用到该语句所查询出来的参数
那么orm会自动识别 直接不执行
"""
#获取书籍表的所有书的名称
#01 正常写法
res = models.Book.objects.values('title')
for i in res:
print(i.get('title'))
res = models.Book.objects.all()
print(res)
for i in res:
print(i.title) #all不需要走了
#02 only写法
res = models.Book.objects.only('title') #得到的是个对象
for i in res:
print(i.title) #点击only括号内的字段 不会走数据库
print(i.price) #点击only括号内没有的字段 会重新走数据库查询而all不需要走了
#03 defer 写法
res = models.Book.objects.defer('title')
for i in res:
print(i.title)
print(i.price)
"""
defer与only刚好相反
defer括号内放的字段不在查询出来的对象里面 查询该字段需要重新走数据
而如果查询的是非括号内的字段 则不需要走数据库了
"""
6.2 连表查询优化
- select_related与prefetch_related
"""
select_related与prefetch_related
"""
## 查询书籍的出版社名称
#01 正常写法
res = models.Book.objects.all()
for i in res:
print(i.publish.name) #all() 每循环一次都会走一次数据库查询
#02 select_related 连表查询 写法
res = models.Book.objects.select_related('publish') #INNER JOIN 连表查询 只会查询一次数据库
for i in res:
print(i.publish.name)
"""
select_related内部直接先将book与publish连起来 然后一次性将大表里面的所有数据
全部封装给查询出来的对象
这个时候对象无论是点击book表的数据还是publish的数据都无需再走数据库查询了
select_related括号内只能放外键字段 一对多 一对一
多对多也不行
"""
#03 prefetch_related 子查询
res = models.Book.objects.prefetch_related('publish')
for i in res:
print(i.publish.name)
"""
prefetch_related该方法内部其实就是子查询
将子查询查询出来的所有结果也给你封装到对象中
给你的感觉好像也是一次性搞定的
"""
七. choices参数
7.1 数据库字段设计常见
"""
用户表
性别
学历
工作经验
是否结婚
是否生子
客户来源
...
针对某个可以列举完全的可能性字段,我们应该如何存储
只要某个字段的可能性是可以列举完全的,那么一般情况下都会采用choices参数
7.2 创建环境
#01 创建表
from django.db import models
class User(models.Model):
username = models.CharField(max_length=32)
age = models.IntegerField()
#性别元祖
gender_choices = (
(1,'男'),
(2,'女'),
(3,'其它'),
)
gender = models.IntegerField(choices=gender_choices)
"""
IntegerField 如果元素第一个是数字 则使用数字字段
该gender字段存的还是数字 但是如果存的数字在上面元祖列举的范围之内
那么可以非常轻松的获取到数字对应的真正的内容
1.gender字段存的数字不在上述元祖列举的范围内容
2.如果在 如何获取对应的中文信息
"""
#评级 字符串字段 保证字段类型跟列举出来的元祖第一个数据类型一致即可
score_choices = (
('A','优秀'),
('B','良好'),
('c','及格'),
('B','不合格'),
)
score = models.CharField(choices=score_choices,null=True,max_length=32)
makemigrations
migrate
#02 tests文件 测试数据
from django.test import TestCase
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day66.settings")
import django
django.setup()
from app01 import models
7.3 取choices数据
"""
只要是choices参数的字段 如果你想要获取对应信息 固定写法 get_字段名_display()
如果没有对应关系 那么字段是什么还是展示什么
"""
#01 创建数据
models.User.objects.create(username='付付',age=18,gender=2)
models.User.objects.create(username='宇宙',age=19,gender=1)
models.User.objects.create(username='东方不败',age=100,gender=3)
models.User.objects.create(username='金木',age=90,gender=4)
#02 获取choices 数据
obj = models.User.objects.filter(gender=1).first()
print(obj.get_gender_display())
#03 如果没有对应关系 那么字段是什么还是展示什么
obj = models.User.objects.filter(gender=4).first()
print(obj.get_gender_display())
##04 添加数据
models.User.objects.filter(gender=1).update(score='A')
models.User.objects.filter(gender=2).update(score='B')
models.User.objects.filter(gender=3).update(score='C')
models.User.objects.filter(gender=4).update(score='D')
#03 取数据
obj = models.User.objects.filter(score='A').first()
print(obj.get_score_display())
男
4
优秀
八. MTV与MVC模型
# MTV:Django号称是MTV模型
M:models
T:templates
V:views
# MVC:其实django本质也是MVC
M:models
V:views
C:controller
# vue框架:MVVM模型
九. 多对多三种创建方式