单表使用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', }, } }
补充
基于双下划线的跨表查询
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"
)