orm查询优化, MVC与MTV, choices参数, ajax

orm查询优化

惰性查询, 目的: 减少不必要的数据库操作, 降低数据库的压力

缓存数据库, redis, mongodb, 目的: 缓解主库压力, 相当于一个非常大的内存, 速度快同时也能做持久化

能少走一次数据库就少走一次

only与defer

only:

  1. only括号内放字段, 查询结果是一个列表套数据对象
  2. "对象.括号内的字段"不会再查询数据库, 直接获取属性值
  3. "对象.括号内没有的字段"会查询数据库获取结果
    queryset = models.Book.objects.only('title')

    # 查询1次数据库: SELECT "..."."id", "..."."title" FROM "..." ...
    print(queryset)  # <QuerySet [<Book: Book object>, ...]>,

    for obj in queryset:  # 查询1次数据库: SELECT "..."."id", "..."."title" FROM "..." ...
        
        print(obj.title)  # 不查询数据库: 红楼梦, ...

        # 一共查询7次数据库: SELECT "..."."id", "..."."price" FROM "..." WHERE "..."."id" = 1; ...
        print(obj.price)  # 111.00, ...

defer:

  1. defer与only互为反操作
  2. "对象.括号内的字段"会查询数据库获取结果
  3. "对象.括号内没有的字段"不会再查询数据库, 直接获取属性值
    queryset = models.Book.objects.defer('title')

    for obj in queryset:  # 查询1次数据库: SELECT "..."."id", "..."."price", "..."."publish_date", "..."."publish_id" FROM "...";

        # 一共查7次数据库: SELECT "..."."id", "..."."title" FROM "..." WHERE "..."."id" = 1;
        print(obj.title)  # 红楼梦, ...

        print(obj.price)  # 不查询数据库: 111.00, ...
  1. select_related括号内只能放外键字段,
  2. 并且外键字段只能是多对一或者一对一, 不能是多对多,
  3. 内部是自动联表操作, 会将括号中的外键字段所关联的表与当前表自动拼接成一张表,
  4. 然后将表中的一条条数据封装成一个个对象, 使跨表查询也不需要重复走数据库,
  5. 括号中可以放多个外键字段, 会将全部外键字段关联的表与当前表拼成一张大表
    queryset1 = models.Book.objects.all()

    # 查询1次数据库: SELECT "x"."id", "x"."title", "x"."price", "x"."publish_date", "x"."publish_id" FROM "x" ...
    print(queryset1)  # <QuerySet [<Book: Book object>, ...]>

    for obj in queryset1:  # 查询1次数据库: SELECT * FROM "app01_book" ...

        # 一共查询7次数据库: SELECT "app01_publish"."id", "y"."name", "y"."address" FROM "y" WHERE "y"."id" = 1; ...
        print(obj.publish.name)  # 如果改成: print(obj.title), 则不查询数据库

    queryset2 = models.Book.objects.select_related('publish')

    # 查1次数据库: SELECT "x".*, "x".* FROM "x" INNER JOIN "y" ON ("x"."publish_id" = "y"."id"); ...
    print(queryset2)  # <QuerySet [<Book: Book object>, ...]>

    for obj in queryset2:  # 查询1次数据库: SELECT "x".*, "x".* FROM "x" INNER JOIN "y" ON ("x"."publish_id" = "y"."id"); ...
        print(obj.title)  # 不查询数据库: 红楼梦, ...
        print(obj.publish.name)  # 不查询数据库: 东方出版社, ...
  1. 内部是子查询, 按步骤查询多张表, 然后将查询的结果封装到对象中, 给用户的感觉像是联表操作
  2. 括号中可以放多个外键字段(包括多对多关系), 每多放一个外键字段, 就会多走一条查询该外键字段关联表的sql语句
    queryset = models.Book.objects.prefetch_related('publish', 'author')

    '''
    查询3次数据库:
        1. SELECT "x"."id", "x"."title", "x"."price", "x"."publish_date", "x"."publish_id" FROM "x" ...
        2. SELECT "y"."id", "y"."name", "y"."address" FROM "y" WHERE "y"."id" IN (1, 2, ...); ...
        3. SELECT ..., "z".* FROM "z" INNER JOIN "w" ON ("z"."id" = "w"."author_id") WHERE "w"."book_id" IN (1, 2, ...); ...
    '''
    print(queryset)  # <QuerySet [<Book: Book object>, ...]>

    for obj1 in queryset:  # 查询3次数据库, sql语句同上
        print(obj1.publish.name)  # 不查询数据库: 东方出版社, ...
        
        # print(obj1.author.all().values_list('name'))  # <QuerySet [('jason',), ...]>, queryset对象获取字段值仍然会7次走数据库
        
        for obj2 in obj1.author.all():  
            print(obj1.title, obj2.name)  # 不查询数据库: 红楼梦 jason, ...

小结

selected_related: 耗时在数据库层面的联表操作上

prefetch_related: 耗时在查询的次数上

当两张表特别大时, selected_related花费时间可能更多

MTV与MCV模型

django自称为MTV框架, 本质还是MVC

MTV:

  • M: models, 数据库模型表
  • T: templates, html模板
  • V: views, 视图层(FBV | CBV)

MVC:

  • Model, 数据库模型
  • View: 处理数据的显示
  • Controller: 处理数据的交互

choices参数

一般用于性别, 学历等能完全列举出可能值的字段,

例如: 数据在数据库中按数字存, 取的时候按照提前设定的对应关系转换成真正的数据

migrations文件夹下必须要有一个__init__.py文件, 否则会报错: No changes detected

# models.py
class User(models.Model):
    ...
    gender_choice = (
        (1, '男'),
        (2, '女'),
        (3, '其他'),
    )
    gender = models.IntegerField(choices=gender_choice)


#test.py
user_obj1 = models.User.objects.get(pk=3)
user_obj2 = models.User.objects.get(pk=10)
print(user_obj1)  # User object

print(user_obj1.get_gender_display())  # 其他
print(user_obj2.get_gender_display())  # 4

'''
1. 包含choices参数的字段类型获取对应关系值的句式: get_字段名_display()
2. 弱限制: 如果存储的数字不在提前设定的对应关系中, 能够正常存储, 也能够正常获取, 获取的结果为表中实际结果
3. 对应关系中的第一个参数不一定是数字, 也可以是字符串
'''

ajax简介

XML

xml也是一门标记语言, 应用场景:

  • 写配置文件
  • 写前端页面
  • 应用于开发企业内部管理软件的框架odoo中
  • odoo框架内部功能实现全部依赖于python2, 其中薪资功能的实现最复杂

AJAX

最大的优点是在不重新加载整个页面的情况下, 可以与服务器交换数据并更新部分网页内容

同步交互: 客户端发出一个请求后, 需要等待服务器响应结束后, 才能发出第二个请求

异步交互: 客户端发出一个请求后, 无需等待服务器返回响应, 就能发出第二个请求

'''
# ajax POST请求发送普通键值对数据, 实现页面数据局部刷新
1. 页面上有三个input框
2. 前两个框输入数字, 并将输入的数字用post请求提交到后端, 最后一个框展示两数之和
3. 要求页面不刷新


def ajax(request):
    # print(request.is_ajax())  # 判断当前请求是否是ajax请求
    # print(request.POST)  # ajax发送的post请求, 普通的键值对也在request.POST中

    if request.is_ajax():
        d1 = request.POST.get('d1')
        d2 = request.POST.get('d2')
        res = int(d1) + int(d2)
        return HttpResponse(res)  # 结果返回给前端的异步回调函数, 一旦使用ajax请求, 数据只与回调函数交互

    return render(request, 'ajax.html')
    
    
<script>
    $('#d4').on(
        'click', function () {
            // 开启ajax语法的句式:
            $.ajax({
                url: '',  // 指定前端提交ajax POST请求到后端时使用的url, 和form表单中的action用法一样
                type: 'post',  // 控制请求提交的方式, 不写默认提交get请求
                data: {'d1': $('#d1').val(), 'd2': $('#d2').val()},  // 指定前端提交到后端的数据
                success: function (data) {  // data为异步提交数据后, 后端返回给前端的对该数据的处理结果
                    $('#d3').val(data)  // 前端的回调函数处理后端的返回结果
                }
            })

        }
    )
</script>
'''

前后端数据交互的编码格式

前端向后端发送请求的途径

a标签的href参数: 只能发送GET请求

form表单: 可以发送GET| POST请求, 但发送的POST请求数据不能是json格式

ajax: 可以发送GET| POST请求, 发送的post请求数据默认编码格式为application/x-www-form-urlencoded

GET请求数据拼接在url后面: url?x=a&y=b

post请求数据的三种编码格式

  • application/x-www-form-urlencoded, multipart/form-data, application/json
  • 前端查看POST请求数据编码方式: f12-->Network-->Headers-->Request Headers-->Content-Type
  • 前端查看POST请求数据: f12-->Network-->Headers-->Form Data-->view source
  • django针对...-form-urlencoded编码格式的POST请求数据, 会自动解析并封装到request.POST中
  • django针对multipart/formdata编码格式的POST请求数据, 会将满足...-form-urlencoded编码格式的数据解析到request.POST中, 将文件数据解析到request.FILES中
  • 发送json格式数据只能借助ajax
  • 在涉及前后端交互时, 要做到数据的格式与数据的编码格式一致

ajax POST请求发送json格式的数据

'''
<script>
    $('#d4').on(
        'click', function () {
            $.ajax({
                url: '', 
                type: 'post',  
                contentType: 'application/json',  // 指定ajax POST请求数据的编码格式
                data: JSON.stringify({'username': 'jason', 'password': '123'}),  // 前端数据使用JSON.stringify序列化为json格式
                success: function (data) {  
                    alert(data.username)  
                }
            })
        }
    )
</script>


def ajax(request):
    from django.http import JsonResponse
    import json

    if request.is_ajax():
        json_bin = request.body  # 获取json格式二进制数据
        json_str = json_bin.decode('utf8')  # 解码
        user_dic = json.loads(json_str)  # 反序列化
        return JsonResponse(user_dic)  # JsonResponse会自动将后端的字典转换成前端的object类型数据

    return render(request, 'ajax.html')
'''

ajax POST请求发送文件格式的数据

ajax传文件需要借助JavaScript中的内置对象

JavaScript实例化对象的关键字: new

FormData类的对象既可以携带文件数据, 也支持携带普通的键值对

'''
<script>
    $('#d1').on(
        'click',
        function () {
            var myFormData = new FormData();  // 实例化得到myFormData对象
            myFormData.append('username', 'jason');  // 添加普通键值数据
            myFormData.append('girl_img', $('#d2')[0].files[0]);  // 获取input框中文件对象, 并添加到myFormData对象中
            $.ajax({
                ...,
                data: myFormData,  
                contentType: false,  // 不使用myFormData对象内部自带的编码, 类似于json.dumps自动转码中文
                processData: false,  // 设置浏览器不处理发送的数据
                success: function (data) {
                    console.log($('#d2')[0].files);  // FileList {0: File, length: 1}
                    console.log($('#d2')[0].files[0]);  // File {name: "666.jpg", ..., size: 35122, type: "image/jpeg"  }
                    alert(data.username);
                }
            })
        }
    )
</script>


def ajax(request):
    if request.is_ajax():
        print(request.POST)  # <QueryDict: {'username': ['jason']}>
        print(request.FILES)  # <MultiValueDict: {'girl_img': [<...: 666.jpg (image/jpeg)>]}>
	...
'''

django内置的序列化模块

序列化的目的: 将数据整合成一个大字典的形式, 使前后端分离之后能方便地进行数据交互

def my_serialize(request):
    from django.core import serializers

    user_queryset = models.User.objects.all()

    res = serializers.serialize('json', user_queryset)  # json为要序列化成的数据格式
	print(res, type(res))  # [{"model": "app01.user", "pk": 1, "fields": {"name": "nick", "gender": 1}}, ...] <class 'str'>
    
    return HttpResponse(res)
原文地址:https://www.cnblogs.com/-406454833/p/11983979.html