Django基础四之Django模型层(ORM)多表操作
一,创建模型
1.1 表和表之间的关系
一对一、多对一、多对多 ,用book表和publish表自己来想想关系,想想里面的操作,加外键约束和不加外键约束的区别,一对一的外键约束是在一对多的约束上加上唯一约束。
例如:
作者模型:一个作者有姓名和年龄。
作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)
出版商模型:出版商有名称,所在城市以及email。
书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。
建立模型:
from django.db import models # Create your models here. 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",to_field="nid",on_delete=models.CASCADE) #就是foreignkey+unique,只不过不需要我们自己来写参数了,并且orm会自动帮你给这个字段名字拼上一个_id,数据库中字段名称为authorDetail_id 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() #多对多的表关系,我们学mysql的时候是怎么建立的,是不是手动创建一个第三张表,然后写上两个字段,每个字段外键关联到另外两张多对多关系的表,orm的manytomany自动帮我们创建第三张表,两种方式建立关系都可以,以后的学习我们暂时用orm自动创建的第三张表,因为手动创建的第三张表我们进行orm操作的时候,很多关于多对多关系的表之间的orm语句方法无法使用#如果你想删除某张表,你只需要将这个表注销掉,然后执行那两个数据库同步指令就可以了,自动就删除了。 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) # 与Publish建立一对多的关系,外键字段建立在多的一方,字段publish如果是外键字段,那么它自动是int类型 publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE) #foreignkey里面可以加很多的参数,都是需要咱们学习的,慢慢来,to指向表,to_field指向你关联的字段,不写这个,默认会自动关联主键字段,on_delete级联删除 字段名称不需要写成publish_id,orm在翻译foreignkey的时候会自动给你这个字段拼上一个_id,这个字段名称在数据库里面就自动变成了publish_id # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表,并且注意一点,你查看book表的时候,你看不到这个字段,因为这个字段就是创建第三张表的意思,不是创建字段的意思,所以只能说这个book类里面有authors这个字段属性 authors=models.ManyToManyField(to='Author',) #注意不管是一对多还是多对多,写to这个参数的时候,最后后面的值是个字符串,不然你就需要将你要关联的那个表放到这个表的上面
1.2 多对多表的三种创建方式
方式一:自行创建第三张表
class Book(models.Model): title = models.CharField(max_length=32, verbose_name="书名") class Author(models.Model): name = models.CharField(max_length=32, verbose_name="作者姓名") # 自己创建第三张表,分别通过外键关联书和作者 class Author2Book(models.Model): author = models.ForeignKey(to="Author") book = models.ForeignKey(to="Book") class Meta: unique_together = ("author", "book")
方式二:通过ManyToManyField自动创建第三张表
class Book(models.Model): title = models.CharField(max_length=32, verbose_name="书名") # 通过ORM自带的ManyToManyField自动创建第三张表 class Author(models.Model): name = models.CharField(max_length=32, verbose_name="作者姓名") books = models.ManyToManyField(to="Book", related_name="authors") #自动生成的第三张表我们是没有办法添加其他字段的
方式三:设置ManyToManyField并指定自行创建的第三张表(中介模型)
class Book(models.Model): title = models.CharField(max_length=32, verbose_name="书名") # 自己创建第三张表,并通过ManyToManyField指定关联 class Author(models.Model): name = models.CharField(max_length=32, verbose_name="作者姓名") books = models.ManyToManyField(to="Book", through="Author2Book", through_fields=("author", "book")) # through_fields接受一个2元组('field1','field2'): # 其中field1是定义ManyToManyField的模型外键的名(author),field2是关联目标模型(book)的外键名。 class Author2Book(models.Model): author = models.ForeignKey(to="Author") book = models.ForeignKey(to="Book") #可以扩展其他的字段了 class Meta: unique_together = ("author", "book")
注意:
当我们需要在第三张关系表中存储额外的字段时,就要使用第三种方式,第三种方式还是可以使用多对多关联关系操作的接口(all、add、clear等等)
当我们使用第一种方式创建多对多关联关系时,就无法使用orm提供的set、add、remove、clear方法来管理多对多的关系了。
创建一对一关系字段时的一些参数
to
设置要关联的表。
to_field
设置要关联的字段。
on_delete
同ForeignKey字段。
创建一对一关系字段时的一些参数
创建一对多关系字段时的一些参数
to 设置要关联的表 to_field 设置要关联的表的字段 related_name 反向操作时,使用的字段名,用于代替原反向查询时的'表名_set'。 related_query_name 反向查询操作时,使用的连接前缀,用于替换表名。 on_delete 当删除关联表中的数据时,当前表与其关联的行的行为。 创建一对多关系字段时的一些参数
创建多对多关系字段时的一些参数
多对多的参数:
to
设置要关联的表
related_name
同ForeignKey字段。
related_query_name
同ForeignKey字段。
through
在使用ManyToManyField字段时,Django将自动生成一张表来管理多对多的关联关系。
但我们也可以手动创建第三张表来管理多对多关系,此时就需要通过
through来指定第三张表的表名。
through_fields
设置关联的字段。
db_table
默认创建第三张表时,数据库中表的名称。
创建多对多字段时的一些参数
创建表时的一些元信息设置
元信息 ORM对应的类里面包含另一个Meta类,而Meta类封装了一些数据库的信息。主要字段如下: class Author2Book(models.Model): author = models.ForeignKey(to="Author") book = models.ForeignKey(to="Book") class Meta: unique_together = ("author", "book") db_table ORM在数据库中的表名默认是 app_类名,可以通过db_table可以重写表名。db_table = 'book_model' index_together 联合索引。 unique_together 联合唯一索引。 ordering 指定默认按什么字段排序。 ordering = ['pub_date',] 只有设置了该属性,我们查询到的结果才可以被reverse(),否则是能对排序了的结果进行反转(order_by()方法排序过的数据) 创建表时的一些元信息设置
获取元信息,可以通过model对象._meta.verbose_name等获取自己通过verbose_name指定的表名,model对象._meta.model_name获取小写的表名,还有model对象.app_label可以获取这个对象的app应用名等等操作。例如:book_obj = models.Book.objects.get(id=1),book_obj._meta.model_name。
关于db_column和verbose_name
1.指定字段名: 在定义字段的时候,增加参数db_column=’real_field’;
2.指定表名: 在model的class中,添加Meta类,在Meta类中指定表名db_table
例如在某个models.py文件中,有一个类叫Info:
class Info(models.Model): ''''' 信息统计 ''' app_id = models.ForeignKey(App) app_name = models.CharField(verbose_name='应用名', max_length=32, db_column='app_name2') class Meta: db_table = 'info' verbose_name = '信息统计' verbose_name_plural = '信息统计'
其中db_column指定了对应的字段名,db_table指定了对应的表明;
如果不这样指定,字段名默认为app_name, 而表明默认为app名+类名: [app_name]_info.
verbose_name指定在admin管理界面中显示中文;verbose_name表示单数形式的显示,verbose_name_plural表示复数形式的显示;中文的单数和复数一般不作区别。
注意事项:
- 表的名称
myapp_modelName,是根据 模型中的元数据自动生成的,也可以覆写为别的名称 id字段是自动添加的- 对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名
- 这个例子中的
CREATE TABLESQL 语句使用PostgreSQL 语法格式,要注意的是Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句。 - 定义好模型之后,你需要告诉Django _使用_这些模型。你要做的就是修改配置文件中的INSTALL_APPSZ中设置,在其中添加
models.py所在应用的名称。 - 外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None 。
咱们的表里面包含了一对一、一对多、多对多的关系,我们基于这几个表来练习,将来无论有多少张表,都逃脱不了这三个关系,操作起来都是一样的。
关于on_delete
on_delete
当删除关联表中的数据时,当前表与其关联的行的行为。
models.CASCADE
删除关联数据,与之关联也删除
models.DO_NOTHING
删除关联数据,引发错误IntegrityError
models.PROTECT
删除关联数据,引发错误ProtectedError
models.SET_NULL
删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
models.SET_DEFAULT
删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
models.SET
删除关联数据,
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
关于on_delete参数
ForeignKey的db_contraint参数
关系和约束大家要搞清楚,我不加外键能不能表示两个表之间的关系啊,当然可以 但是我们就不能使用ORM外键相关的方法了,所以我们单纯的将外键换成一个其他字段类型,只是单纯的存着另外一个关联表的主键值是不能使用ORM外键方法的。 #db_constraint=False只加两者的关系,没有强制约束的效果,并且ORM外键相关的接口(方法)还能使用,所以如果将来公司让你建立外键,并且不能有强制的约束关系,那么就可以将这个参数改为False customer = models.ForeignKey(verbose_name='关联客户', to='Customer',db_constraint=False)
二,表的增删改查
在modles中创建modle对象:
from django.db import models # Create your models here. class Author(models.Model): # 常用字段 name = models.CharField(max_length=32) age = models.IntegerField() authorDetail = models.OneToOneField(to="AuthorDetail", on_delete=models.CASCADE, # Django2.0,需要写on_delete=models.CASCADE 级联删除 # on_delete = models.SET_NULL, # Django2.0,需要写on_delete=models.SET_NULL 不做级联删除 ) # 一对一的作者详细信息表 # 一对一到AuthorDetail表生成为表字段后,会自动变为authorDetail_id 这样有一个名称 # 外键字段 --外键字段名_id def __str__(self): return self.name class AuthorDetail(models.Model): # 不常用字段 birthday = models.DateField() # telephone = models.BigIntegerField() # 长整型 telephone = models.CharField(max_length=32) addr = models.CharField(max_length=64) def __str__(self): return self.addr # 出版社 和书籍表是一对多的关系 class Publish(models.Model): name = models.CharField(max_length=32) city = models.CharField(max_length=32) email = models.EmailField() # 实际上是charfield def __str__(self): return self.name # 书籍表 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) publishs = models.ForeignKey(to='Publish', on_delete=models.CASCADE) # 书籍表和出版社表示多对一关系,需在多的一方加上 models.ForeignKey() authors = models.ManyToManyField(to='Author') # 作者表和书籍表示多对多关系 def __str__(self): return self.title
2.1,表的增加
单表的增加
new_author_detail = models.AuthorDetail.objects.create( birthday='1923-08-08', telephone='123456789', addr='三国中的蜀国' )
一对一表的增加
# 方式一 # 直接用类的属性=新建modle对象 models.Author.objects.create( name="张飞", age=345, authorDetail=new_author_detail # 或 # authorDetail=obj ) obj = models.AuthorDetail.objects.filter(addr='广东深圳').first() # queryset = models.AuthorDetail.objects.filter(addr='广东深圳') # 方式二 常用 # 直接用数据库中的字段 print(obj) models.Author.objects.create( name="张飞2", age=345, authorDetail_id=obj.id )
一对多表的增加
# 方式一,使用对象 obj = models.Publish.objects.get(id=2) models.Book.objects.create( title='资本论', publishDate = '1923-02-04', price = '67', # publishs= models.Publish.objects.get(id = 2) publishs= obj ) # # 方式二:常用 obj = models.Publish.objects.get(id=2) models.Book.objects.create( title='资本论2', publishDate='1923-02-04', price='67', # publishs= models.Publish.objects.get(id = 2) publishs_id=obj.id )
多对多增加
# 方式一:常用 book_obj = models.Book.objects.get(nid=3) # book_obj.authors.add([1,2]) # 会报错TypeError: unhashable type: 'list',所以要加* 打散。 book_obj.authors.add(*[1,2]) # 方式二: author1 = models.Author.objects.get(id=2) author2 = models.Author.objects.get(id=4) book_obj = models.Book.objects.get(nid=4) book_obj.authors.add(*[author1, author2])
2.2 删除
一对一表的删除
# 2.1 一对一删除 ,表一外键关联到表二,表一删除,不影响表2,表2删除会影响表1 models.AuthorDetail.objects.get(id=1).delete()
一对多表的删除
# 2.2 一对多删除 models.Publish.objects.get(id=3).delete()
多对多表的删除
# 2.3 多对多删除(实际上删除的是第三张表里的关系) book_obj=models.Book.objects.get(nid=4) book_obj.authors.remove(4) book_obj.authors.remove(*[4,2]) # 清空4号书籍所对应的作者关系 book_obj.authors.clear() book_obj.authors.add(*[5]) # 清空4号书籍所对应的作者关系,并添加2号和4号作者 book_obj.authors.set(['2','4']) # 清空4号书籍所对应的作者关系,并添加3号作者 book_obj.authors.set('3') # 删除然后更新
2.3 表的修改
一对一表的修改
models.Author.objects.filter(id=3).update( name = '明星', age = '18', # authorDetail =models.AuthorDetail.objects.get(id=6), authorDetail_id =3 )
一对多表的修改
# 可以将nid 换成 pk (primary key 的意思) models.Book.objects.filter(nid = 3).update( title = '鬼谷子2', # publishs = models.Publish.objects.get(id=2) publishs_id = 1 ) # 级联更新 models.Publish.objects.filter(pk=2).update( id = 7 # Django 没有级联更新。。。 )
多对多表的修改
# 多对多更新,只能用set() 同上 # 清空4号书籍所对应的作者关系,并添加2号和4号作者 book_obj.authors.set(['2','4']) # 清空4号书籍所对应的作者关系,并添加3号作者 book_obj.authors.set('3') # 删除然后更新
2.4 表的查询(重点)
2.4.1 基于对象的跨表查询 ----类似于子查询
关系属性(字段)写在哪个类(表)里面,从当前类(表)的数据去查询它关联类(表)的数据叫做正向查询,反之叫做反向查询
一对一的查询
# 一对一查询 # 查询胡的手机号码 author_obj = models.Author.objects.filter(name="胡惠明").first() print(type(author_obj)) ren = author_obj.authorDetail.telephone print(ren) # 反向查询 # 查询14785296312号码是谁的 author_detail_obj = models.AuthorDetail.objects.get(telephone='12345678910') print(author_detail_obj) print(author_detail_obj.author.name) ''' 正向查询:Authorobj.authorDetail,对象.关联属性名称 Author----------------------------------->AuthorDetail <----------------------------------- 反向查询:AuthorDetailobj.author ,对象.小写类名 '''
一对多的查询
# 一对多查询 # 查询一下资本论这本书的出版社是哪个 # 正向查询 book_obj = models.Book.objects.get(title='资本论') print(book_obj.publish.name) # 反向查询 pub_obj = models.Publish.objects.get(name='沙河出版社') print(pub_obj.book_set.all()) ''' 正向查询 book_obj.publishs 对象.属性 Book ---------------------------------------------> Publish <---------------------------------------------- 反向查询 publish_obj.book_set.all() 对象.表名小写_set '''
多对多的查询
# 多对多查询 # python编程是谁写的 book_obj = models.Book.objects.get(title='python编程') print(book_obj.authors.all()) # 李作者写了什么书 author_obj = models.Author.objects.get(name='李作者') print('李作者写的', author_obj.book_set.all()) ''' 正向查询 book_obj.authors.all() 对象.属性 Book ---------------------------------------------> Author <---------------------------------------------- 反向查询 author_obj.book_set.all() 对象.表名小写_set '''
2.4.2 基于双下划线(queryset)的跨表查询 --连接join
一对一
# 一对一 # 正向查询 # 1,查询李作者的手机号码是多少? obj = models.Author.objects.filter(name='李作者').values('authorDetail__telephone') print(obj) # <QuerySet [{'authorDetail__telephone': 12345678910}]> # 反向查询 obj = models.AuthorDetail.objects.filter(author__name='李作者').values('telephone', 'author__age') print(obj) # <QuerySet [{'telephone': 12345678910, 'author__age': 18}]> # 哪个老师的电话是:12345678910 # 正向查询 obj = models.Author.objects.filter(authorDetail__telephone='12345678910').values('name') print(obj) # 反向查询 obj = models.AuthorDetail.objects.filter(telephone='12345678910').values('author__name') print(obj)
一对多
# 一对多: # 查询python编程这本书的出版社是哪一个 # 正向查询 obj = models.Book.objects.filter(title='资本论').values('publish__name') print(obj) # 方向查询 obj = models.Publish.objects.filter(book__title='python编程').values('name') print(obj) # 沙河出版社出版了哪些书? # 正向查询 obj = models.Book.objects.filter(publish__name='沙河出版社').values('title') print(obj) # <QuerySet [{'title': 'python编程'}, {'title': '老李的床头故事'}]> # 反向查询 obj = models.Publish.objects.filter(name="沙河出版社").values('book__title') print(obj) # <QuerySet [{'book__title': 'python编程'}, {'book__title': '老李的床头故事'}]>
多对多
# 多对多 # 正向查询 # 查询python编程这本书是谁写的 obj = models.Book.objects.filter(title='python编程').values('authors__name','authors__authorDetail__telephone') print(obj) # 反向查询 obj = models.Author.objects.filter(book__title='python编程').values('name') print(obj) # 李作者写了哪些书 # 正向 查询 obj = models.Book.objects.filter(authors__name='李作者').values('title') print(obj) # 反向查询 obj = models.Author.objects.filter(name='李作者').values('book__title') print(obj)
2.4.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") # 练习: 手机号以151开头的作者出版过的所有书籍名称以及出版社名称 # 方式1: queryResult=Book.objects .filter(authors__authorDetail__telephone__regex="151") .values_list("title","publish__name") # 方式2: ret=Author.objects .filter(authordetail__telephone__startswith="151") .values("book__title","book__publish__name")
2.4.4 related_name
反向查询时,如果定义了related_name ,则用related_name替换 表名,例如:
publish = ForeignKey(Blog, related_name='bookList')
# 练习: 查询人民出版社出版过的所有书籍的名字与价格(一对多)# 反向查询 不再按表名:book,而是related_name:bookList queryResult=Publish.objects .filter(name="人民出版社") .values_list("bookList__title","bookList__price")
三,聚合查询,分组查询,F查询和Q查询

浙公网安备 33010602011771号