Django-7 ORM多表操作
多表操作
创建模型
实例:我们来假定下面这些概念,字段和关系
作者模型:一个作者有姓名和年龄。
作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)
出版商模型:出版商有名称,所在城市以及email。
书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。
模型建立如下:
from django.db import models # Create your models here. # 出版社表 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 AuthorDetail(models.Model): uid = models.AutoField(primary_key=True) name= models.CharField(max_length=32) tel = models.BigIntegerField() addr = models.CharField(max_length=255) # 作者表 class Author(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=32) age = models.IntegerField() # 建立外键关联(一对一关系:建立在哪张表都可以,一对一要使用onetoone) # AuthorDetail = models.OneToOneField(to='AuthorDetail',to_field='nid') 如果不添加一个on_delete,会在迁移数据库时报错 AuthorDetail = models.OneToOneField(to='AuthorDetail',to_field='uid',on_delete=models.CASCADE) #书籍表 class Book(models.Model): bid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) price = models.DecimalField(max_digits=8,decimal_places=2) # 建立外键关联(一对多关系:一个出版社可以出版多本书籍,关联要建在多的那张表中(book表)) publish = models.ForeignKey(to='Publish',to_field='nid',on_delete=models.CASCADE) # 多对多关系:manytomany,只需要to另外一张表就OK了, author = models.ManyToManyField(to="Author") ''' 上面的author语句相当于执行下面的sql语句: create table book2author( id int auto_increment, book_id int , author_id int, foreign key (book_id) references book(id), foreign key (author_id) refernces author(id) ) '''
然后通过 python manage.py makemigrations和 python manage.py migrate来迁移数据库(记得要导入pymysql和在settings中设置DATABASE)
注意事项:
- 表的名称
myapp_modelName
,是根据 模型中的元数据自动生成的,也可以覆写为别的名称 id
字段是自动添加的- 对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名
- 这个例子中的
CREATE TABLE
SQL 语句使用PostgreSQL 语法格式,要注意的是Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句。 - 定义好模型之后,你需要告诉Django _使用_这些模型。你要做的就是修改配置文件中的INSTALL_APPSZ中设置,在其中添加
models.py
所在应用的名称。 - 外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None 。
添加表记录
一对一
# publish = Publish.objects.create(name='人民出版社',city="北京",email="people@people.com") # print(publish) #--------------------------------添加一对多关联记录------------------------------------ # 添加书籍和出版社信息 # 方式一: # book_obj = Book.objects.create(name="三国演义",price=85.5,publish_id=1) # print(book_obj) # print(book_obj.publish) # 注意:这个publish是与这本书籍相关联的出版社对象 # 方式二: # pub_obj = Publish.objects.filter(name='人民出版社').first() # orm会自动把最后一个参数pub_obj的主键取出 # book_obj = Book.objects.create(name="三国演义2", price=85.5, publish=pub_obj) # print(book_obj.name) ''' 在添加书籍时,无论是是以publish_id=1,还是publish=pub_obj,最终都会生成一个publish对象 ''' # print(book_obj.publish) #注意:book_obj.publish是与这本书籍相关联的出版社对象 # print(book_obj.publish.email) #取书籍对应的email
多对多
# --------------------------------添加多对多关联记录------------------------------------ # book_obj = Book.objects.create(name='白雪公主',price=99,publish_id=1) # 取作者对象,注意,这里要取的是一个obj,而不是一个queryset对象,如果是qset对象,则会报错.如果用qset对象来添加的话,可以使用book_obj.author.add(nick.first().id,joseph.first().id) # nick = Author.objects.filter(name='nick').first() # joseph = Author.objects.filter(name='joseph').first() # print(nick.first().id) # 取obj对象方法二: # nick = Author.objects.get(name='nick') # joseph = Author.objects.get(name='joseph') # print(nick) # # book_obj.author.add(nick,joseph) # book_obj.author.add(1,2,3) # book_obj.author.add(*[1,2,3]) # book_obj.author.add(nick.first().id,joseph.first().id) # # 移除对应关系 # book_obj = Book.objects.filter(name='追风筝的人').first() # book_obj.author.remove(2) # 移除书籍所有的作者对应信息 # book_obj.author.clear() # 查询与书籍相关的所有作者对象,qset对象,book_obj.author.all()是一个作者集合的queryset对象 book_obj = Book.objects.filter(name='白雪公主').first() ret = book_obj.author.all() print(ret.values_list('name'))
基于对象的跨表查询
''' 两张有关系的表,A和B,关联字段在A表中. 正向查询:从A表查询B表中的内容,正向查询按 字段 反向查询:从B表查询A表中的内容,反向查询按 表名小写_set.all() 一对多查询: 正向查询:从A表查询B表中的内容,正向查询按 字段 反向查询:从B表查询A表中的内容,反向查询按 表名小写_set.all() 正向查询: book_obj.publish.email:关联属性在book表中是publish,所以查email用publish.email,也就是关联属性(对象)的属性 book(关联对象:publish)------------------------------------>publish 反向查询:book_obj.book_set.all() publish--------------------------------->book 多对多查询: 正向查询:从A表查询B表中的内容,正向查询按 字段 反向查询:从B表查询A表中的内容,反向查询按 表名小写_set.all() 正向查询(查书籍作者): book_obj.author.all() book(关联对象:author)-------------------------------------------------------------->author 反向查询(查作者所有书籍):author.book_set.all() author------------------------------------------------------------>book 一对一查询: 正向查询:从A表查询B表中的内容,正向查询按 字段 反向查询:从B表查询A表中的内容,反向查询按 表名小写 正向查询(查作者详细信息): author.AuthorDetail.tel author(关联对象:authorDetail)-------------------------------------------->authorDetail 反向查询(查作者所有书籍):auth.name authorDetail------------------------------------------------------------>author '''
一对多表记录查询
# 一对多的正向查询: 1 查询白雪公主这本书的出版的邮箱 ret =Book.objects.filter(name='白雪公主').first().publish.email print('<白雪公主>的出版社email:',ret) # 一对多的反向查询:2 查询人民出版社出版的所有书籍 # ret = Publish.objects.filter(name='人民出版社').first().book_set.all() book_obj = Publish.objects.filter(name='人民出版社').first() book_obj.book_set.all() print(book_obj.book_set.all()) for obj in ret: print(obj.name,obj.publish.name)
查询语法:
''' 两张有关系的表,A和B,关联字段在A表中. 正向查询:从A表查询B表中的内容,正向查询按 字段 反向查询:从B表查询A表中的内容,反向查询按 表名小写_set.all() 一对多查询: 正向查询:从A表查询B表中的内容,正向查询按 字段 反向查询:从B表查询A表中的内容,反向查询按 表名小写_set.all() 正向查询: book_obj.publish.email:关联属性在book表中是publish,所以查email用publish.email,也就是关联属性(对象)的属性 book(关联对象:publish)------------------------------------>publish 反向查询:book_obj.book_set.all() publish--------------------------------->book '''
多对多正反向查询
# 1.正向查询:查询白雪公主这本书的作者 book_obj = Book.objects.filter(name='白雪公主').first() book_obj.author.all() #书籍的所有作者信息,qset对象 for obj in book_obj.author.all(): print(obj.name) # 2.反向查询:查询nick出版的所有书籍 author = Author.objects.filter(name='nick').first() ret = author.book_set.all() for obj in ret: print(obj.name)
语法:
''' 多对多查询: 正向查询:从A表查询B表中的内容,正向查询按 字段 反向查询:从B表查询A表中的内容,反向查询按 表名小写_set.all() 正向查询(查书籍作者): book_obj.author.all() book(关联对象:author)-------------------------------------------------------------->author 反向查询(查作者所有书籍):author.book_set.all() author------------------------------------------------------------>book '''
一对一正反向查询
# 1.正向查询:查询作者nick的详细信息 author = Author.objects.filter(name='nick').first() print(author.AuthorDetail.tel) # 2.反向查询:查询作者id=2的作者名称 auth = Author.objects.filter(id=2).first() #拿到的是一个obj对象,可以直接通过点语法获取属性 print(auth.name)
语法:
''' 一对一查询: 正向查询:从A表查询B表中的内容,正向查询按 字段 反向查询:从B表查询A表中的内容,反向查询按 表名小写 正向查询(查作者详细信息): author.AuthorDetail.tel author(关联对象:authorDetail)-------------------------------------------->authorDetail 反向查询(查作者所有书籍):auth.name authorDetail------------------------------------------------------------>author '''
基于双下划线的跨表查询(join)
''' 基于双下划线的多表查询 正向查询按字段 反向查询按表名小写,按表名小写是告诉ORM用来join哪个表 1.正向查询白雪公主这本书的出版社名称 SQL语句: select app01_publish.name from app01_book inner join app01_publish on app01_book.publish_id = app01_publish.nid where app01_book.name='白雪公主'; ORM语句: ret = book.objects.filter(name='白雪公主').values('publish__name') 注意,这是正向查询,所以这里是publish是指的book表中的publish字段(其实拿到的还是Publish表) ,而不是Publish表 filter(name='白雪公主'):相当于sql中的where语句 values():则相当于select的内容 1.反向查询白雪公主这本书的出版社名称 SQL: select app01_publish.name from app01_publish inner join app01_book on app01_publish.nid = app01_book.publish_id where app01_book.name='白雪公主'; ORM语句: ret =Publish.objects.filter(book__name='白雪公主').values('name') filter(book__name='白雪公主'):告诉ORM引擎,inner join book表,然后查询name=白雪公主的书籍 values('name'):拿到和白雪公主这本书籍相对应的出版社的名字 '''
跨表查询的本质:把多个表join成一张表,然后进行分组查询!
跨表查询总结:
每个后表模型.objects.values('基表主键 pk').annotate(聚合函数(关联表__查询字段)).values('表模型的所拥有的字段','聚合函数字段')
例如:查询每个作者的名字以及出版过的书籍的最高价格
这里每个后面的字符,就是基表,这个基表就是作者表
一对多正向查询
# 正向查询:查白雪公主这本书籍的出版社名称 ret = Book.objects.filter(name='白雪公主').values('publish__name') print(ret) for name in ret: print(name.get('publish__name'))
'''
正向查询按字段
反向查询按表名小写,按表名小写是告诉ORM用来join哪个表
1.正向查询白雪公主这本书的出版社名称
SQL语句:
select app01_publish.name from app01_book inner join app01_publish
on app01_book.publish_id = app01_publish.nid
where app01_book.name='白雪公主';
ORM语句:
ret = book.objects.filter(name='白雪公主').values('publish__name')
注意,这是正向查询,所以这里是publish是指的book表中的publish字段(其实拿到的还是Publish表)
,而不是Publish表
filter(name='白雪公主'):相当于sql中的where语句
values():则相当于select的内容
'''
一对多反向查询
# 反向查询:查白雪公主这本书籍的出版社名称 ret =Publish.objects.filter(book__name='白雪公主').values('name') print(ret) ''' 1.反向查询白雪公主这本书的出版社名称 SQL: select app01_publish.name from app01_publish inner join app01_book on app01_publish.nid = app01_book.publish_id where app01_book.name='白雪公主'; ORM语句: ret =Publish.objects.filter(book__name='白雪公主').values('name') filter(book__name='白雪公主'):告诉ORM引擎,inner join book表,然后查询name=白雪公主的书籍 values('name'):拿到和白雪公主这本书籍相对应的出版社的名字 '''
多对多正向查询
#--------------多对多跨表查询 # 正向多表查询:查询白雪公主这本书的所有作者的名字 '''正向查询按关联表的字段,下面是SQL语句 select app01_author.name from app01_book inner join app01_book_author on app01_book.bid = app01_book_author.book_id inner join app01_author on app01_book_author.author_id=app01_author.id where app01_book.name='白雪公主'; ''' ret = Book.objects.filter(name='白雪公主').values('author__name')#这里的author是Book表中关联的字段 print(ret)
多对多反向查询
# 反向多表查询:查询白雪公主这本书的所有作者的名字 ''' SQL: select app01_author.name from app01_author inner join app01_book_author on app01_author.id = app01_book_author.author_id inner join app01_book on app01_book.bid = app01_book_author.book_id where app01_book.name='白雪公主';
反向查询按表名__字段值 ''' ret = Author.objects.filter(book__name='白雪公主').values('name') print(ret)
一对一正向查询
#1.正向查询:查询作者nick的电话号码 '''正向是按照字段名称(必须和对应的字段名称大小写一致) 流程:通过author表join与其关联的authordetail表,按照字段AuthorDetail(我在author表中设置的字段就是大写开头的),告诉ORM引擎 inner join authordetail 表,使用'表名__tel'来取出所需要的电话号码 ''' ret =Author.objects.filter(name='nick').values('AuthorDetail__tel') print(ret)
一对一反向查询
#2.反向查询:查询作者nick的电话号码 '''反向查询:按照表名小写.values('tel'),相当于select tel,filter() 相当于 where 流程:通过authordetail表join与其关联的author表,按照表名小写(author__name),告诉ORM引擎来inner join author表 ''' ret=AuthorDetail.objects.filter(author__name='nick').values('tel') print(ret)
跨多表查询
#1.查询手机号码以133开头的作者姓名,出版过的书籍,以及出版社名称 ''' SQL: select app01_book.name as book_name,app01_publish.name as publish_name,app01_author.name as author_name from app01_book inner join app01_book_author on app01_book.bid = app01_book_author.book_id inner join app01_author on app01_book_author.author_id = app01_author.id inner join app01_publish on app01_book.publish_id = app01_publish.nid inner join app01_authordetail on app01_authordetail.uid = app01_author.id where app01_authordetail.tel like "%133%"; ''' ret = Book.objects.filter(author__AuthorDetail__tel__startswith='133').values('publish__name','author__name') print(ret)