Py学习  »  Django

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

凤求凰的博客 • 3 年前 • 368 次点击  

一、创建模型

书籍(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主键字段之间的关系
    '''
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

注意事项:

  • 多对多,创建的表的名称: 应用名称_表名_字段 ,是根据模型中的元数据自动生成的,也可以覆写为别的名称
  • 一个表的主键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)  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

核心: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外键字段值
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

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

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

四、跨表查询

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

1、基于对象的跨表查询

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

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

在这里插入图片描述

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

一对多查询:一个出版社一般会查到多本书,'因此加个_set表示得到的是一个集合!'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
(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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 反向查询(按表名: 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'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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是个列表
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
(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")

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
(3)一对一查询
    # 查询天龙八部的书籍详细
    
    # 正向查询
    ret=Book.objects.filter(name="天龙八部").values("bookDetail__pagenum")

    # 反向查询
    ret=BookDetail.objects.filter(book__name="天龙八部").values("pagenum")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
(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")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/72004
 
368 次点击