周总结12

周总结12

学习内容:

django路由层 django模板层,django模型层

路由层

主要是用来做路由 匹配调用对应的视图函数。

匹配的方法有多种正常的就是 path第一个内容填写的是路由,第二个内容填写的是视图函数,当用户通过访问这个路由 给服务端发送get请求或post请求服务端就可以根据请求的不同做出不同反馈。

转换器的使用方法

有一下几种比较常用的转换器

str   匹配除路径分隔符外的任何非空字符
int  匹配0或任意正整数
slug:匹配任意一个由字母或数字组成的字符串。
uuid:匹配格式化后的UUID。
path:能够匹配完整的URL路径

也称之为动态匹配

使用方法

path('index/<str:info>/',views.index_func)
info会自动把str匹配到的字符数据当做参数传给视图函数 我们可以利用此参数做一系列的操作

正则匹配

我们不光可以使用转换器匹配,也可以使用正则表达式进行匹配。前提是需要导入re_path模块。注意(django 1版本是不需要导入模块的,默认是正则),为了避免匹配成功后立刻停止其他匹配。我们可以在正则中加上开头与结尾的匹配

re_path('^test/$',views.test)
必须匹配到test/ 才可以执行对应的视图函数

django 1中的匹配方法时 url()

也可以对匹配进行无名分组合有名分组

无名分组与有名分组

在多组正则表达式情况下并不会报错

re_path('^test/\d{4}/',views.test)

但是使用无名分组后就会报错,因为分组意味着把分组内的正则匹配到的内容会当做位置参数传给视图函数。此时视图函数需要加一个形参。

re_path('^test/(\d{4})/',views.test)

有名分组与无名分组不同的是会用名字当做变量名 把匹配到的内容当做关键字参数传参给视图函数

re_path('test/(?P<year>\d{4}/)',views.test)

注意:

无名分组与有名分组不能混合使用。

反向解析

反向解析就是利用给路由起别名在前端在直接使用别名就可以访问到该路由。此时路由可以随意更改,都能通过别名访问到。

前端的语法

{% url 'login_view' %}

后端语法

reverse('login_view')

路由层语法

path('func1/', views.func1_func, name='func_view')

动态路由的反向解析

动态路由是根据动态匹配的结果 我们提前给他定义好,就会把我们想要的数据当做参数帮助我们传给视图函数。

使用方法

path('func1/<str:others>/', views.func1_func, name='func_view')
使用转换器进行动态匹配,并把others当做参数名

前端语法

{% url 'func1_view' 'jason' %}

后端语法

reverse('func1_view', args=('嘿嘿嘿',))  

路由分发

使用路由分发整合多个app的路由,每个app的路由名应该不同,此时只需要在总路由层按照应用名分配 匹配关系。

总路由层
"关键字 include"
path('应用名/',include('应用名.urls'))  
"应用名根据具体业务来起名" 

虚拟环境的创建

不同项目可能使用到的环境也不同我们需要根据项目的不同切换不同的环境,用虚拟环境比较方便。可以创建多个虚拟环境

创建虚拟环境的方法

pycharm 中 
点击File 选择New porject 

cmd方式

注意: 不支持多版本共存需要调整环境变量顺序或者进入 该版本的python目录下

1.创建虚拟环境
python -m venv xuni1  # 没有提示就说明创建成功
2.激活
# 进入该xuni1 文件目录下的Scripts 进行激活
	activate 输入该指令自动进入xuni1 环境下
 # 有多个activate版本对应不同的系统

3.关闭
deactivate 输入该指令退出

视图层

三板斧详解

研究视图层的三板斧源码发现其render HttpResponse redirect这三板斧其实内部都是返回了一个HttpResponse对象。

JsonResponse对象

不需要用json进行序列化后传值,直接可以用这个模块直接传

from django.http import JsonResponse
def json_dict(request):
    user_dict = {'name':'liaji李阿鸡','age':18,}
    return JsonResponse(user_dict)
但是自动把中文转码了
return JsonResponse(user_dict,json_dumps_params={'ensure_ascii':False})

request对象获取文件

request对象在使用form表单的input标签设置好对应的name值 并在form表单里添加method属性(post,get)默认是get属性。

我们就可以在视图函数内通过request.GET或request.POST.input标签name值 获取到input标签用户输入的值。进行一系列操作

获取文件比较特殊

form表单携带文件类型的数据 需具备下列条件
1. form表单的method必须是post
2. form表单的enctype 必须是form-data
 <form action="" method="post" enctype="multipart/form-data">
django后端需要通过request.FILES获取文件类型的数据

FBV与CBV

FBV

FBV就是我们现在所用的 在视图层里编写函数。称为: 基于函数的视图

CBV

基于类的视图

# CBV 路由层写法
path('login/',views.MyloginView.as_view())


# 视图层写法
class MyloginView(views.View):
def get(self,request):   # 编写跟请求方法相同的 功能
    return HttpResponse('get 方法')
def post(self,request):  # 编写跟请求方法相同的 功能
    return HttpResponse('post 方法')
# CBV自动判断请求方式并执行   

CBV源码剖析

了解CBV如何自动判断请求方式

首先想要了解一个源码就要找准切入口
因为视图层中的类继承的view 我们可以理解成一个父类
从路由层的as_view()这里我们看不懂 就从这里开始查看源码
1.路由层中使用MyloginView来点as_view这个名字,我们回想名称空间的知识点类(对象)点一个名字的查找顺序  对象自身名称空间-产生对象的类的名称空间-产生类的父类的名称空间 我们查看我们写的类并没有这个名字
    as_view() 可以想象要么是个无参函数,要么是个绑定给类的方法,类来调把类当做参数传过去"函数名+()优先级最高 匹配到路由就会先执行as_view()"
我们查看as_view的源码发现是个绑定给类的方法 并且返回值是view
此时我们的path('login/',views.MyloginView.as_view()) 就会变成
('login/',views.view)  # 访问login/就会执行 views.view函数


2.研究view函数
        def view(request, *args, **kwargs):
            self = cls(**initkwargs) # cls是我们自己写的类,因为as_view是绑定给类的方法会自动把类当做参数传过来,类() 产生一个我们自己类的对象
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.setup(request, *args, **kwargs)
            if not hasattr(self, 'request'):
                raise AttributeError(
                    "%s instance has no 'request' attribute. Did you override "
                    "setup() and forget to call super()?" % cls.__name__
                )
            return self.dispatch(request, *args, **kwargs)  
			# self是我们自己编写的类产生的对象。
我们的自己编写的类产生的对象.dispatch 名字 按照查找顺序我们查找发现他在view类里是一个函数

3. 研究父类view 里的 disptch函数
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
    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)  # 用getaattr 反射方法 拿到我们对象中的请求方法  
"第一个参数我们的对象,第二个参数当前请求方法并转小写,最后一个参数是获取不到的报错信息"
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)  # getattr拿到方法后 并调用方法

模板层

模板语法之传值操作

模板语法

{{}}   # 主要与数据值相关
{%%}   # 主要与逻辑相关

注意:

django的模板语法是自己写的,与jinja2模块不一样。

模板语法的注释前端浏览器是无法查看的 {##}

传值操作

方法1:
return render(request, 'demo02.html', {'n1': name, 'a1': age})
这种传值方式很精准,但是数据如果过多的话书写麻烦

方法2:
return render(request,'demo02.html', locals()) 
将当前函数内的所有名字都传给页面,如果函数内部含有大量的逻辑代码名字过多 不使用的情况下比较浪费资源

模板语法的传值特性

  1. 基本数据类型都可以正常展示
  2. 文件对象也可以展示并调用文件的一些功能
  3. 函数名会自动加括号调用并将返回值展示到页面上(不支持额外传参)
  4. 类名也会自动括号调用
  5. 对象则不会加括号调用可以点他的方法
"针对可以加括号调用的名字模板语法都会自动加括号调用"

过滤器(内置函数)

{{i|add:1}}       # 给数字+1
{{s|add:' nb'}}    # 给字符拼接
{{l|length }}      # 统计字符串或列表的长度
{{s|slice:'1:4'}}   # 切割操作
{{s|truncatechars:3}} # 3个字符后... 三个点算一个字符  用在简介内
{{s|truncatewords:3}} # 按照空格来截取 第三个空格
{{time|date:'Y-m-d H:i:s'}}  # 把视图层传过来的时间(结构化) 转成格式化
{{file_size|filesizeformat }} # 单位换算
传标签默认是不识别的,需要修改safe参数 告诉他是安全的
{{ h1|safe }}
更多过滤器参考博客:
https://www.cnblogs.com/Dominic-Ji/articles/10982302.html

模板层之标签

if..elif..else

模板层也有if判断与python大体上相似

{% if 条件 %}
	<p>if 条件成立展示</p>
{% elif 条件1 %}
	<p>elif 条件成立展示</p>
{% else %}
 	<p> 上述条件不成立展示</p>
{% endif %}

不同的是在模板层使用if判断需要用模板语法{% %} 来写,并且要用 endif 结尾。条件可以自己编写也可以从视图层传。

for循环

{%for i in 数据集 %}
	<p>{{ i }}</p>
{% endfor %}
它有一个自带的对象 forloop 里面有些数据 请看下图
利用forloop编写if判断
{% for foo in li %}
    {% if forloop.first %}  # 判断第一次循环
        <p>勇士,欢迎进入大草原副本(拒绝绿),您有3次机会</p>
        {% elif forloop.last %} # 判断最后一次循环 
        <p>勇士,您的机会用完了,您绿了</p>
        {% else %}  # 不是第一次和最后一次那就是中间的
        <p>这一次你也失败了</p>
    {% endif %}
    {% empty %} # 如果传的数据是空的就会展示下p
      <p> 传的数据是空的 无法循环取值</p>
{% endfor %}

模板层取值

d1={'name':'liaji','age':18,'hobby':['read','music',{'a1':'哥布林森林','a2':'瘟疫之源'}]}
如上所示:
我们需要取到字典中hobby内列表里的字典a1的值
django模板层索引位也支持点的方式
<p>{{d1.hobby.2.a1}}</p>
哥布林森林

并且支持其别名 # 前提是必须在with里
<p>
    {{ d1.hobby.2.a1}}
    {% with d1.hobby.2.a1 as g %}
        <p style="color: hotpink">{{ g }}</p>
    {% endwith %}
</p>

自定义过滤器

准备工作

自定义一些模板语法 需完成下列的三步操作

  1. 在应用目录下创建一个名字必须叫templatetags的目录.
  2. 在上述目录下创建任意名称的py文件
  3. 在上述py文件内先编写两行固定的代码
from django import template
register = template.Library()

开始操作

自定义过滤器

研究内置过滤器发现 他们都是一个函数并且用一个类似语法糖 装饰了
@register.filter(is_safe=False)

我们编写一个add功能
@register.filter(name='myadd')
def func1(a,b):
    return a+b

在前端使用时需要提前导入py文件
{% load my %}
<p> {{ i|myadd:41 }}</p>

自定义标签

@register.simple_tag(name='mytag')
def func2(a,b,c,d,e):
    return f'{a}{b}{c}{d}{e}'

{% load my %}
{% mytag 'h' 'e' 'l' 'l' 'o'%}

hello

自定义inclusion_tag

局部的html代码
与前两种方法不一样的是 他是先把函数的返回值传给一个页面后渲染后,然后再把页面放到调用inclusion_tag的位置。

1.需要我们提前创建好一个空的html内部清空。# menu.html
2.再去templatetags的目录下创建的py文件内编写函数
@register.inclusion_tag('menu.html',name='mymenu')
def func3(n):  # 传一个数值过来
    html = []
    for i in range(n):  # 循环对应的数值此时
        html.append('<li>第%s个</li>'%i)  
        # 把每次循环 li标签添加到空列表里
    return locals()

menu.html文件内 # 先把函数的返回值传给一个页面后渲染后
<ul>
    {% for liStr in html %}   # 循环获取列表内的li标签
          {{ liStr|safe }}   # 展示 safe可以识别标签语法
    {% endfor %}

</ul>

前端页面(调用inclusion_tag的位置):
{% load my %}
{% mymenu 10 %}  # 穿一个数值回去

模板的继承

功能网页可以继承主站的一些样式,并可以通过   自定义一些板块与样式

方法1: 复制主站的代码
方法2: 模板的继承
   
  1. 在主站中使用block划定可以给子版修改的区域
{% block 区域名称 %} # 名称可以随便起
{% endblock %}
  1. 子板继承模板
 需要清空子文件
    {% extends '继承的html文件' %}
	{% block 区域名称 %}
	子板自己的内容
	{% endblock %}
    
# 模板中至少应该有三个区域
页面内容区、css样式区、js代码区
子板也可以继续使用模板的内容 
{{ block.super }} 在 {% block 区域名称 %} 下面填写

模板的导入

举个例子:
 如果有很多html文件都可以用到表单文件,我们可以先写一个html文件在其内部 写好form表单渲染后, 可以在别的地方直接导入这个模板 就可以使用内部的表单。

    {% include 'myform.html' %}

模型层

前期准备

我们的django项目运行后 会自动创建一个sqlite3的数据库,但是sqlite3数据库对时间字段不准确,有时候会出错。

所以我们一般习惯切换使用MySQL django orm,这些数据库并不会自动生成,所以我们要先准备好。

测试django某个功能层环境搭建

我们的django默认不能像我们之前编写py文件写好了之后可以运行测试,django一旦运行整个项目就全部运行起来了。

如果想要测试某个py文件要做一下操作

测试环境1
pycharm提供的python console
在pycharm界面底部

它有一个缺点,就是我们写过的代码无法保存。

测试环境2
在我们应用目录下创建一个新的py文件或者用自带的tests文件 
先去manage.py 拷贝前四行代码 
然后放在我们自己创建的py文件内或tests.py文件内
接着输入两行代码:
import django
django.setup()
接着导入models 就可以测试了

django orm底层还是SQL语句 我们是可以查看的
如果我们手上是一个QuerySet对象 那么可以直接点query查看SQL语句

print(res.query) res 接收语句对象的

如果想查看所有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',
        },
    }
}

ORM常用关键字

# 1.create() 创建数据并返回用户对象
res = models.user.objects.create(name='liaji',age=18)
print(res)  # 用户对象:liaji

# 2.filter()  根据条件筛选数据 结果是QuerySet 列表套对象[对象1,对象2]
models.user.objects.filter() # 默认拿全部
models.user.objects.filter(name='tank') # 单个条件筛选
models.user.objects.filter(name='tank',age=18) # 多个条件筛选

# 3.first()   last()   针对 获取到的列表套对象 拿到其中第一个或最后一个(也可以用索引但是不建议)
res = models.user.objects.filter().first()  # 拿到查询所有数据后的列表套对象里的第一个
res = models.user.objects.filter().last()   # 拿到查询所有数据后的列表套对象里的最后一个
res = models.user.objects.filter()[0]  # 也可以根据索引位拿到数据,但是如果超出会报错,报错不好。但是使用first,last filter查询超出范围的数据并不会报错
res = models.user.objects.filter(pk=100).first() # 返回None

# 4 update() 更新数据/批量更新 不输入默认更新全部
res = models.user.objects.filter().update()  # 批量更新
res = models.user.objects.filter(id=3).update(name='nxm')  # 根据id查询到数据后更新名字

# 5.delete() 单独删除/批量删除
res = models.user.objects.filter().delete()  # 删除全部
res = models.user.objects.filter(id=4).delete() # 根据条件删除对应数据

# 6 all()  查询所有数据
res = models.user.objects.all() # 结果也是QuerySet 列表套对象

# 7 values() 根据指定字段回去字段数据
res = models.user.objects.filter().values() # 结果也是 QuerySet 列表套字典
res = models.user.objects.all().values('name')   # 拿到所有的名字

 # 8 values_list() 根据指定字段获取数据 结果也是QuerySet 列表套元组
res = models.user.objects.all().values_list('name')
print(res)

# 9 distinct() 去重  根据多条数据全部一模一样的才能去重,主键就不能去重,可以按照别的字段筛选了在去重
res = models.user.objects.values('name').distinct()

# 10 order_by() 根据指定条件排序
res = models.user.objects.all().order_by('age')  # 默认升序
res = models.user.objects.all().order_by('-age') # 加 - 号降序
res = models.user.objects.all().order_by('id','age')  # 可以写多个,第一个排不出来按第二个排

# 11.get() 根据条件筛选数据并直接获取数据对象
res= models.user.objects.get(id=1)

# 12. exclude() 取反
res = models.user.objects.exclude(pk = 2,)

# 13.reverse()  颠倒顺序(被操作的对象必须是已经排过序的才可以)
res = models.user.objects.all()
res = models.user.objects.all().order_by('age')
res = models.user.objects.all().order_by('age').reverse()

# 14.count()  统计结果集中数据的个数
res = models.user.objects.all().count()

15.exists()  判断结果集中是否含有数据 如果有则返回True 没有则返回False
res = models.user.objects.all().exists()
res = models.user.objects.filter(pk=100).exists()

注意事项:

时间字段注意事项:
auto_now_add 	#  首次创建数据获取当时时间
auto_now  		# 每次操作数据都会自动更新时间

为了方便我们查询对象名,在模型层ORM内部使用双下str魔法方法获取对象名
   def __str__(self):
        return f'用户对象:{self.name}'

ORM执行SQL语句

有时候ORM的操作效率可能偏低,我们可以自己编写sql代码的.

方式1

res = models.User_info.objects.raw('select * from app01_user_info;')
注意结果是个数据集。可以使用List(res)展示出来,或者用for循环取出来

方式2

导入connection模块,内部封装了pymysql

from django.db import connection
cursor = connection.cursor()
cursor.execute('select * from app01_user_info;')
cursor.fetchall()

双下划线查询

queryset 对象可以无限制的点queryset对象的方法( fileter().values().filter() )一直点下去

# 查询年龄大于18的用户数据
res = models.User.objects.filter(age__gt=19) 
"ORM里不支持 大于小于号  使用gt代表大于 lt代表小于"

# 查询年龄小于38的用户数据
res = models.User.objects.filter(age__lt=18)

__gte=18  # 大于等于
models.User.objects.filter(age__gte=18)

__lte=18  # 小于等于
models.User.objects.filter(age__lte=18)


# 查询年龄是18或者28或者38的数据
age__in 
"支持成员运算符" 
models.User.objects.filter(age__in(18,28,38))


# 查询年龄在18到38范围之内的数据
age__range
"查询2个范围之内的数据需要使用元组或列表包起来(范围1,范围2)"
models.User.objects.filter(age__range=(18,38))

# 查询名字中含有字母j的数据
模糊查询 name__contains
models.User.objects.filter(name__contains='j')
"注意: 区分大小写的"

name__icontains # 不区分大小写
models.User.objects.filter(name__icontains='j')

 # 查询注册年份是2022的数据
 查询时间按照 年月日的 英文单词(月日需要在配置文件内转时区)
models.User.objects.filter(register_time__year=2022) 
关键字 功能
gt 大于
lt 小于
gte 大于等于
lte 小于等于
in 成员运算符查询18,或28
range 查询2个数据范围之间的数据
contains 模糊查询区分大小写
icontains 模糊查询不区分大小写

ORM外键字段的创建

首先需考虑Mysql的外键关系是怎么判断的

外键关系分为3种 一对多(外键建在多的一方)----多对多(统一建在第三张关系表内)----一对一(建在查询频率高的一方)

先要判断表与表中间的关系 采用换位思考原则

第一步
	创建基础表(书籍表,出版社表,作者表,作者详情表)
第二步
	确定表之间的外键关系
	1.外键字段可以直接建在查询频率较高的表内,多对多的外键字段ORM会自动帮你创建第三张关系表
   2.自己创建第三张关系表并创建外键关系

注意事项

针对一对多或一对一 的外键同步到表中之后自动加_id 的后缀
针对多对多不会再表中有展示,而是创建第三张表
字段功能 字段
小数字段 DecimalField(max_length=8,decimal_places='2') 总共8位数小数占两位
长数字字段名 BigIntegerField()
外键字段 ForeignKey()

创建外键的方法

创建外键的方法 
# 一对多
models.ForeignKey(to='表名',on_delete=models.CASCADE)  # 第二个属性是级联更新级联删除
"django1 默认外键字段都是级联更新删除,django2以上版本需要自己申明"

# 多对多
models.ManyToManyField(to='表名') # 不用申明级联更新,因为是单独的关系表

# 一对一
models.OneToOneField(to='表名',on_delete=models.CASCADE)

外键字段操作方法

外键字段相关操作
一对多
# 一对多 插入数据可以填写表中的实际字段名
models.Book.objects.create(title='极品公子',price=48.8,publish_id=1)
models.Book.objects.create(title='神墓',price=48.8,publish_id=2)

# 一对多 插入数据也可以填写表外键字段对应的表对象(注意并不是publish_id因为publish_id是表中的实际字段名)
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.create(title='遮天',price=58.88,publish=publish_obj) # 可以帮你自动拿到对象的主键值

 "一对一与一对多一致"
多对多

多对多的关系表因不是我们自己创建的所以使用models无法点出该表名

解决方法:

获取到含有对多对字段的数据对象来点其 多对多的外键名(虚拟字段)就可以直接访问到第三张表

book_obj = models.Book.objects.filter(pk=1).first()  # 获取一个id值为1的书籍对象
book_obj.authors.add(1)  # 给这个书籍对象添加id值为1的作者
book_obj.authors.add(2, 5)  # 给这个书籍对象添加多个作者

"也可先获取作者对象然后添加"
book_obj = models.Book.objects.filter(pk=2).first() # 获取一个id值为2的书籍对象
author_obj = models.Author.objects.filter(pk=1).first()# 获取一个id值为1的作者对象
author_obj1 = models.Author.objects.filter(pk=2).first()# 获取一个id值为2的作者对象
book_obj.authors.add(author_obj,author_obj1) # 给第三张关系表id值为2的书籍添加多个作者(id1作者,id2作者)
 
    
"修改关系 set"# 先删除在新增
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.authors.set((1,2)) 
# 用元组套起来,不然会报错,修改id值为1的书籍的作者为 id1 作者与id2作者,如果表内有多余的关系自动删除 
book_obj.authors.set((1,))"注意,一个数据需要加逗号。[1,]列表也可以"
# 把书籍对象的作者修改成id值为1的作者。 
"也可以获取作者对象 来进行修改 (作者对象,)"


'删除关系 remove'
book_obj = models.Book.objects.filter(pk=2).first()
book_obj.authors.remove(2,) # 删除与id2作者的关系,也可以删除多个(1,2)
"也可以获取作者对象进行删除"

"删除所有关系 clear"
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.authors.clear() # 删除这个书籍对象的所有关系

ORM的跨表查询

正反向查询的概念(重要)

正向查询
  • 由外键字段所在的表数据查询关联的表数据
反向查询
  • 没有关键字段的表数据查询关联的表数据

如何判断正反向查询:

  • 核心就看外键字段在不在当前数据所在的表。
  • 外键字段在谁手上 基于他查关联的表数据就是正向,从关联的数据表查询含有外键字段的表的数据 就是反向
ORM跨表查询口诀
"""
正向查询按外键字段查
反向查询按表名小写查
"""

基于对象的跨表查询

正向查询
1.查询主键为1的书籍对应的出版社名称
book_obj =models.Book.objects.filter(id=1).first()  # 先获取手上现有的条件主键为1的书籍对象
print(book_obj.publish.name)  # 直接使用对象点外键字段就可以拿到出版社对象在通过.name 方式拿到出版社名

2.查询主键值为5的数据对应的作者姓名
book_obj = models.Book.objects.filter(pk=5).first()
print(book_obj.authors)  # app01.Author.None 由于书和作者是多对多关系。可能会有很多个作者
print(book_obj.authors.all())  #< QuerySet [<Author: 作者对象:辰东>]>

# 3.查询作者辰东的电话号码
author_obj = models.Author.objects.filter(name='辰东').first()
print(author_obj.author_detail.phone)  
反向查询
关键语法"小写表名_set"
先只写表名观看是否报错 如果报错则使用_set
# 4.查询北方出版社出版过的书籍名称
publish_obj = models.Publish.objects.filter(name='北方出版社').first()
print(publish_obj.book_set.all())

# 5.查询辰东写过的书籍
author_obj = models.Author.objects.filter(name='辰东').first()
print(author_obj.book_set.all())  #<QuerySet [<Book: 书籍对象:神墓>, <Book: 书籍对象:遮天>, <Book: 书籍对象:剑来>]>

 # 6.查询电话号码是110的作者姓名
author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
print(author_detail_obj.author.name)
总结
当查询的结果可能返回多个对象使用all()获取,如何判断。点表名或字段名返回的是None

基于双下划线的跨表查询

正向查询
# 1.查询主键为1的书籍对应的出版社名称
res=models.Book.objects.filter(pk=1).values('publish__name')  # 外键字段__内部字段名
print(res)

# 2.查询主键为4的书籍对应的作者姓名
res = models.Book.objects.filter(pk=4).values('authors__name')
res= models.Book.objects.filter(pk=4).values('title','authors__name')
# 也可以同时拿到书名与作者名
print(res)

# 3.查询辰东的电话号码
res = models.Author.objects.filter(name='辰东').values('author_detail__phone')
print(res)
反向查询
# 4.查询北方出版社出版过的书籍
res = models.Publish.objects.filter(name='北方出版社').values('book__title')
print(res)

# 5.查询辰东写过的书籍
res = models.Author.objects.filter(name='辰东').values('book__title')
print(res)

# 6.查询电话号码是110的作者姓名
res = models.AuthorDetail.objects.filter(phone=110).values('author__name')
    print(res)

进阶操作

不准使用条件一进行查询。从后面往前面查询
 # 1.查询主键为1的书籍对应的出版社名称
res = models.Publish.objects.filter(book__id=1).values('name')
print(res)

# 2.查询主键为4的书籍对应的作者姓名
res = models.Author.objects.filter(book__id=4).values('name')
print(res)


# 3.查询辰东的电话号码
res = models.AuthorDetail.objects.filter(author__name='辰东').values('phone')
print(res)
# 4.查询北方出版社出版过的书籍
res = models.Book.objects.filter(publish__name='北方出版社').values('title')
print(res)
# 5.查询辰东写过的书籍
res = models.Book.objects.filter(authors__name='辰东').values('title')
print(res)
# 6.查询电话号码是110的作者姓名
res = models.Author.objects.filter(author_detail__phone='110').values('name')
print(res)

补充

# 查询主键为4的书籍对应的作者的电话号码
res = models.Book.objects.filter(pk=4).values('authors__author_detail__phone')  # 先拿到book对应的id4,然后基于作者外键字段__作者表里的详情外键字段__需要获取的值的字段名
print(res)

res= models.AuthorDetail.objects.filter(author__book__pk=4).values('phone')
# 基于电话表拿,筛选作者表里作者写的书id是4的  因为values里不能用=所以放在filter内
print(res)
    
res = models.Author.objects.filter(book__pk=4).values('author_detail__phone')
print(res)

聚合查询

聚合查询就是利用一些聚合函数来进行查询

常用聚合函数

关键字aggregate

函数名 功能
avg 平均值
max 最大值
min 最小值
sum 求和
count 计数

ORM中聚合函数可以单独使用 需要使用关键字来配合

只要跟模型层相关的模块都藏在

django.db 或者django.db.models里

"aggregate"
需要导入模块使用
from django.db.models import Max,Min,Sum,Count,Avg
res = models.Book.objects.aggregate(Max('price'))  # 单独使用需要用字符串的形式
res = models.Book.objects.aggregate(max_price= Max('price'))  # 可以起别名
res = models.Book.objects.aggregate(max_price= Max('price'),all_Count=Count('pk'),min_price=Min('price'),sum_price=Sum('price'))  #多个一起用,需要注意如果多个都起别名了,那么没有别名的应该放在前面

分组查询

关键字 annotate

查询的时候可能或报错原因是因为mysql的严格模式开启了,需要取消严格模式set global sql_mode='strict_trans_tables';在当前服务端有效

 1.统计每一本数的作者个数
res = models.Book.objects.annotate(shuliang=Count('authors__pk')).values('title','shuliang')
print(res)   
# 可以给分组起别名的方式同时拿到书名与对应的作者的数量

2 统计出每个出版社卖的最便宜的书的价格
res = models.Publish.objects.annotate(jiage=Min('book__price')).values('name','jiage')
print(res)

3.统计不止一个作者的书
思路: 先拿到每本书有几个作者。然后在进行条件筛选,filter放在前面相当于where 放在后面相当于having
res = models.Book.objects.annotate(shuliang=Count('authors__pk')).filter(shuliang__gt=1).values('title','shuliang')
print(res)

4查询出每个作者的出售的书籍总和
res = models.Author.objects.annotate(zonghe=Sum('book__price')).values('name','zonghe')
print(res)

注意:

models.表名.objects.annotate() 按照表分组

models.表名.oobjects.values(‘字段名’).annotate() 按照values括号内字段分组

# 查看每个出版社的书的总和
res = models.Book.objects.values('publish_id').annotate(zonghe=Count('pk')).values('publish_id','zonghe')
print(res)

F与Q查询

注意在模型层给表添加字段后执行迁移会提示需要默认值,需要在新增的字段后增加默认值

default=100 字段默认100数量
null=True 允许字段为空

查询数据不是明确的,但也需要从数据库中获取 需要使用F查询

F查询

from django.db.models import F
1.查询库存大于卖出数的书籍
res = models.Book.objects.filter(kucun__gt=F('shouchu'))
print(res)

2.将所有的书籍价格涨66
res = models.Book.objects.update(price=F('price')+66)
print(res)

将ID值为1的书籍名字后面追加爆款
与整型不同如果需要+名字的话那么需要使用模块
from django.db.models.functions import Concat  # 拼接
from django.db.models import Value #新的值
res = models.Book.objects.filter(pk=1).update(title=Concat(F('title'),Value('爆款')))
print(res)

Q查询

如果需要把filter括号内的and关系改成or 那么就需要使用Q查询

from django.db.models import Q
1.查询主键是1或者价格大于1000的书籍
res = models.Book.objects.filter(Q(pk=1) | Q(price__gt=100))
print(res)

用Q把条件包起来

Q关系功能符 功能说明
逗号隔开 and关系
‘|’ 隔开 or关系
~ not关系
posted @ 2022-12-18 15:06  李阿鸡  阅读(23)  评论(0编辑  收藏  举报
Title