Django框架(六):模型层之多表操作

一、创建模型

书籍(Book)和书籍详细(BookDetail)是一对一关系
出版社(Publish)和书籍(Book)是一对多的关系
作者(Author)和书籍(Book)是多对多的关系

# 为了简洁和突出重点,其他表(BookDetail、Publish、Author)就不展示了!
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建立一对多的关系,外键字段建立在多的一方
    # to_field默认建立给主键,也就是说to_field="nid"不用指定。
    publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
    
    
     # 与AuthorDetail建立一对一的关系,会自动建立两边unique唯一约束
    bookDetail=models.OneToOneField(to="BookDetail",on_delete=models.CASCADE)
    '''
    OneToOneField相当于:
	publish=models.ForeignKey(to="Publish",on_delete=models.CASCADE,unique=True)
    '''
  
    
    # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
    authors=models.ManyToManyField(to='Author')
    '''
    自动创建一张名为:应用名称_表名_字段的表,比如我应用为app01,上面这句代码生成的表名为app01_book_authors
    存储Book表的id主键字段和Author表的id主键字段之间的关系
    '''

注意事项:

  • 多对多,创建的表的名称:应用名称_表名_字段,是根据模型中的元数据自动生成的,也可以覆写为别的名称
  • 一个表的主键id字段可以自动添加,也可以自己指定,一般都不自己指定!
  • 对于外键字段,Django 会在字段名后添加"_id" 来创建数据库中的列名,比如上面的publish、bookDetail,实际数据库存储的字段名publish_id、bookDetail_id

给大家看下ManyToManyField生成的表
在这里插入图片描述

二、添加表记录

1、一对多

方式1:
   publish_obj=Publish.objects.get(nid=1)
   book_obj=Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=100,publish=publish_obj)
  
方式2(推荐):
   book_obj=Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=100,publish_id=1)  

核心:book_obj.publish与book_obj.publish_id是什么?

答:
1. 书和出版社是多对一的关系,一个书对象book_obj,应该通过外键对应Publish表的一个出版社。
2. book_obj.publish(就是publish=models.ForeignKey()这里的publish)
它代表的是该书对象相对应的一个publish出版社对象
我们可以用book_obj.publish.name这种方法,获取出版社名等其他出版社信息

3. book_obj.publish_id就是取到了书对象的publish_id外键字段值

2、多对多

# 当前生成的书籍对象
book_obj=Book.objects.create(title="追风筝的人",price=200,publishDate="2012-11-12",publish_id=1)

1. 方式一:拿到作者对象再add
    # 为书籍绑定的做作者对象
    yuan=Author.objects.filter(name="yuan").first() # 在Author表中主键为2的纪录
    egon=Author.objects.filter(name="alex").first() # 在Author表中主键为1的纪录

    # 绑定多对多关系,即向关系表app01_authors中添加纪录
    book_obj.authors.add(yuan,egon)    #  为一个book对象添加两个作者yuan和egon
	# 将某些特定的 model 对象添加到被关联对象集合中。   =======    book_obj.authors.add(*[])

2. 方式二(推荐):拿到作者表的主键id字段的值再add(如下图)
book_obj.authors.add(1) # 把当前书对象的id和Author表中的id主键字段值1放入关系表
# 这里的authors也就是authors=models.ManyToManyField(to='Author')这里的authors

在这里插入图片描述最后一条记录就是了

authors=models.ManyToManyField()中的authors并不是真正的字段,只是一个操作系统自建关系表的一个接口!

三、修改、删除表记录

1、删除表记录

不论是一对一、多对一还是多对多,删除表记录都是Book.objects.filter(过滤条件).delete(),一对一、多对一有on_delete保证级联删除。多对多,只要删除了一个book对象,那么这个对象相应关系表中的相应记录也会被级联删除

2、修改表记录

一对一,多对一不用说,直接Book.objects.update(name=name, price=price, pub_date=pub_date, publish_id=publisher_id)把外键字段重新赋值就行了,注意外键赋值的是主键id字段的值!

多对多有点特别,多对多你要先把字段重新赋值更新了,如:Book.objects.update(name=name, price=price, pub_date=pub_date, publish_id=publisher_id),再修改关系表记录!

修改关系表记录方法:

1. 方法一
	book_obj.authors.clear() 
	book_obj.authors.add() # 需要传参
	# 先清空该书籍对象的关系表中相应对应关系,再重新添加关系
2. 方法二(推荐,用的多)
	book_obj.authors.set() # 相当于先clear,再add,需要传参,和add一样的参数
3. 方法三
	book_obj.authors.remove() # 需要传参,拿作者和书来说,就是该书对象移除部分作者

四、跨表查询

基于对象的跨表查询和基于双下划线的跨表查询并没有谁好谁撇,但是基于双下划线的跨表查询肯定更加简单简洁!不同情况有不同的使用,联表是会占内存资源的

1、基于对象的跨表查询

注意:基于一、创建模型来看下面!对象的跨表查询是基于子查询的!

(1)一对多查询(例如:Publish 与 Book)
  • 正向查询(按字段:publish):
# 查询主键为1的书籍的出版社所在的城市
book_obj=Book.objects.filter(pk=1).first()
# book_obj.publish 是主键为1的书籍对象关联的出版社对象
print(book_obj.publish.city)  
  • 反向查询(按表名:book_set):
 # 查询与苹果出版社关联的所有书籍对象集合
publish_obj=Publish.objects.get(name="苹果出版社")
book_list=publish.book_set.all() # book是相应的表名,或者说是模型名(class类名)的小写
(2)一对一查询(例如:Book 与 BookDetail)
  • 正向查询(按字段:bookDetail):
# 查询金瓶眉这本书的页数
book_obj=Book.objects.filter(title="金瓶眉").first()
print(book_obj.bookDetail.pagenum) # 书籍详细信息中才有页数字段

在这里插入图片描述

  • 反向查询(按表名:book):
# 查询所有在北京写成的书籍的名字
bookDetail_list=BookDetail.objects.filter(writed_addr="beijing")
for obj in bookDetail_list:
     print(obj.book.title) # 为什么没有_set?因为这里是一对一查询,只可能查到一个

一对多查询:一个出版社一般会查到多本书,'因此加个_set表示得到的是一个集合!'
(3)多对多查询 (Book 与 Author)
  • 正向查询(按字段:authors):
# 查询金瓶眉所有作者的名字
book_obj=Book.objects.filter(title="金瓶眉").first()
authors_list=book_obj.authors.all()
for author_obj in authors_list:
     print(author_obj.name)
  • 反向查询(按表名:book_set):
# 查询egon出过的所有书籍的名字
author_obj=Author.objects.get(name="egon")
book_list=author_obj.book_set.all() # 与egon作者相关的所有书籍
for book_obj in book_list:
    print(book_obj.title)

'多对多嘛,同样可能查到多个,因此需要加_set'

2、基于双下划线的跨表查询

双下划线的跨表查询是基于join拼表实现的!比如左连接(LEFT JOIN)、内连接(INNER JOIN)等

要点:正向查询按字段,反向查询按表名小写
上面这句话是对怎么告诉ORM引擎join哪张表的高度概括,无论是下面三种那种都脱离不了这句话!

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

    # 正向查询 按字段:publish

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

    # 反向查询 按表名:book
    queryResult=Publish.objects
              .filter(name="苹果出版社")
              .values_list("book__title","book__price")
	# 默认values后的queryResult是一个字典,values_list后的queryResult是个列表
(2)多对多查询
# 练习: 查询alex出过的所有书籍的名字(多对多)

    # 正向查询 按字段: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)一对一查询
    # 查询天龙八部的书籍详细
    
    # 正向查询
    ret=Book.objects.filter(name="天龙八部").values("bookDetail__pagenum")

    # 反向查询
    ret=BookDetail.objects.filter(book__name="天龙八部").values("pagenum")
(4)进阶练习(连续跨表)
# 练习: 查询人民出版社出版过的所有书籍的名字以及作者的姓名


    # 正向查询
    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开头的作者出版过的所有书籍名称以及出版社名称
# 假设引入作者详细表AuthorDetail,和Author表是OneToOne,外键字段为authorDetail!
# AuthorDetail表中有作者电话(telephone字段),

    # 方式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")

猜你喜欢

转载自blog.csdn.net/weixin_44571270/article/details/107583916
今日推荐