zoukankan      html  css  js  c++  java
  • Django model对象接口

    Django model查询

    # 直接获取表对应字段的值,列表嵌元组形式返回
    Entry.objects.values_list('id', 'headline')
    #<QuerySet [(1, 'First entry'), ...]>
    from django.db.models.functions import Lower
    # 使用函数。对查询的结果进行处理,这里将对应字段headline的值全部转为小写。
    # 更多对结果处理对函数都在该模块内
    Entry.objects.values_list('id', Lower('headline'))
    # 查询的结果被处理了
    #<QuerySet [(1, 'first entry'), ...]>
    Entry.objects.values_list('id')
    # 对于单个字段的值的结果,默认是这样的结果,也是元组形式返回,通常这并不是你想要的结果
    #<QuerySet[(1,), (2,), (3,), ...]>
    
    # 使用关键字参数flat,就只能将单个字段的结果以列表形式直接返回了
    Entry.objects.values_list('id', flat=True)
    # <QuerySet [1, 2, 3, ...]>
    
    # 当有多个字段需要返回时,也可以以命名元组形式返回
    # 使用关键字参数named
    Entry.objects.values_list('id', 'headline', named=True)
    #<QuerySet [Row(id=1, headline='First entry'), ...]>
    
    # 当你需要只获取指定字段的值,可以使用get方法获取,前提是你能保证存在pk=1这条记录,否则会抛出异常
    Entry.objects.values_list('headline', flat=True).get(pk=1)
    # 这个显示就是pk为1的字段headline对应的值
    # 'First entry'
    
    # 使用values得到是字典形式的结果,也支持函数参数对值进行处理
    Blog.objects.values()
    # <QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>
    
    # 对指定key的结果转换为小写输出
    from django.db.models.functions import Lower
    Blog.objects.values(lower_name=Lower('name'))
    #<QuerySet [{'lower_name': 'beatles blog'}]>
    
    
    # 使用count方法将相同的字段数据进行计数统计
    from django.db.models import Count
    Blog.objects.values('author', entries=Count('entry'))
    # <QuerySet [{'author': 1, 'entries': 20}, {'author': 1, 'entries': 13}]>
    # 如果需要使用group by形式分组查询结果。
    Blog.objects.values('author').annotate(entries=Count('entry'))
    # <QuerySet [{'author': 1, 'entries': 33}]>
    
    # 当获取一个外键字段值时。如字段名称foo为外键字段。values查询时。使用foo 和foo_id是等价当效果
    Entry.objects.values()
    # <QuerySet [{'blog_id': 1, 'headline': 'First Entry', ...}, ...]>
    
    Entry.objects.values('blog')
    # <QuerySet [{'blog': 1}, ...]>
    
    Entry.objects.values('blog_id')
    # <QuerySet [{'blog_id': 1}, ...]>
    
    
    # 以下这种方式查询是等价的
    Blog.objects.values().order_by('id')
    Blog.objects.order_by('id').values() 
    
    # 合并查询结果
    qs1 = Author.objects.values_list('name')
    qs2 = Entry.objects.values_list('headline')
    qs1.union(qs2).order_by('name')
    
    # 合并多个查询结果
    qs1.union(qs2, qs3)
    
    # 返回交集查询结果
    qs1.intersection(qs2, qs3)
    
    # 返回差集查询结果
    qs1.difference(qs2, qs3)
    

    select_related()查询优化

    # 对于有外键关联的表查询。如果不使用select_related查询。那么最终只会查询单条没有任何关联的结果
    
    # 例如。获取ID为5的entry记录,第一次会查询一次数据库
    e = Entry.objects.get(id=5)
    
    # 当访问这条记录对应的外键关联字段时。还会再次查询数据库。显然。这不是我们想要的。我们希望查询上面的记录同时把对应的blog也一并查询
    b = e.blog
    
    
    # 使用这种方式。明确告知查询外键字段blog对应的实例对象结果
    # select_related不指定参数时。则获取所以与之外键关联的对象
    e = Entry.objects.select_related('blog').get(id=5)
    
    # 这时再去访问blog对象时。不会进行第二次数据库查询。
    b = e.blog
    
    
    # 下面两个语句。filter和select_related顺序先后都一样。没什么区别。
    Entry.objects.filter(pub_date__gt=timezone.now()).select_related('blog')
    Entry.objects.select_related('blog').filter(pub_date__gt=timezone.now())
    
    
    from django.db import models
    class City(models.Model): 
        # ...
        pass
    class Person(models.Model): 
        # ...
        hometown = models.ForeignKey( City,
        on_delete=models.SET_NULL, blank=True,
        null=True,
        )
    class Book(models.Model): 
        # ...
        author = models.ForeignKey(Person, on_delete=models.CASCADE)
        
    # 下面这种查询方式将会把City对象缓存起来。
    b = Book.objects.select_related('author__hometown').get(id=4)
    
    p = b.author  # 不从数据库查询获取,直接从上面查询的缓存获取结果
    c = p.hometown  # 不从数据库查询获取,直接从上面查询的缓存获取结果
    
    
    b = Book.objects.get(id=4)  # 没有使用select_related查询
    p = b.author  # 这时会从数据库查询获取结果
    c = p.hometown  # 这时会从数据库查询获取结果
    
    # select_related同样支持链式操作
    select_related('foo', 'bar')
    # 等价于
    select_related('foo').select_related('bar')
    
    

    prefetch_related()查询优化

    from django.db import models 
    class Topping(models.Model):
        name = models.CharField(max_length=30)
    class Pizza(models.Model):
        name = models.CharField(max_length=50) 
        toppings = models.ManyToManyField(Topping)
        def __str__(self): 
            return "%s (%s)" % (
                self.name,
            ", ".join(topping.name for topping in self.toppings.all()), 
            )
            
    class Restaurant(models.Model):
        pizzas = models.ManyToManyField(Pizza, related_name='restaurants') 
        best_pizza = models.ForeignKey(Pizza, related_name='championed_by', on_delete=models.CASCADE) 
    
    # 在大量查询集的情况下。没有进行优化。那么每次调用
    # Pizza.__str__()时(print会自动触发该方法),会每触发一次,查询一次数据库
    Pizza.objects.all()
    # ["Hawaiian (ham, pineapple)", "Seafood (prawns, smoked salmon)"...
    
    # 将上面的方式优化为下面这种
    Pizza.objects.all().prefetch_related('toppings')
    
    # 这时再调用self.toppings.all()将从查询缓存中获取数据。不是去查询数据库
    
    # 如果这时再对缓存数据进行子查询,那么依然会查询数据库。而不是从缓存过滤查询集
    pizzas = Pizza.objects.prefetch_related('toppings')
    # 下面的子句过滤会从数据库查询获取,这时的prefetch_related链式调用并没有效果,反而降低了性能,所以使用这个功能需要格外小心
    [list(pizza.toppings.filter(spicy=True)) for pizza in pizzas]
    
    # 
    Restaurant.objects.prefetch_related('pizzas__toppings')
    
    # 下面这种方式会产生三次查询
    Restaurant.objects.prefetch_related('best_pizza__toppings')
    
    # 应该优化写成这种形式,这样会优化查询次数为2次
    Restaurant.objects.select_related('best_pizza').prefetch_related('best_pizza__toppings')
    
    
    • select_related 和 prefetch_related。前者适用于单条数据的查询集缓存。后者使用于大的查询集缓存
    • 普通的foreign key用select_related,many to many用prefetch_related

    extra,扩展sql表达式,该功能会被遗弃,使用RawSQL替代

    # 使用这种方式要避免SQL注入攻击
    qs.extra(select={'val': "select col from sometable where othercol = %s"}, select_params=(someparam,),)
    # 与下面这种方式等价
    qs.annotate(val=RawSQL("select col from sometable where othercol = %s", (someparam,)))
    
    
    # 以查询结果方式展示,is_recent相当于查询的别名
    Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})
    
    # SQL等价于
    # SELECT blog_entry.*, (pub_date > '2006-01-01') AS is_recent FROM blog_entry
     
     
    Blog.objects.extra( select={
    'entry_count': 'SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id= blog_blog.id'
    }, )
    
    # 等价于
    
    # SELECT blog_blog.*, (SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id =blog_blog.id) AS entry_count FROM blog_blog;
    
    
    Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
    # SELECT * FROM blog_entry WHERE (foo='a' OR bar='a') AND (baz='a')
    
    
    # 对需要排序的查询。需要使用正确的查询字段
    q = Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"}) 
    q = q.extra(order_by = ['-is_recent'])
    
    # 对于参数查询,推荐使用这种查询方式
    Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
    # 不推荐使用下面这种方式查询
    Entry.objects.extra(where=["headline='Lennon'"])
    

    defer 延迟查询方法。

    • 当你需要访问该字段的值时才去查询。实际上有的类型values。指定查询字段,只不过这里是取反,参数里的字段不查。而values是查参数里的字段
    • 对主键貌似无效
    class Organization(models.Model):
        """
        组织关系架构表
        """
        name = models.CharField(max_length=255, verbose_name='组织名称')
        parent = models.ForeignKey('self', verbose_name='所属组织', blank=True,
                                   null=True, on_delete=models.SET_NULL)
        company = models.ForeignKey('Company', verbose_name='所属公司', blank=True,
                                    null=True, on_delete=models.SET_NULL)
        admin = models.ForeignKey('User', verbose_name='管理负责人', blank=True,
                                  null=True, on_delete=models.SET_NULL,
                                  related_name='organization_admin')
        members = models.ManyToManyField('User', verbose_name='组织成员', blank=True,
                                         related_name='organization_members')
        label = models.ManyToManyField('Label', verbose_name='标签集合', blank=True)
        
    Organization.objects.defer('id').query.__str__()
    # SELECT `organization`.`id`, `organization`.`name`, `organization`.`parent_id`, `organization`.`company_id`, `organization`.`admin_id` FROM `organization`
    
    Organization.objects.defer('company').query.__str__()
    # SELECT `organization`.`id`, `organization`.`name`, `organization`.`parent_id`, `organization`.`admin_id` FROM `organization`
    
    Organization.objects.defer('company', 'admin').query.__str__()
    # SELECT `organization`.`id`, `organization`.`name`, `organization`.`parent_id` FROM `organization`
    
    # 看出效果了么?
    
    • 如果对于外键部分字段想延迟查询,也可以使用defer
    # headline为外键字段。select_related会获取外键对应对表的数据查询。使用defer会过滤掉暂时不需要外键某些表的数据字段
    Blog.objects.select_related().defer("entry__headline", "entry__body")
    
    • 当你不想花过多时间考虑在需要过滤掉哪些字段需要延迟查询时,可以考虑通过创建同名表model。
    # 两个表名一样
    class CommonlyUsedModel(models.Model): 
        f1 = models.CharField(max_length=10)
        class Meta:
            managed = False  # 这个属性告诉Django在迁移数据库配置时,不要把它考虑进去。否则如果存在两张一样的表,Django迁移会出错的
            db_table = 'app_largetable'
    class ManagedModel(models.Model):
        f1 = models.CharField(max_length=10) 
        f2 = models.CharField(max_length=10)
        class Meta:
            db_table = 'app_largetable'
    
    # 下面这两种查询方式完全一样的查询结果。第一种不需要关注哪些需要延迟查询的字段。
    CommonlyUsedModel.objects.all()        
    ManagedModel.objects.all().defer('f2')
    
    # 注意。当在使用defer查询再调用save方法时。保存的只有已经加载的字段数据。延迟的字段值不会保存,也就是说,这里的f2即使有对应的f2关键字参数赋值。也不会更新f2的值到数据库
    

    only 相对于defer作的优化。也是上面这个例子的一个解决方案

    • 顾名思义。只查询某些字段
    # 第一种,排除两个字段。实际也就剩余name字段需要现在查询
    Person.objects.defer("age", "biography")
    
    # 第二种是直接查询name字段,其它字段不管
    Person.objects.only("name")
    
    Organization.objects.only('company', 'admin').query.__str__()
    # SELECT `organization`.`id`, `organization`.`company_id`, `organization`.`admin_id` FROM `organization`
    
    # 注意。在链式查询时。只会保留最后一个only的查询字段。其余被排除
    Organization.objects.only('company').only('admin').query.__str__()
    # SELECT `organization`.`id`, `organization`.`admin_id` FROM `organization`
    
    # defer在前与在后调用区别
    # defer在only后。那么only与defer重合的字段会被延迟。只查询only与defer的差集字段
    Organization.objects.only('company').defer('admin').query.__str__()
    # SELECT `organization`.`id`, `organization`.`company_id` FROM `organization`
    
    Organization.objects.only('company', 'admin').defer('admin').query.__str__()
    # SELECT `organization`.`id`, `organization`.`company_id` FROM `organization`
    
    Organization.objects.only('company', 'admin').defer('company').query.__str__()
    # SELECT `organization`.`id`, `organization`.`admin_id` FROM `organization`
    
    # defer在前已经看不透了。。。
    Organization.objects.defer('company', 'admin').only('company').query.__str__()
    'SELECT `organization`.`id`, `organization`.`name`, `organization`.`parent_id`, `organization`.`company_id`, `organization`.`admin_id` FROM `organization`'
    Organization.objects.defer('company', 'admin').only('admin').query.__str__()
    'SELECT `organization`.`id`, `organization`.`name`, `organization`.`parent_id`, `organization`.`company_id`, `organization`.`admin_id` FROM `organization`'
    Organization.objects.defer('admin').only('admin').query.__str__()
    'SELECT `organization`.`id`, `organization`.`name`, `organization`.`parent_id`, `organization`.`company_id`, `organization`.`admin_id` FROM `organization`'
    Organization.objects.defer('admin').only('admin','company').query.__str__()
    'SELECT `organization`.`id`, `organization`.`company_id` FROM `organization`'
    
    

    select_for_update(nowait=False, skip_locked=False, of=())

    • 返回一个查询集前将会进行行级锁,直到这个事务完成
      意味着在事务完成前,所有匹配的行会被加锁。不允许修改。直到该事务完成。才会释放锁

    • nowait=False, skip_locked=False这两个参数只支持oracle和Postgres数据库。mysql暂时不支持

    create(**kwargs)

    p = Person.objects.create(first_name="Bruce", last_name="Springsteen")
    # 等价于下面这种写法
    p = Person(first_name="Bruce", last_name="Springsteen")
    p.save(force_insert=True)
    
    # force_insert关键字参数意味着总是能保证将新的对象写入到数据库,前提是你没有自定义主键或联合唯一索引。否则可能创建失败
    

    get_or_create(defaults=None, **kwargs)

    • defaults实际是是一个字典形式参数

    • 通常这种方法调用用于POST请求接口

    • 获取一个对象,如果对象不存在则创建。返回一个元组形式数据。结构为(对象实例,是否创建)

    • 创建的前提是你必须提供创建的必须参数,如一些不能为空的字段,如果没有提供,则创建失败

    # 存在则返回
    Organization.objects.get_or_create(id=1)
    # (<Organization: 中民投国际>, False)
    # 不存在则创建
    Organization.objects.get_or_create(id=100)
    # (<Organization: >, True)
    
    
    • 这个功能实际上是下面操作的一个简化版
    try:
        obj = Person.objects.get(first_name='John', last_name='Lennon')
    except Person.DoesNotExist:
        obj = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9)) 
        obj.save()
        
    # 转换成下面这种形式
    obj, created = Person.objects.get_or_create( 
        first_name='John',
        last_name='Lennon',
        defaults={'birthday': date(1940, 10, 9)},
    )
    
    # defaults参数明确用于get(birthday=date(1940, 10, 9)) 条件获取调用。相当于获取该条件记录。如果不存在。则将其余信息包括该信息一起写入数据库。创建一条新记录返回
    
    
    • 下面是这个方法的实现算法
    # 先将非默认关键字参数获取。然后将默认关键字参数更新到非默认关键字参数里。
    # kwargs的key不能包含双下划线,通常这种参数会被认为是多表查询或条件匹配参数
    # 也就是defaults和kwargs同时存在相同key.则defaults最终会被写入数据库,defaults的value可以是可调用函数方法。这里可以看出。defaults就是一个字典对象
    params = {k: v for k, v in kwargs.items() if '__' not in k}
    params.update({k: v() if callable(v) else v for k, v in defaults.items()})
    # 下面这种写法与我们正常创建一个新对象一样了
    obj = self.model(**params)
    obj.save()
    
    # 下面这句代码表示查询字段defaults(注意。这里的defaults__exact是指字段)明确匹配为bar的记录。
    Foo.objects.get_or_create(defaults__exact='bar', defaults={'defaults': 'baz'})
    
    • 对于多对多操作时。外键表包含唯一索引。要避免插入一个重复的错误数据
    class Chapter(models.Model):
        title = models.CharField(max_length=255, unique=True)
    class Book(models.Model):
        title = models.CharField(max_length=256) 
        chapters = models.ManyToManyField(Chapter)
    # 假设之前没有数据的情况,添加一本书,没问题
    book = Book.objects.create(title="Ulysses")
    # 为这本书添加一个章节。不存在。则创建。也没问题
    book.chapters.get_or_create(title="Telemachus")
    # (<Chapter: Telemachus>, True)
    # 重复该操作。存在。则返回,不创建。也没问题
    book.chapters.get_or_create(title="Telemachus") 
    #(<Chapter: Telemachus>, False)
    # 现在通过正常方式创建一个章节。但是并没有与书进行关联,创建章节操作是成功的
    Chapter.objects.create(title="Chapter 1") 
    # <Chapter: Chapter 1>
    # 这时获取与书匹配的章节。是不存在的。应该创建。但是章节要求唯一索引。所以最终这个章节创建失败,且抛出异常
    book.chapters.get_or_create(title="Chapter 1") 
    # Raises IntegrityError
    

    update_or_create(defaults=None, **kwargs)

  • 相关阅读:
    spring boot整合mybatis+mybatis-plus
    Spring Boot Shiro 权限管理
    Spring Boot 热部署(转)
    SpringBoot 使用yml配置 mybatis+pagehelper+druid+freemarker实例
    详解Spring Boot配置文件之多环境配置
    Java 泛型-泛型类、泛型方法、泛型接口、通配符、上下限
    mybatis中整合ehcache缓存框架的使用
    Java总结篇系列:Java泛型
    java中接口之间的继承
    Java中接口继承泛型接口
  • 原文地址:https://www.cnblogs.com/zengchunyun/p/9175721.html
Copyright © 2011-2022 走看看