• Django ORM-objects-QuerySet


    Django ORM

    ORM执行查看原生SQL的两种方法

    1.在setting中配置

                LOGGING = {
                    'version': 1,
                    'disable_existing_loggers': False,
                    'handlers': {
                        'console': {
                            'level': 'DEBUG',
                            'class': 'logging.StreamHandler',
                        },
                    },
                    'loggers': {
                        'django.db.backends': {
                            'handlers': ['console'],
                            'propagate': True,
                            'level': 'DEBUG',
                        },
                    }}
    

    2.如果查询的结果是queryset对象可以.query查看命令

    只要是queryset对象就可以无限制的点queryset对象的方法,queryset.filter().filter().filter()

    搭建django-ORM测试环境

    from django.test import TestCase
    import os
    # Create your tests here.
    if __name__ == "__main__":
        os.environ.setdefault("DJANGO_SETTINGS_MODULE", "zx1.settings")
        import django
        
    
        django.setup()
        from app01 import models
        # 你就可以在下面测试django任何的py文件
        
    

    res1=models.Book.objects.get(title='步天歌') 研究objects

    不知道你有没有发现,objects和Queryset方法有很多是一样的,Queryset有的方法objects都会有,而且他们还有一个相似的地方,设置方法是返回值都是一样,都是返回Queryset对象

    查看objects源码
    发现底层是c实现的,看不了,但是这里出现了熟悉的东西,它继承的类中有个QUerySet
    class Manager(BaseManager.from_queryset(QuerySet)):
        pass
        
    查看from_queryset源码,看它到底干了啥
    	@classmethod
        def from_queryset(cls, queryset_class, class_name=None):
        	#class为空执行if内的内容
            if class_name is None:
            	#这里拼接了字符串
                class_name = '%sFrom%s' % (cls.__name__, queryset_class.__name__)
            class_dict = {
                '_queryset_class': queryset_class,
            }
            #这一步很可疑,到底向这个字典里放了什么,看下一段代码,使用了BaseManager._get_queryset_methods();
            class_dict.update(cls._get_queryset_methods(queryset_class))
            #创建了一个新的类对象,继承BaseManager
            return type(class_name, (cls,), class_dict)
    
    查看_get_queryset_methods源码
    	#可以发现这部分代码比较难,还好它有官方的注释,只要看注释就大概知道这部分代码大概干了啥
    	只复制缺少的方法。
    	只复制公共方法或具有属性的方法
    	将方法复制到管理器上。
    	根据注释知道这部分就是把queryset中的方法提取出来的部分,最后把方法放入字典
        @classmethod
        def _get_queryset_methods(cls, queryset_class):
        	#这里的queryset_class其实就是Queryset类对象
            def create_method(name, method):
                def manager_method(self, *args, **kwargs):
                    return getattr(self.get_queryset(), name)(*args, **kwargs)
                manager_method.__name__ = method.__name__
                manager_method.__doc__ = method.__doc__
                return manager_method
    
            new_methods = {}
            # Refs http://bugs.python.org/issue1785.
            predicate = inspect.isfunction if six.PY3 else inspect.ismethod
            for name, method in inspect.getmembers(queryset_class, predicate=predicate):
                # Only copy missing methods.
                if hasattr(cls, name):
                    continue
                # Only copy public methods or methods with the attribute `queryset_only=False`.
                queryset_only = getattr(method, 'queryset_only', None)
                if queryset_only or (queryset_only is None and name.startswith('_')):
                    continue
                # Copy the method onto the manager.
                new_methods[name] = create_method(name, method)
            return new_methods
    
    

    objects总结

    根据上面的研究objects其实就是一个由Manager创建的一个实例对象,而且它有QuerySet的方法,所以它和QuerySet是没有继承一类的关系的

    接下来就是下一个问题了:既然objects和QuerySet的方法是一样的,那QuerySet是不是也可以查询数据库呢?

    其实我们没创建一个model,比如Book他就会默认创建一个objects,那么objects有表的一些信息也就不奇怪了,但是单独创建一个QuerySet对象,它是没有这些信息的,那么它是缺少数据的。

    QuerySet特性

    由此可见QuerySet对象的强大,我看了部分源码发现其实惰性查询也是它实现的,它的功能非常强大。

    queryset切片

    queryset的切片不是对结果集的切片而是直接对sql进行limit和offset

    res = models.Article.objects.all()[5:10] # (OFFSET 5 LIMIT 5)
    

    queryset惰性查询

    res = models.Article.objects.all()[5:10]
    不去打印或者使用res的话,数据是还没有的,只有使用res才会去执行sql语句拿到查询的数据
    print(res)的时候才会去查询数据
    

    queryset缓存机制

    下面的案例sql只查询了一次

        res = models.Article.objects.all()
        for i in res:
            print(i.title)
        for i in res:
            print(i.desc)
    

    但是这样的话,是执行了两次

        res = models.Article.objects.all()
        print(res)
        print(res)
    

    什么时候会触发缓存,什么时候不会触发?

    https://www.jb51.net/article/169274.htm

    get-first-last

    get

    因为框架默认返回的就是一个数据,如果条件是多个的就会报错

    get方法并不是惰性查询,而且返回的是表模型的实例,可以这样理解它并不是QuerySet对象,不是惰性就很正常

    res1=models.Book.objects.get(title='步天歌')
    

    first-last

    通过sql可以发现,如果拿第一个数据和最后一个数据,其实就是按主键进行排序,然后通过limit拿值

    (0.001) SELECT `app01_book`.`id`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`publish_date`, `app01_book`.`publish_id` FROM `app01_book` ORDER BY `app01_book`.`id` ASC LIMIT 1; args=()
    
    (0.000) SELECT `app01_book`.`id`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`publish_date`, `app01_book`.`publish_id` FROM `app01_book` ORDER BY `app01_book`.`id` DESC LIMIT 1; args=()
    

    filter

    惰性查询,我执行下面的代码的时候查询并没有执行,sql代码存在Queryset的内部了,这个时候我们可以看到sql是啥了

    res=models.Book.objects.filter(title='步天歌')
    print(type(res))
    print(res.query)
    
    <class 'django.db.models.query.QuerySet'>
    (0.001) SELECT @@SQL_AUTO_IS_NULL; args=None
    (0.000) SELECT VERSION(); args=None
    SELECT `app01_book`.`id`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`publish_date`, `app01_book`.`publish_id` FROM `app01_book` WHERE `app01_book`.`title` = 步天歌
    

    QuerySet内部的SQL优化

    下面我打印了每一个阶段的sql语句,安装我们一般的思路,如果打印了res2的话,这两条sql都会执行

        res=models.Book.objects.filter(title='步天歌')
        print(res.query)
        res2 = res.values("id")
        print(res2.query)
        
    (0.000) SELECT @@SQL_AUTO_IS_NULL; args=None
    SELECT `app01_book`.`id`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`publish_date`, `app01_book`.`publish_id` FROM `app01_book` WHERE `app01_book`.`title` = 步天歌
    (0.000) SELECT VERSION(); args=None
    SELECT `app01_book`.`id` FROM `app01_book` WHERE `app01_book`.`title` = 步天歌
    

    那么情况到底如何呢?

    最后只执行了最后的代码,说明QuerySet内部还进行了Sql代码的优化

    print(res2)
    
    (0.001) SELECT `app01_book`.`id` FROM `app01_book` WHERE `app01_book`.`title` = '步天歌' LIMIT 21; args=('步天歌',)
    
    

    filter和values

    我发现filter和values其实和sql中最简单的查询非常的相似

    filter就类似

    select * from modle.book where filter()
    

    values就类似

    select vlaues() from modle.book
    

    filter和values一起用就是这样了

    select values() from modle.book where filter()
    

    filter和values_list

    filter和values_list其实查询数据的sql语句是一样的,差别就是对拿来的数据进行了不同的处理,一个把记录包装成对象,另一个单纯把数据拿出来放在元组里面

    (0.000) SELECT `app01_book`.`id`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`publish_date`, `app01_book`.`publish_id` FROM `app01_book` LIMIT 21; args=()
    
    (0.001) SELECT `app01_book`.`id`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`publish_date`, `app01_book`.`publish_id` FROM `app01_book` LIMIT 21; args=()
    

    queryset方法详解

    https://www.jianshu.com/p/13d2ce796770

  • 相关阅读:
    运动世界校园破解2.0
    Docker进阶操作
    一键开启https
    Docker的第一次实践总结
    手机通话黑屏
    mysql安装、操作、配置、远程
    excel添加列数据导入后,列数据不显示的问题
    常见邮箱的各类协议服务器地址
    POP3/SMTP/IMAP等邮箱协议的基本概念
    You credentials did not work (The logon attempt failed)
  • 原文地址:https://www.cnblogs.com/zx125/p/11743515.html
Copyright © 2020-2023  润新知