ORM操作

单表使用ORM对数据库的增删改查

ORM与数据库的对应

类     ==>  表

对象    ==>  记录

属性  ==>  字段

from django.db import models


class Publisher(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32, unique=True)


class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32, unique=True)
    publisher = models.ForeignKey(to=Publisher, on_delete=models.CASCADE)
    # publisher为外键,ORM自动将publisher后面加上_id作为字段传给数据库,可以使用book_obj.publisher_id找到书籍对象对应的出版社的id,这是直接操作数据库的
    # publisher作为ORM中BOOK类的属性,可以通过book_obj.publisher找到书籍对象对应的出版社对象,这仍然是通过ORM操作数据库
    def __repr__(self):
        return "<Book object: {} >".format(self.title)


class Author(models.Model):
    name = models.CharField(max_length=32, unique=True)
    books = models.ManyToManyField('Book')

    def __repr__(self):
        return "<Author object: {} >".format(self.name)

    __str__ = __repr__
    
增:
    普通表:            models.Publisher.objects.create(name='xxx')    
    
    带外键的表:        models.Book.objects.create(name='xxx',publisher=publisher_obj)
                    models.Book.objects.create(name='xxx',publisher_id=publisher_obj.id)
    
    带多对多的表:    author_obj = models.Author.objects.create(name='xxxxx')
                    author_obj.books.set([1,3,4])
    
删除:
    对象列表.delete()
    对象.delete()
    
修改:
    普通表:            pub_obj.name='xxxxx'
                    pub_obj.save()

    带外键的表:        book_obj.title = 'xxxxx'
                    book_obj.publisher = publisher_obj 或
                    book_obj.publisher_id = publisher_obj.id  或 book_obj.publisher_id = 'id的数值'(推荐这种写法,直接操作数据库)
                    book_obj.save()
    
    带多对多的表    author_obj.name = 'xxx'
                    author_obj.save()    
                    author_obj.books.set([1,3,4])
                    
查:
    普通字段:        pub_obj.id
    
    外键字段:        book_obj.publisher       —— 》 关联的出版社对象
                    book_obj.publisher_id    —— 》 关联的出版社对象的id
                    
    多对多字段:        author_ob.books             —— 》 管理对象 
                    author_ob.books.all()     —— 》 作者关联的所有书籍对象列表  
            

常用字段 

AutoField

自增的整形字段,必填参数primary_key=True,则成为数据库的主键。无该字段时,django自动创建。

一个model不能有两个AutoField字段。

IntegerField

一个整数类型。数值的范围是 -2147483648 ~ 2147483647。

CharField

字符类型,必须提供max_length参数。max_length表示字符的长度。

DateField

日期类型,日期格式为YYYY-MM-DD,相当于Python中的datetime.date的实例。

参数:

  • auto_now:每次修改时修改为当前日期时间。
  • auto_now_add:新创建对象时自动添加当前日期时间。

auto_now和auto_now_add和default参数是互斥的,不能同时设置。

DatetimeField

复制代码
    AutoField(Field)
        - int自增列,必须填入参数 primary_key=True

    BigAutoField(AutoField)
        - bigint自增列,必须填入参数 primary_key=True

        注:当model中如果没有自增列,则自动会创建一个列名为id的列
        from django.db import models

        class UserInfo(models.Model):
            # 自动创建一个列名为id的且为自增的整数列
            username = models.CharField(max_length=32)

        class Group(models.Model):
            # 自定义自增列
            nid = models.AutoField(primary_key=True)
            name = models.CharField(max_length=32)

    SmallIntegerField(IntegerField):
        - 小整数 -32768 ~ 32767

    PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正小整数 0 ~ 32767

    IntegerField(Field)
        - 整数列(有符号的) -2147483648 ~ 2147483647

    PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正整数 0 ~ 2147483647

    BigIntegerField(IntegerField):
        - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807

    BooleanField(Field)
        - 布尔值类型

    NullBooleanField(Field):
        - 可以为空的布尔值

    CharField(Field)
        - 字符类型
        - 必须提供max_length参数, max_length表示字符长度

    TextField(Field)
        - 文本类型

    EmailField(CharField):
        - 字符串类型,Django Admin以及ModelForm中提供验证机制

    IPAddressField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制

    GenericIPAddressField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
        - 参数:
            protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
            unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both"

    URLField(CharField)
        - 字符串类型,Django Admin以及ModelForm中提供验证 URL

    SlugField(CharField)
        - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)

    CommaSeparatedIntegerField(CharField)
        - 字符串类型,格式必须为逗号分割的数字

    UUIDField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证

    FilePathField(Field)
        - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
        - 参数:
                path,                      文件夹路径
                match=None,                正则匹配
                recursive=False,           递归下面的文件夹
                allow_files=True,          允许文件
                allow_folders=False,       允许文件夹

    FileField(Field)
        - 字符串,路径保存在数据库,文件上传到指定目录
        - 参数:
            upload_to = ""      上传文件的保存路径
            storage = None      存储组件,默认django.core.files.storage.FileSystemStorage

    ImageField(FileField)
        - 字符串,路径保存在数据库,文件上传到指定目录
        - 参数:
            upload_to = ""      上传文件的保存路径
            storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
            width_field=None,   上传图片的高度保存的数据库字段名(字符串)
            height_field=None   上传图片的宽度保存的数据库字段名(字符串)

    DateTimeField(DateField)
        - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]

    DateField(DateTimeCheckMixin, Field)
        - 日期格式      YYYY-MM-DD

    TimeField(DateTimeCheckMixin, Field)
        - 时间格式      HH:MM[:ss[.uuuuuu]]

    DurationField(Field)
        - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型

    FloatField(Field)
        - 浮点型

    DecimalField(Field)
        - 10进制小数
        - 参数:
            max_digits,小数总长度
            decimal_places,小数位长度

    BinaryField(Field)
        - 二进制类型
详细字段类型
#自定义一个char类型字段:
class MyCharField(models.Field):
    """
    自定义的char类型的字段类
    """
    def __init__(self, max_length, *args, **kwargs):
        self.max_length = max_length
        super(MyCharField, self).__init__(max_length=max_length, *args, **kwargs)
 
    def db_type(self, connection):
        """
        限定生成数据库表的字段类型为char,长度为max_length指定的值
        """
        return 'char(%s)' % self.max_length
#使用自定义char类型字段:
class Class(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=25)
    # 使用自定义的char类型的字段
    cname = MyCharField(max_length=25)
    null                数据库中字段是否可以为空
    db_column           数据库中字段的列名
    default             数据库中字段的默认值
    primary_key         数据库中字段是否为主键
    db_index            数据库中字段是否可以建立索引
    unique              数据库中字段是否可以建立唯一索引
    unique_for_date     数据库中字段【日期】部分是否可以建立唯一索引
    unique_for_month    数据库中字段【月】部分是否可以建立唯一索引
    unique_for_year     数据库中字段【年】部分是否可以建立唯一索引
 
    verbose_name        Admin中显示的字段名称
    blank               Admin中是否允许用户输入为空
    editable            Admin中是否可以编辑
    help_text           Admin中该字段的提示信息
    choices             Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
                        如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)
 
    error_messages      自定义错误信息(字典类型),从而定制想要显示的错误信息;
                        字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
                        如:{'null': "不能为空.", 'invalid': '格式错误'}
 
    validators          自定义错误验证(列表类型),从而定制想要的验证规则
                        from django.core.validators import RegexValidator
                        from django.core.validators import EmailValidator,URLValidator,DecimalValidator,
                        MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
                        如:
                            test = models.CharField(
                                max_length=32,
                                error_messages={
                                    'c1': '优先错信息1',
                                    'c2': '优先错信息2',
                                    'c3': '优先错信息3',
                                },
                                validators=[
                                    RegexValidator(regex='root_d+', message='错误了', code='c1'),
                                    RegexValidator(regex='root_112233d+', message='又错误了', code='c2'),
                                    EmailValidator(message='又错误了', code='c3'), ]
                            )
 
字段参数
字段参数

Model Meta参数

app01 下的models.py
class
UserInfo(models.Model): nid = models.AutoField(primary_key=True) username = models.CharField(max_length=32) class Meta: # 数据库中生成的表名称 默认 app名称 + 下划线 + 类名 db_table = "table_name" # admin中显示的表名称 verbose_name = '个人信息' # verbose_name加s verbose_name_plural = '所有用户信息' # 联合索引 index_together = [ ("pub_date", "deadline"), # 应为两个存在的字段 ] # 联合唯一索引 unique_together = (("driver", "restaurant"),) # 应为两个存在的字段

普通索引:仅加速查询

唯一索引:加速查询 + 列值唯一(可以有null)

主键索引:加速查询 + 列值唯一 + 表中只有一个(不可以有null)

组合索引:多列值组成一个索引,
              专门用于组合搜索,其效率大于索引合并

全文索引:对文本的内容进行分词,进行搜索 


查询的13条命令 

不必非在app下的models进行ORM操作,可以在项目目录下新建文件夹,新建py文件进行ORM操作

我们在django中运行项目都是通过manage.py来完成的,下面的代码设置的一些环境在manage.py中也有,可以直接粘过来

import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm_practice.settings") # orm_practice是当前的项目名
    import django
    django.setup()

    from app01 import models


    # all  获取所有对象   ——》 QuerySet
    ret = models.Person.objects.all()

    # get  获取单独的对象  ——》 对象
    # 找不到或者找到多个就报错
    ret= models.Person.objects.get(id=1,name='阿旭')

    # filter 过滤 获取所有满足条件的对象   ——》QuerySet
    ret = models.Person.objects.filter(id=1)

    # exclude  获取不满足条件的所有对象
    ret = models.Person.objects.exclude(id=1)

    # values   ——》QuerySet  元素是字典
    # 不写参数   默认所有字段的名字和值
    # 写参数     拿到指定字段的名字和值
    ret = models.Person.objects.all().values('id','name')
    ret = models.Person.objects.values('id','name')
    # values_list   ——》QuerySet  元素是元组  只有值没有字段名
    # 不写参数   默认所有字段值
    # 写参数     拿到指定字段值
    ret = models.Person.objects.values_list('name','id')
    # print(ret)

    # for i in ret :
    #     print(i,type(i))
    #  reverse 对已经排序的QuerySet进行反转
    ret = models.Person.objects.all().reverse()


    # distinct 去重  ——》QuerySet


    # count 对QuerySet中的元素进行计数
    ret = models.Person.objects.filter(id=100).count()


    # first  取QuerySet中的第一个元素
    ret = models.Person.objects.all().first()

    # last  取QuerySet中的最后一个元素
    ret = models.Person.objects.all().last()
    ret = models.Person.objects.values_list().last()

    ret = models.Person.objects.filter(id=100).first()

    # exists 判断查询结果是否有值
    ret = models.Person.objects.filter(id=100).exists()
    # print(ret)

    # order_by 按字段进行排序 ——》QuerySet
    ret =models.Person.objects.order_by('age')
    print(ret)
"""
返回是QuerySet的方法
1. all()
2. filter()
3. values()
4. values_list()
5. order_by()
6. distinct()
7. reverse()
8. exclude()

返回是对象
1. get()
2. first()
3. last()

返回是布尔值
1. exists()

返回是数字
1. count()

"""

 单表的双下划线

import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm_practice.settings")
    import django

    django.setup()

    from app01 import models

    ret = models.Person.objects.filter(id__lt=3)  # less than
    ret = models.Person.objects.filter(id__gt=3)  # great than

    ret = models.Person.objects.filter(id__gte=3)  # great than equel
    ret = models.Person.objects.filter(id__lte=3)  # great than equel
    ret = models.Person.objects.filter(id__gte=3, id__lte=5)

    ret = models.Person.objects.exclude(id__gt=5)

    ret = models.Person.objects.filter(id__in=[1, 3, 5])
    ret = models.Person.objects.exclude(id__in=[1, 3, 5])

    ret = models.Person.objects.filter(id__gte=1, id__lte=100) #并且的关系
    ret = models.Person.objects.filter(id__range=[1, 5])    # id范围在1-5包括1和5
    ret = models.Person.objects.filter(name__contains='A')    # name包含'A'类似于sql语句中的like
    ret = models.Person.objects.filter(name__icontains='a')    # name中包含'A'或'a',不区分大小写

    ret = models.Person.objects.filter(name__istartswith='a')    # 以'a'开头
    ret = models.Person.objects.filter(name__endswith='x')        # 以'x'结尾

    ret = models.Person.objects.filter(birth__year='2018')        # 筛选年份为'2018'日期的对象
    ret = models.Person.objects.filter(birth__month='09')        # 筛选月份为'09'日期的对象
    ret = models.Person.objects.filter(name__isnull=True)        # 筛选名字不为空的对象
    print(ret)

跨表查询(连表查询)中的双下划线表示另一个关联表(外键关联或多对多关联)中的字段.

如ret= models.Book.objects.filter(publisher__name='沙河出版社')    publisher__name表示publisher表中的name字段   这个语句的意思是筛选出所有 出版社为沙河出版社出版的书籍

import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm_practice.settings")
    import django

    django.setup()

    from app01 import models
    # 正向查询   book ——》 publisher
    # 基于对象查询
    book_obj = models.Book.objects.get(id=1)
    print(book_obj.id)
    print(book_obj.title)
    print(book_obj.publisher_id)
    print(book_obj.publisher_id)  # 关联的出版社对象
    print(book_obj.publisher.id)  # 关联的出版社对象
    print(book_obj.publisher.name)

    # 基于字段查询
    ret= models.Book.objects.filter(publisher__name='沙河出版社')
    ret= models.Book.objects.filter(publisher_id=1)
    print(ret)


    
    
    
    
    # 反向查询   publisher ——》 book
    # 基于对象的查询
    # 1 —— 》 多    表名小写_set ——》 管理对象   .all()  出版社关联的所有书籍对象
    pub_obj = models.Publisher.objects.first()

    # 表名小写_set ,注意是表名小写_set  而不是子表(多对一中的多)的外键名_set   
    ret = pub_obj.book_set.all()        
    print(pub_obj.book_set,type(pub_obj.book_set))   book_set是外键反向查询的管理对象
    print(ret)

    class Book(models.Model):
    title = models.CharField(max_length=32)
    publisher = models.ForeignKey('Publisher',
                                  related_name='books',      # 关联名字
                                  # related_query_name='xx',    #关联查询名字
                                  on_delete=models.CASCADE, null=True)
    
    # Class Book中指定了related_name  books
    # print(pub_obj.books.all())
    
    

    # 基于字段的查询
    #1,不指定related_name和related_query_name
    ret = models.Publisher.objects.filter(book__title='跟太亮学开车')   表名
    #2,指定related_name  不指定related_query_name
    ret = models.Publisher.objects.filter(books__title='跟太亮学开车')  使用related_name 指定的名字 'books'
    #3,指定related_name和related_query_name
    ret = models.Publisher.objects.filter(xx__title='跟太亮学开车')        使用related_query_name 指定的名字 'xx'
    print(ret)
import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm_practice.settings")
    import django

    django.setup()

    from app01 import models
    author_obj = models.Author.objects.get(id=1)
    # print(author_obj.books.all())   books是多对多管理对象
# 多对多中的 creat() add() remove() clear()
    # create()
    # author_obj.books.create(title='戴绿与嫂子的故事',publisher_id=1)
    # 1. 创建书籍对象(与出版社进行关联)
    # 2. 该对象和作者进行关联

    
    # add()
    book_list = models.Book.objects.filter(id__in=[1,6,8,9])
    author_obj.books.add(*books_list)  # 增加作者对应的书籍  可以写id 可以写对象,book_list是对象列表,需要用*打散

    # set()
    author_obj.books.set([1,6,8,9])     # 用书籍id设置作者对应的书籍
    author_obj.books.set(books)
    author_obj.books.remove(*books)
    author_obj.books.clear()
    
# 一对多中的 creat() remove() clear()
    pub_obj= models.Publisher.objects.first()
    pub_obj.book_set        book_set是反向查询的管理对象
    pub_obj.books.create(title='跟老男孩学思想')
    
    pub_obj = models.Publisher.objects.get(id=1)

    # 一对多的管理对象remove不能使用id  使用对象
    pub_obj.books.remove(models.Book.objects.get(id=5))

    pub_obj.books.clear()    # 清空关联的书籍
    

聚合和分组

聚合:aggregate(*args,**kwargs)
  通过对QuerySet进行计算,返回一个聚合值的字典。aggregate()中每一个参数都指定一个包含在字典中的返回值。即在查询集上生成聚合。
  聚合的结果不包含原来表中的字段,aggregate()是QuerySet 的一个终止子句,只能在最后写他
分组:annotate(*args,**kwargs):
  可以通过计算查询结果中每一个对象所关联的对象集合,从而得出总计值(也可以是平均值或总和),即为查询集的每一项生成聚合。
  对分组后的每一组进行查询计算,然后将结果作为注释添加到表每行的数据中

简单看下用法,详细用法见下面的练习代码

#查询alex出的书的总价格
Book.object.filter(authors__name='alex').aggregate(Sum('price'))
#查询各个作者出的书的总价格,这里就涉及到分组了,分组条件是anthors__name
Book.object.values('authors__name').annotate(Sum('price'))
#查询各个出版社最便宜的书价是多少
Book.object.values('publisher__name').annotate(Min('price'))

F查询和Q查询

  

#仅仅靠单一的关键字参数查询已经很难满足查询要求。此时Django为我们提供了F查询和Q查询
# F 使用查询条件的值,专门取对象中某列值的操作
models.Tb1.objects.update(num=F('num')+1)  # 将num加1后重新赋值给num
# Q 构建搜索条件
models.Book.objects.filter(Q(id__lt=3)|Q(id__gt=5)).values()	# filter中两个条件以逗号分隔是and的关系,使用Q查询可以有或的关系

import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm_practice.settings")
    import django

    django.setup()

    from app01 import models
    from django.db.models import F,Q
    ret = models.Book.objects.filter(sale__gt=50).values()   # 一个字段的比较不用使用F查询
    ret = models.Book.objects.filter(sale__gte=F('kucun')+30).values()   # 两个字段的值进行比较需要使用F查询	
	

    ret = models.Book.objects.filter(~Q(id__lt=3)|Q(id__gt=5)).values()   # ~是取反   和!意义一样
    for i in ret:
        print(i)
	
	
	# updata()和save()的区别,updata只修改条件内的表的字段,save是把所有的字段都再保存一遍
    # models.Book.objects.all().update(sale=100)
    # book_obj = models.Book.objects.get(id=1)
    # book_obj.sale=0
    # book_obj.save()
    # models.Book.objects.all().update(sale=(F('sale')+10)*2)

   

  详细说明

# F 使用查询条件的值,专门取对象中某列值的操作

    from django.db.models import F
    models.Tb1.objects.update(num=F('num')+1)

# Q 构建搜索条件
    from django.db.models import Q
    
    # 1 Q对象(django.db.models.Q)可以对关键字参数进行封装,从而更好地应用多个查询
    q1 = models.Book.objects.filter(Q(title__startswith='P')).all()
    print(q1)  # [<Book: Python>, <Book: Perl>]
    
    # 2、可以组合使用&,|操作符,当一个操作符是用于两个Q的对象,它产生一个新的Q对象。
    Q(title__startswith='P') | Q(title__startswith='J')
    
    # 3、Q对象可以用~操作符放在前面表示否定,也可允许否定与不否定形式的组合
    Q(title__startswith='P') | ~Q(pub_date__year=2005)

    # 4、应用范围:
    
    # Each lookup function that takes keyword-arguments (e.g. filter(),
    #  exclude(), get()) can also be passed one or more Q objects as
    # positional (not-named) arguments. If you provide multiple Q object
    # arguments to a lookup function, the arguments will be “AND”ed
    # together. For example:

    Book.objects.get(
        Q(title__startswith='P'),
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
    )

    # sql:
    # SELECT * from polls WHERE title LIKE 'P%'
    #     AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')
    
    # import datetime
    # e=datetime.date(2005,5,6)  #2005-05-06
    
    # 5、Q对象可以与关键字参数查询一起使用,不过一定要把Q对象放在关键字参数查询的前面。
    # 正确:
    Book.objects.get(
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
        title__startswith='P')
    # 错误:
    Book.objects.get(
        question__startswith='P',
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))

事务

 通过事务我们可以把两件或者两件以上的ORM操作组成一个原子,这几件事要么都成功,一旦有一个失败了,那么全部不执行,即使执行过了也要回滚回去

比如,我们操作数据库进行转账操作,A向B转账分为两步,A的账户扣钱,B的账户加钱,这两件事必须都成功,或者都失败

import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm_practice.settings")
    import django

    django.setup()

    from app01 import models

    try:
        from django.db import transaction		# 导入从django事务模块
        import datetime

        with transaction.atomic():
            new_publisher = models.Publisher.objects.create(name="火星出版社") # 这条语句单独是可以成功的
            models.Book.objects.create(title="橘子物语",
                                       publisher_id=100)  # 指定一个不存在的出版社id,这条语句会报错,所以作为整体,事务内所有语句都不会执行
    except Exception as e:
        print(str(e))

  


ORM查询练习

简单说明:    publisher     字段   id    name   city

      book            字段   id    title    publish_date     price     memo      publisher(外键关联了publisher)    author(多对多管理对象,关联了作者)

      author          字段    id   name    age      phone

import os

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm_homework.settings")
import django

django.setup()

from app01 import models
from django.db.models import Q, Max, Count


# 查找所有书名里包含金老板的书
ret = models.Book.objects.filter(title__contains='金老板')

# 查找出版日期是2018年的书
ret = models.Book.objects.filter(publish_date__year=2018)

# 查找价格大于10元的书
ret = models.Book.objects.filter(price__gt=10)

# 查找memo字段是空的书
# -----字段为空包括空字符串和null  , null和空字符串并不是一回事
ret = models.Book.objects.filter(Q(memo='') | Q(memo__isnull=True))

# 查找名字以沙河开头的出版社
# ----distinct去重,有两个名字相同但是id不同的沙河出版社对象,所以这个去重并不能给对象去重,需要先使用values()取出出版社的名字,这样变成字典后再对名字进行去重
ret = models.Publisher.objects.filter(name__startswith='沙河').values('name').distinct()

# 查找“沙河出版社”出版的所有书籍
# ----连表查询,使用另一个表的属性时要使用双下划线
ret = models.Book.objects.filter(publisher__name='沙河出版社')


# 查找每个出版社出版价格最高的书籍价格
# ----annotate分组聚合,分组后就一定要使用聚合函数,annotate在官方文档的意思是注释的意思,给表中每行的数据添加注释
# ----对annotate前的表进行分组,annotate前有values的话,是以values中的值进行分组,没有values默认以annotate前表的id进行分组
ret = models.Publisher.objects.annotate(max=Max('book__price')).values('name', 'max', )

ret = models.Book.objects.values('publisher__name').annotate(max=Max('price')).values('publisher_id', 'max', 'title')
# 查找每个出版社出版价格最高的书籍价格,这是我之前做的方法,也是正确的可以相较而看
    # 单表分组,book表以publisher_id分组,统计每组的最大价格,注意后面的values()中不要包含book表的字段(如果后面的values中包含了book的字段如'title',那么会以 'publisher_id'和'title'一起进行分组,这样就乱了)
    # 这种方法有个问题是不同publisher_id但是同一个出版社名字的不会被分到一组
    ret = models.Book.objects.values('publisher_id').annotate(Max('price')).values('publisher_id','max')
    # 连表分组,book表和publisher表联合,以publisher表的name进行分组,统计每组的最大价格,正确答案,也符合sql语句的查询逻辑
    ret = models.Book.objects.values('publisher__name').annotate(Max('price')).values('publisher_id','max')
    # 单表分组,连表查询 ,以publisher表的id进行分组,查询book的最大价格,同样也是有不同publisher_id但是同一个出版社名字的不会被分到一组的问题
    ret = models.Publisher.objects.annotate(Max('book__price')).values('book__price__max')
    # 单表分组,连表查询 ,以publisher表的name进行分组,查询book的最大价格,不会有上述问题,比较符合人的逻辑
    ret = models.Publisher.objects.values('name').annotate(Max('book__price')).values('book__price__max')



# 查找每个出版社的名字以及出的书籍数量
# ----这里的Count计数的是以publisher分组的book对象,
ret = models.Publisher.objects.annotate(count=Count('book')).values()


# 查找书名是“跟金老板学开车”的书的出版社出版的其他书籍的名字和价格
# ---1,找到出版了“跟金老板学开车”的书的出版社 models.Publisher.objects.get(book__title='跟金老板学开车')
# ---2,筛选出是上述出版社出版的书,使用publisher出版社对象进行筛选  models.Book.objects.filter(publisher=models.Publisher.objects.get(book__title='跟金老板学开车')
# ---3.在这些书里排除 '跟金老板学开车'的书  .exclude(title='跟金老板学开车')
ret = models.Book.objects.filter(publisher=models.Publisher.objects.get(book__title='跟金老板学开车')).exclude(
    title='跟金老板学开车').values('title', 'price')

# 法2
# ---1,找到出版了“跟金老板学开车”的书的出版社
# ---2,通过外键book_set 找到该出版社出版的所有书籍,此时的对象列表已经不是出版社,而是书籍
# ---3,排除 '跟金老板学开车'的书 
ret = models.Publisher.objects.get(book__title='跟金老板学开车').book_set.exclude(title='跟金老板学开车').values('title', 'price')


# 查找书名是“跟金老板学开车”的书的作者们的姓名以及出版的所有书籍名称和价钱

# 这样是有问题的,不能这么写
ret = models.Author.objects.filter(book__title='跟金老板学开车').values('name', 'book__title', 'book__price').distinct()

# 这样是有问题的,不能这么写,见下面代码
ret = models.Book.objects.get(title='跟金老板学开车').author.values('name', 'book__title', 'book__price').distinct()

# ---1,找到“跟金老板学开车”的书籍对象
# ---2,找到写了'跟金老板学开车'书籍的所有的作者对象
# ---3,在所有书籍中筛选作者在上述作者对象的书籍
ret = models.Book.objects.filter(
        author__in=models.Author.objects.filter(book=models.Book.objects.filter(title='跟金老板学开车'))).values(
        'author__name', 'title', 'price')        
        
# 简化
ret = models.Book.objects.filter(
        author__in=models.Author.objects.filter(book__title='跟金老板学开车')).values(
        'author__name', 'title', 'price')
# 显示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',
        },
    }
}
显示sql语句的命令,在settings中添加下面代码

 补充

基于双下划线的跨表查询 

Django 还提供了一种直观而高效的方式在查询(lookups)中表示关联关系,它能自动确认 SQL JOIN 联系。要做跨关系查询,就使用两个下划线来链接模型(model)间关联字段的名称,直到最终链接到你想要的 model 为止。

关键点:正向查询按字段,反向查询按表明。

 

# 练习1: 查询人民出版社出版过的所有书籍的名字与价格(一对多)

# 正向查询 按字段:publish

queryResult=Book.objects
            .filter(publish__name="人民出版社")
            .values_list("title","price")

# 反向查询 按表名:book

queryResult=Publish.objects
              .filter(name="人民出版社")
              .values_list("book__title","book__price")

# 练习2: 查询egon出过的所有书籍的名字(多对多)

# 正向查询 按字段:authors:
queryResult=Book.objects
            .filter(authors__name="yuan")
            .values_list("title")

# 反向查询 按表名:book
queryResult=Author.objects
              .filter(name="yuan")
              .values_list("book__title","book__price")


# 练习3: 查询人民出版社出版过的所有书籍的名字以及作者的姓名


# 正向查询
queryResult=Book.objects
            .filter(publish__name="人民出版社")
            .values_list("title","authors__name")
# 反向查询
queryResult=Publish.objects
              .filter(name="人民出版社")
              .values_list("book__title","book__authors__age","book__authors__name")


# 练习4: 手机号以151开头的作者出版过的所有书籍名称以及出版社名称

queryResult=Book.objects
            .filter(authors__authorDetail__telephone__regex="151")
            .values_list("title","publish__name")

注意:

反向查询时,如果定义了related_name ,则用related_name替换表名,例如: publish = ForeignKey(Blog, related_name='bookList'):

# 练习1:  查询人民出版社出版过的所有书籍的名字与价格(一对多)

 
    # 反向查询 不再按表名:book,而是related_name:bookList
 
    queryResult=Publish.objects
              .filter(name="人民出版社")
              .values_list("bookList__title","bookList__price")
原文地址:https://www.cnblogs.com/perfey/p/9648474.html