Q查询进阶惰性查询 ORM查询优化 ORM事物操作 ORM常用字段及字段参数 Ajax请求

Q查询进阶操作

from django.db.models import Q
q_obj = Q()  # 1.产生q对象
q_obj.connector = 'or'  # 默认多个条件的连接是and可以修改为or
q_obj.children.append(('pk', 1))  # 2.添加查询条件
q_obj.children.append(('price__gt', 2000))  # 支持添加多个
res = models.Book.objects.filter(q_obj)  # 查询支持直接填写q对象
print(res)

查询条件的左侧 能够将查询条件的左侧由变量名或字段名变字符串

image

先用Q产生一个对象 而Q其实就一个类 类加括号就是在实例化对象 那么q_obj就成查询条件了 默认是and关系

image

那么也支持改成or关系

image

这样就可以实现与用户交互 前端返回的是字符串类型 就可以编写简易的搜索框查询功能

image

ORM查询优化

1.ORM的查询默认都是惰性查询
2.ORM的查询自带分页处理
3.only与defer
	'''数据对象+含有指定字段对应的数据'''
    # 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查询
        
    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查询
4.select_related与prefetch_related
	 # 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查询

    res = models.Book.objects.prefetch_related('publish')  # 子查询
    for obj in res:
        print(obj.publish.name)

1.ORM的查询默认都是惰性查询

image

如上图中res都是灰色 没有执行 针对查询 只要不用 就不会执行这样就可以节省效率

用了就会执行

image

2.ORM的查询自带分页处理

当在查数据比较庞大的时候 假如有几亿条数据 那么在查询的时候就得做分页处理 ORM中查询是默认分页

image

PS:以后自己在写SQL的时候 在对一张表做查询 表数据比较庞大的时候 为防止崩溃 最好是加上limit做防范

3.only与defer

only

all就是把表里面所有的数据都封装到对象里面去 然后对象去点里面的数据就不用再走数据库查询

只执行了一条sql语句

image

那如果只想要书和价格呢?

用values拿指定字段对应的数据 那么得到的是列表套字典 并不能拥有对象点的方式

想要i能够点书名和价格 结果报错 手里拿的字典没法点 报错

image

多个对象里面含有只有指定的多个字段数据 其他的没有 这时候就要用到only了

就是all与values结合 既拥有的all对象点的方式也拥有了values拿到指定字段的数据

如图得到的就是列表套对象

image

就能拿到指定的字段数据 点only括号内的不走sql

SQL也只执行了一次

image

点括号外的走SQL

image

将only括号里面的数据封装到对象里面 当在点括号里面的数据是不会走SQL查询 而如果是点的是括号里没有填写的字段 可以拿到数据 但每点一次 都会执行一次SQL数据库查询

image

defer

defer与only相反 括号内的走sql 括号外的不走

点defer括号里面的 就会走SQL查询

image

点defer括号外的其他字段 就不走SQL查询

image

总结

1.only与defer
only会将括号内填写的字段封装到数据对象中 后续获取不走SQL但是获取括号内没有的字段数据则需要走SQL
defer与only恰巧相反

如下图的查询是把书籍的数据封装到了对象中 书籍对象点其他除书以外其他的字段就每点一次就会得执行一次SQL查询

image

如何优化查询次数 以及查询时间呢?
用到select_related 连表查询

image

提前封装好了 就不用再走SQL查询了 也只走一句

image

不允许填写多对多虚拟字段 只能是一对一 一对多

否则报错

image

外键字段不允许填写多对多虚拟字段 只能是一对一或一对多

    res = models.Book.objects.prefetch_related('publish')  # 子查询
    for obj in res:
        print(obj.publish.name)

把书所有的书籍先查出来基于书里面的publish_id再到出版社里面把对应数据查找出来

只会执行两条SQL 先执行一条把这一条的结果当做另外一条的条件 用的时候感觉不出来

image

总结

select_related与prefetch_related
select_related括号内填写一对多、一对一字段 自动连表然后继续数据封装
prefetch_related括号内填写一对多、一对一字段 基于子查询然后封装数据

ORM事物操作

"""
1.事务的四大特性(ACID)
	原子性、一致性、隔离性、持久性
2.相关SQL关键字
	start transaction;
	rollback;
	commit;
	savepoint;
3.相关重要概念
	脏读、幻读、不可重复读、MVCC多版本控制...
"""
django orm提供了至少三种开启事务的方式
	方式1:配置文件数据库相关添加键值对		全局有效
       "ATOMIC_REQUESTS": True每次请求所涉及到的orm操作同属于一个事务
	方式2:装饰器							局部有效
       from django.db import transaction
       @transaction.atomic
       def index():pass	
 	方式3:with上下文管理					局部有效
       from django.db import transaction
    	def reg():
    		with transaction.atomic():
             pass

三种开启事物的方式

settings配置中有 网址记录了数据库相关操作
image

方式1:配置文件数据库相关添加键值对

       "ATOMIC_REQUESTS": True每次请求所涉及到的orm操作同属于一个事务

全局事物开启 全局有效

默认每一次请求所涉及到的orm操作同属于一个事务

要么都成功要么都失败

image

如下报错就会回滚

image

极限情况 :

当返回的不是一个response对象时 也是会执行成功的

但会事物而言只要到执行到return就成功 即使会报错 表数据也添加成功了

image

方式2:装饰器

       from django.db import transaction
       @transaction.atomic
       def index():pass	

需要导一个transaction模块

局部有效 局部开启针对某个视图函数 image

被装饰器修饰的视图函数就属于一个事物

image

表数据添加成功

image

极限情况:

当返回的不是一个response对象时 也是会执行成功的

image

image

3.with上下文管理

局部有效 也是需要导一个transaction模块

image

with里面的报错自动回滚

如下图中在with里面报错就会自动回滚

image

添加成功:

image

ORM常用字段

AutoField
	primary_key=True
CharField
	max_length
IntegerField
BigIntergerField
DecimalField
	max_digits decimal_places
DateField
	auto_now auto_now_add
DateTimeField
	auto_now auto_now_add
BooleanField
	传布尔值自动存0或1
TextField
	存储大段文本
EmailField
	存储邮箱格式数据
FileField
	传文件对象 自动保存到提前配置好的路径下并存储该路径信息

image-20221219104113630

什么文件都可以

image-20221219104245842

支持自定义字段类型

ORM还支持用户自定义字段类型
	class MyCharField(models.Field):
        def __init__(self, max_length, *args, **kwargs):
            self.max_length = max_length
            super().__init__(max_length=max_length, *args, **kwargs)

        def db_type(self, connection):
            return 'char(%s)' % self.max_length


    class User(models.Model):
        name = models.CharField(max_length=32)
        info = MyCharField(max_length=64)

字段类型都是一个个的类

image

重新改写继承

image

image

ORM字段类型与原生SQL的对应


对应关系:
    'AutoField': 'integer AUTO_INCREMENT',
    'BigAutoField': 'bigint AUTO_INCREMENT',
    'BinaryField': 'longblob',
    'BooleanField': 'bool',
    'CharField': 'varchar(%(max_length)s)',
    'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
    'DateField': 'date',
    'DateTimeField': 'datetime',
    'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
    'DurationField': 'bigint',
    'FileField': 'varchar(%(max_length)s)',
    'FilePathField': 'varchar(%(max_length)s)',
    'FloatField': 'double precision',
    'IntegerField': 'integer',
    'BigIntegerField': 'bigint',
    'IPAddressField': 'char(15)',
    'GenericIPAddressField': 'char(39)',
    'NullBooleanField': 'bool',
    'OneToOneField': 'integer',
    'PositiveIntegerField': 'integer UNSIGNED',
    'PositiveSmallIntegerField': 'smallint UNSIGNED',
    'SlugField': 'varchar(%(max_length)s)',
    'SmallIntegerField': 'smallint',
    'TextField': 'longtext',
    'TimeField': 'time',
    'UUIDField': 'char(32)',

ORM字段参数

外键多了就增强了耦合度 就不一样解耦合 所以外键一般是靠代码逻辑来建立

primary_key 	主键
verbose_name	注释
max_length		字段长度
max_digits     小数总共多少位
decimal_places	小数点后面的位数
auto_now		每次操作数据自动更新事件
auto_now_add	首次创建自动更新事件后续不自动更新
null			允许字段为空
default			字段默认值
unique			唯一值
db_index		给字段添加索引
choices			当某个字段的可能性能够被列举完全的情况下使用
	性别、学历、工作状态、...
	class User(models.Model):
        name = models.CharField(max_length=32)
        info = MyCharField(max_length=64)
        # 提前列举好对应关系
        gender_choice = (
            (1, '男性'),
            (2, '女性'),
            (3, '其他'),
        )
        gender = models.IntegerField(choices=gender_choice,null=True)
    user_obj = User.objects.filter(pk=1).first()
    user_obj.gender 
    user_obj.get_gender_display()
    
to				关联表
to_field		关联字段(不写默认关联数据主键)
on_delete		当删除关联表中的数据时,当前表与其关联的行的行为。
	 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相似

choices

当个某一个字段能够被完全的列举出来时

image

image

除里面指定的填啥就是啥

如下图填写的4就是4了 而1 2 3指定的就是男性 女性 其他

image

固定语法get_xxx_display()

多对多三种创建方式

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)

告诉ORM那个表是来维护书与作者表的多对多的关系 其中through_field中的字段顺序是不能乱(在书表为基表中创建的外键那么字段名book记得在前)

image

Ajax请求

异步提交 局部刷新

场景:在没有点提交 都会自动校验

搜索引擎根据用户输入的关键字,自动提示检索关键字。

还有一个很重要的应用场景就是注册时候的用户名的查重。

其实这里就使用了AJAX技术!当文件框发生了输入变化时,使用AJAX技术向服务器发送一个请求,然后服务器会把查询到的结果响应给浏览器,最后再把后端返回的结果展示出来。

整个过程中页面没有刷新,只是刷新页面中的局部位置而已!
当请求发出后,浏览器还可以进行其他操作,无需等待服务器的响应!

image

Async就是异步的意思

ajax不是一门新的技术并且有很多版本 我们目前学习的是jQuery版本(版本无所谓 本质一样就可以)

	$.ajax({
        url:'',  // 后端地址 三种填写方式 与form标签的action一致
        type:'post',  // 请求方式 默认也是get
        data:{'v1':v1Val, 'v2':v2Val},  // 发送的数据
        success:function (args) {  // 后端返回结果之后自动触发 args接收后端返回的数据
         	 $('#d3').val(args)
                                }
    })
	
使用ajax交互 那么后端返回的数据会被args接收 不再直接影响整个浏览器页面

jQuery发送AJAX请求示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <style>
        .hide {
            display: none;
        }
    </style>
</head>
<body>
<p><input type="text" class="user"><span class="hide" style="color: red">用户名已存在</span></p>

<script src="/static/jquery-3.3.1.min.js"></script>
{#下面这一项是基于jQuery的基础上自动给我们的每一个ajax绑定一个请求头信息,类似于form表单提交post数据必须要有的csrf_token一样#}
{#否则我的Django中间件里面的校验csrf_token那一项会认为你这个请求不是合法的,阻止你的请求#}
<script src="/static/setup_Ajax.js"></script>
<script>
    //给input框绑定一个失去焦点的事件
    $('.user').blur(function () {
        //$.ajax为固定用法,表示启用ajax
        $.ajax({
            //url后面跟的是你这个ajax提交数据的路径,向谁提交,不写就是向当前路径提交
            url:'',
            //type为标定你这个ajax请求的方法
            type:'POST',
            //data后面跟的就是你提交给后端的数据
            data:{'username':$(this).val()},
            //success为回调函数,参数data即后端给你返回的数据
            success:function (data) {
                ret=JSON.parse(data);
                if (ret['flag']){
                    $('p>span').removeClass('hide');
                }
            }
        })
    });
</script>
</body>
</html>

如下图 在点提交让它不刷新 原来的数据还在

image

必须要导:

image

image

image

写js代码

image

异步回调函数

image

数据编码格式(Content-Type)

在django中,针对前后端传输数据的编码格式,我们主要研究的是post请求;因为get请求传输的数据往往是直接放在url的后面的!

ost请求传输数据的编码格式时,我们需要先了解,可以朝后端发送post请求的方式有:
1.form表单中的method可以发送post请求
2.ajax中type可以指定post请求
前后端传输数据的编码格式有:
1.urlencoded
name=jason&age=123&hobby=read
request.POST\request.GET
2.formdata
request.POST request.FILES
3.application/json
request.body

form表单

form表单能够指定数据传输的编码格式只有两种,分别是默认urlencoded 和在form表单里指定enctype="multipart/form-data"的formdata格式,无法发送json格式数据

ps:

django针对contentType为urlencoded的编码格式,会自动帮你解析封装到request.POST中,所有我们才能在后端通过request.POST.get的方式进行获取前端传来的数据。

针对contenType为formdata的编码格式,前端传输过来的普通键值对还是会被封装到request.POST中,对于文件则解析封装到request.FILES中

几种数据编码格式介绍:

1.urlencoded

他是ajax默认的编码格式、form表单默认也是urlencoded
数据格式  xxx=yyy&uuu=ooo&aaa=kkk
django后端会自动处理到request.POST中

2.formdata

(即multipart/form-data,具体知识点参考request对象获取文件)
	django后端针对普通的键值对还是处理到request.POST中 但是针对文件会处理到request.FILES中

3.application/json

form表单不支持 ajax可以
使用ajax的时候网页返回的数据就只会被ajax中的形参接收,我们就不能设置网页跳转了

ajax发送json格式数据

1.确保data对应的数据是json格式字符串
data:JSON.stringify({})
2.修改数据编码格式(默认是urlencoded)
contentType:'applicaton/json'

	<script>
    $('#d1').click(function () {
        $.ajax({
            url:'',
            type:'post',
            data:JSON.stringify({'name':'jason','age':18}),  // 千万不要骗人家 转换成json字符串格式
            contentType:'application/json',  //一定要指定格式
            success:function (args) {
                alert(args)
            }

        })
    })
</script>
	后端需要从request.body中获取并自己处理

ajax携带文件数据

html
<script>
    $('#d3').click(function () {
        // 1.先产生一个FormData对象
        let myFormDataObj = new FormData();
        // 2.往该对象中添加普通数据
        myFormDataObj.append('name', 'jason');
        myFormDataObj.append('age', 18);
        // 3.往该对象中添加文件数据
        myFormDataObj.append('file', $('#d2')[0].files[0])
        // 4.发送ajax请求
        $.ajax({
            url:'',
            type:'post',
            data:myFormDataObj,

            // ajax发送文件固定的两个配置
            contentType:false,  // 告诉jQuery不要去设置Content-Type请求头
            processData:false, //告诉jQuery不要去处理发送的数据
            success:function (args){
                alert(args)
            }

        })
    })
</script>

views
def ab_ajax_func(request):
    if request.method == 'POST':
        print(request.POST)
        print(request.FILES)
        这里就可以看到文件的数据了
    return render(request, 'abAjaxPage.html')
	
	
总结:
ajax携带文件数据
let myFormObj = new FormData();
myFormObj.append('name','jason')
myFoemObj.append('myfile',标签对象.files[0])
data:myFormObj
contentType:false
processData:false

image

posted @ 2022-12-19 17:48  小福福  阅读(33)  评论(0编辑  收藏  举报