ORM操作 之 跨表查询和 分组查询实例
浏览目录
分析代码
<1> 每个数据模型都是django.db.models.Model的子类,它的父类Model包含了所有必要的和数据库交互的方法。并提供了一个简介漂亮的定义数据库字段的语法。
<2> 每个模型相当于单个数据库表(多对多关系例外,会多生成一张关系表),每个属性也是这个表中的字段。属性名就是字段名,它的类型(例如CharField)相当于数据库的字段类型(例如varchar)。大家可以留意下其它的类型都和数据库里的什么字段对应。
<3> 模型之间的三种关系:一对一,一对多,多对多。
一对一:实质就是在主外键(author_id就是foreign key)的关系基础上,给外键加了一个UNIQUE=True的属性;
一对多:就是主外键关系;(foreign key)
多对多:(ManyToManyField) 自动创建第三张表(当然我们也可以自己创建第三张表:两个foreign key)
创建表
作者模型:一个作者有姓名和年龄。
作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)
出版商模型:出版商有名称,所在城市以及email。
书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。
模型建立如下:
#作者
class Author(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) age = models.IntegerField() # 与AuthorDetail建立一对一的关系 authorDetail = models.OneToOneField(to="AuthorDetail")
#作者详情
class AuthorDetail(models.Model): nid = models.AutoField(primary_key=True) birthday = models.DateField() telephone = models.BigIntegerField() addr = models.CharField(max_length=64)
#图书版 class Publish(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) city = models.CharField(max_length=32) email = models.EmailField() #书籍 class Book(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField(max_length=32) publishDate = models.DateField() price = models.DecimalField(max_digits=5, decimal_places=2) keepNum = models.IntegerField() commentNum = models.IntegerField() # 与Publish建立一对多的关系,外键字段建立在多的一方 publish = models.ForeignKey(to="Publish", to_field="nid",) # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表 authors = models.ManyToManyField(to='Author')
注意事项:
1、 表的名称myapp_modelName
,是根据 模型中的元数据自动生成的,也可以覆写为别的名称
2、id
字段是自动添加的
3、对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名
4、这个例子中的CREATE TABLE
SQL 语句使用PostgreSQL 语法格式,要注意的是Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句。
5、定义好模型之后,你需要告诉Django _使用_这些模型。你要做的就是修改配置文件中的INSTALL_APPSZ中设置,在其中添加models.py
所在应用的名称。
6、外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None 。
基于对象的跨表查询(子查询)
一对多
''' 正向查询按字段 Book--------------->Publish <--------------- 反向查询按表名小写_set '''
正向查找
查询id为2的书籍对应出版社的邮箱
obj = Book.objects.filter(nid=2).first() print(obj.publish.email)
反向查找
橘子出版社出版过的所有的书籍的名字
obj = Publish.objects.filter(name="橘子出版社").first() ret = obj.book_set.all().values("title") print(ret)
多对多
''' 正向查询按字段 Book--------------->Author <--------------- 反向查询按表名小写_set 反向查询按xxx '''
注意:有个relate_name可以为反向查找表名做命名,当做字段来用。
正向查找
查询十万个为什么所有作者的名字
obj = Book.objects.filter(title="十万个为什么").first() ret = obj.authors.all().values("name") print(ret)
反向查找
查询alex出版的书籍个数
obj = Author.objects.filter(name="alex").first() ret = obj.book_set.all().count() print(ret)
一对一
''' 正向查询按字段 Book--------------->Author <--------------- 反向查询按表名小写 '''
正向查找
查询alex的手机号
obj = Author.objects.filter(name="alex").first() ret = obj.authorDetail.telephone print(ret)
反向查找
住在烟台的作者的名字
obj_list = AuthorDetail.objects.filter(addr="烟台") for obj in obj_list: print(obj.author.name)
基于queryset和__的跨表查询(join查询)
''' 正向查询安字段 反向查询安表名 '''
''' Book.objects.filter(price=100).values("title","publish__email"): queryset=Book.objects.filter(price=100) temp=[] for obj in queryset: temp.append({ "title":obj.title, "publish__email":obj.publish__email }) temp '''
(1)查询id为2的书籍对应出版社的邮箱
ret = Book.objects.filter(price=100).values("publish__email") print(ret) # [{publish__email:123},{publish__email:456},{publish__email:456}]
(2)橘子出版社出版过的所有的书籍的名字
ret = Publish.objects.filter(name="橘子出版社").values("book__title") print(ret)
(3)查询十万个为什么所有作者的名字
ret = Book.objects.filter(title="十万个为什么").values("authors__name") print(ret)
(4)查询alex出版的书籍个数
Author.objects.filter(name="alex").values("book__title").count()
(5)查询alex的手机号
ret = Author.objects.filter(name="alex").values("authorDetail__telephone") print(ret)
(6)住在烟台的作者的名字
ret = AuthorDetail.objects.filter(addr="烟台").values("author__name") print(ret)
分组查询
跨表查询
from django.db.models import Count, Avg
每一个出版社的名字以及对应出版书籍个数
ret = Publish.objects.all().annotate(book_count=Count("book__title")).values("name", "book_count") print(ret)
查询每一个作者的名字以及对应书籍的平均价格
ret = Author.objects.all().annotate(books_avg=Avg("book__price")).values("name", "books_avg") print(ret)
查询每一本书 的名字以及作者的个数
ret = Book.objects.all().annotate(c=Count("authors")).values("title", "c") print(ret)
单表查询