Django ORM 多表操作:一对一、一对多、多对多的增删改查 (二)
  OHxphH6Es7WL 2023年12月07日 156 0


Django model ORM数据表相关操作

分析思路,创建数据表
对于表操作,表之间的关联关系,必须理解他们之间的关系,对于编程很重要。可以看看映射关系、外键和relationship查询 ,至少明白外键相关基本知识。
下面是简单的demo

1.创建数据模型

models.py 文件,

from django.db import models

# Create your models here.

#作者表
class Author(models.Model): #比较常用的信息放到这个表里面

    name=models.CharField( max_length=32)
    age=models.IntegerField()
    # 与AuthorDetail建立一对一的关系,一对一的这个关系字段写在两个表的任意一个表里面都可以
    authorDetail=models.OneToOneField(to="AuthorDetail",to_field="id",on_delete=models.CASCADE) #就是foreignkey+unique,只不过不需要我们自己来写参数了,并且orm会自动帮你给这个字段名字拼上一个_id,数据库中字段名称为authorDetail_id

    def __str__(self):
        return self.name

#作者详细信息表
class AuthorDetail(models.Model):#不常用的放到这个表里面
    birthday=models.DateField()
    telephone=models.CharField(max_length=32)
    addr=models.CharField( max_length=64)

    def __str__(self):
        return self.telephone

#出版社表 和 书籍表 一对多的关系
class Publish(models.Model):
    name=models.CharField( max_length=32)
    city=models.CharField( max_length=32)
    email=models.EmailField() #charField类似,EmailField做邮箱数据校验使用

    def __str__(self):
        return self.name

#多对多的表关系,我们学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)#Decimal ,max_digits整数加小数位数,decimal_places小数位数

    # 与Publish建立一对多的关系,外键字段建立在多的一方,字段publish如果是外键字段,那么它自动是int类型
    publishs=models.ForeignKey(to="Publish",to_field="id",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这个参数的时候,最后后面的值是个字符串,不然你就需要将你要关联的那个表放到这个表的上面

    def __str__(self):
        return self.title

2.ORM增删改操作

2.1 增

2.1.1 一对一增加
#作者信息表
author_detail = models.AuthorDetail.objects.create(
    birthday='2000-01-13',
    telephone='15265891211',
    addr='上海浦东新区',
)

#作者表
# 方式1:直接使用authorDetail使用对象来创建

models.Author.objects.create(
    name='李白',
    age='56',
    authorDetail=author_detail,
)

# 方式2:使用数据库字段名称来创建
models.Author.objects.create(
    name='苏轼',
    age='47',
    authorDetail_id=author_detail.id,
)
2.1.2一对多
#方式1:使用对象
models.Book.objects.create(
    title = '钢铁怎样炼成',
    publishDate='2019-03-26',
    price = 17.8,
    publishs=models.Publish.objects.filter(id=2).first(),
    # publishs=models.Publish.objects.get(id=2),
)

#方式2:使用属性字段
models.Book.objects.create(
    title = '木棒怎样炼成',
    publishDate='2017-03-26',
    price = 27.8,
    publishs_id=models.Publish.objects.get(id=2).id
)
2.1.3 多对多
#方式1:创建一个找到一个book对象,就可以调book表中的authors属性来添加对应的作者id,注意这儿使用add方法
book_obj = models.Book.objects.get(nid=8)
book_obj.authors.add(*[1,4])

#方式2:使用对象 (不推荐使用)
AUTHOR1=models.Author.objects.get(id=3)
AUTHOR2 = models.Author.objects.get(id=4)
AUTHOR3 = models.Author.objects.get(id=1)
book_obj1 = models.Book.objects.get(nid=7)
book_obj1.authors.add(*[AUTHOR1,AUTHOR2,AUTHOR3])

2.2 删除

一对一,多对多的删除和单表删除是一样的

# 一对一 、一对多。删除被依赖的依赖的也被删除。删除依赖被依赖的不会删除
models.AuthorDetail.objects.get(id=2).delete()

#多对多删除
#找到nid为6的书籍,
book_obj = models.Book.objects.get(nid=6)
#删除作者id为6的数据
book_obj.authors.remove(6)
#删除多个
book_obj.authors.remove(*[6,5])
#删除作者id为6的所有数据
book_obj.authors.clear()
#删除所有作者id为6的数据,并添加绑定一条id为1的数据
book_obj.authors.set("1")
#绑定一条id为1和5的两条数据,注意这儿不用打散
book_obj.authors.set(["1","5"])

2.3 更新

找到bookid为5,更新绑定信息

2.3.1 一对一
models.Author.objects.filter(id=5).update(
    name = '礼拜',
    age=17,
    # 根据对象
    # authorDetail = models.AuthorDetail.objects.get(id=5),
    # 根据id更新
    authorDetail_id = 4,
)
2.3.2一对多
models.Book.objects.filter(nid=4).update(
    title = '钢铁怎么练成',
    # publishs =  models.Publish.objects.get(id=3)
    publishs_id = 3,
)
2.3.3 多对多

使用set(), 和删除有些类似,实际上就是先删除在添加

book_obj = models.Book.objects.get(nid=6)
#删除所有作者id为6的数据,并添加绑定一条id为1的数据
book_obj.authors.set("1")
# book.authors.set([3,4])
2.4 基于对象的跨表查询 — 类似于子查询

正向or反向查询:
关系属性(字段)写在那个类(表)里面,从当前类的数据去查询它关联类(表)的数据,反之交反向查询。

正向查询:关联字段在从表,由从表查询主表中的数据    -----> 按字段查询
反向查询:关联字段在从表,由主表查询从表中的数据    -----> 按表名小写查询
2.4.1 一对一

正向查询: 靠关联的属性字段查询
查询罗贯中地址是什么

author_obj = models.Author.objects.filter(name="罗贯中").first()
# author_obj.authorDetail 拿到一对一关联的对象,我们就可以通过属性来获取值
print(author_obj.authorDetail.addr) #北京

反向查询 : 根据小写的类名查询
查询一下15235412112电话是什么人

author_detail_obj = models.AuthorDetail.objects.get(telephone="15235412112")
# author_detail_obj.author 拿到一对一的关联对象,可以通过属性来获取
print(author_detail_obj.author.name) #李白
正向查询: Authorobj.authorDetail,对象.关联属性名称
Author------------------------------------>AuthorDetail
      <------------------------------------
        反向查询:AuthorDetailobj.author,对象.类名小写.属性名
2.4.2 一对多

正向查询
查询书名为西游记的对象,调用关联对象,在获取属性
查询西游记出版社是什么

book_obj = models.Book.objects.get(title="西游记")
print(book_obj.publishs.name)

反向查询
查询新华出版社出版了那些书

publish_obj = models.Publish.objects.get(name='新华出版社')
print(publish_obj.book_set.all())  #<QuerySet [<Book: 水浒传>]>
正向查询 book_obj.publishs.属性
Book-------------------------------------------------->Publish
    <--------------------------------------------------
        反向查询 publish_obj.book_set.all()  对象.表名小写_set
2.4.3 多对多

正向查询
西游记是谁写的

book_obj = models.Book.objects.get(title='西游记')
print(book_obj.authors.all()) #<QuerySet [<Author: 罗贯中>, <Author: 吴承恩>]>

查询吴承恩写了那些书

author_obj = models.Author.objects.get(name='吴承恩')
print(author_obj.book_set.all()) #<QuerySet [<Book: 西游记>, <Book: 水浒传>, <Book: 钢铁怎样炼成>]>
正向查询 book_obj.表明小写.all()
Book-------------------------------------->Author
    <--------------------------------------
    反向查询 author_obj.表名小写_set.all()

2.5 基于双下划线的跨表查询 — 连标 join

2.5.1 一对一

1.查询罗贯中地址是什么

#方式1 正向查询
    addr1 = models.Author.objects.filter(name='罗贯中').values()
    print(addr1) #<QuerySet [{'id': 1, 'name': '罗贯中', 'age': 36, 'authorDetail_id': 1}]>
    addr = models.Author.objects.filter(name='罗贯中').values("authorDetail__addr",'name').first() #{'authorDetail__addr': '北京', 'name': '罗贯中'}
    print(addr) #<QuerySet [{'authorDetail__addr': '北京', 'name': '罗贯中'}]>
    #方式2 反向查询
    obj = models.AuthorDetail.objects.filter(author__name='罗贯中').values('addr','author__age') #<QuerySet [{'addr': '北京', 'author__age': 36}]>
    print(obj)

2.电话号码13854712314是谁

#方式1 正向查询
name_obj1 = models.AuthorDetail.objects.filter(telephone='13854712314').values('author__name') #<QuerySet [{'author__name': '吴承恩'}]>
print(name_obj1)

#方式2 反向查询
name_obj2 = models.Author.objects.filter(authorDetail__telephone='13854712314').values('name') #<QuerySet [{'name': '吴承恩'}]>
print(name_obj2)
2.5.2 一对多

1.查询西游记出版社是什么

# 方式1 正向查询
obj = models.Book.objects.filter(title='西游记').values('publishs__name') #<QuerySet [{'publishs__name': 'Cc出版社'}]>
print(obj)

# 方式2 反向查询
obj1 = models.Publish.objects.filter(book__title='西游记').values('name') #<QuerySet [{'publishs__name': 'Cc出版社'}]>
print(obj)

2.西安出版社出版了那些书

obj2 = models.Publish.objects.filter(name='西安出版社').values('book__title') #<QuerySet [{'book__title': '三国演义'}, {'book__title': '钢铁怎样炼成'}, {'book__title': '木棒怎样炼成'}]>
print(obj2)

obj3 = models.Book.objects.filter(publishs__name='西安出版社').values('title') #<QuerySet [{'title': '三国演义'}, {'title': '钢铁怎样炼成'}, {'title': '木棒怎样炼成'}]>
print(obj3)

3.钢铁怎样炼成是谁写的

obj4 = models.Book.objects.filter(title='钢铁怎样炼成').values('authors__name') #<QuerySet [{'authors__name': '罗贯中'}, {'authors__name': '吴承恩'}, {'authors__name': '李白'}]>
print(obj4)

obj5 = models.Author.objects.filter(book__title='钢铁怎样炼成').values('name') #<QuerySet [{'name': '罗贯中'}, {'name': '吴承恩'}, {'name': '李白'}]>
print(obj5)

4.李白写了那些书

obj6 = models.Author.objects.filter(name='李白').values('book__title') #<QuerySet [{'book__title': '红楼梦'}, {'book__title': '钢铁怎样炼成'}, {'book__title': '木棒怎样炼成'}]>
print(obj6)

obj7 = models.Book.objects.filter(authors__name='李白').values('title') #<QuerySet [{'title': '红楼梦'}, {'title': '钢铁怎样炼成'}, {'title': '木棒怎样炼成'}]>
print(obj7)
进阶 三表关联

1.查询西安出版社出版的书的名称以及作者名称

#正向查询
obj8 = models.Publish.objects.filter(name='西安出版社').values('book__authors__name','book__title')
print(obj8) #<QuerySet [{'book__authors__name': '罗贯中', 'book__title': '三国演义'}, {'book__authors__name': '罗贯中', 'book__title': '钢铁怎样炼成'}, {'book__authors__name': '罗贯中', 'book__title': '木棒怎样炼成'}, {'book__authors__name': '吴承恩', 'book__title': '钢铁怎样炼成'}, {'book__authors__name': '李白', 'book__title': '钢铁怎样炼成'}, {'book__authors__name': '李白', 'book__title': '木棒怎样炼成'}, {'book__authors__name': '苏轼', 'book__title': '三国演义'}]>
#反向查询
obj9 = models.Book.objects.filter(publishs__name='西安出版社').values('title','authors__name')
print(obj9) #<QuerySet [{'title': '三国演义', 'authors__name': '罗贯中'}, {'title': '钢铁怎样炼成', 'authors__name': '罗贯中'}, {'title': '木棒怎样炼成', 'authors__name': '罗贯中'}, {'title': '钢铁怎样炼成', 'authors__name': '吴承恩'}, {'title': '钢铁怎样炼成', 'authors__name': '李白'}, {'title': '木棒怎样炼成', 'authors__name': '李白'}, {'title': '三国演义', 'authors__name': '苏轼'}]>

obj10 = models.Author.objects.filter(book__publishs__name='西安出版社').values('name','book__title')
print(obj10) #<QuerySet [{'name': '罗贯中', 'book__title': '三国演义'}, {'name': '罗贯中', 'book__title': '钢铁怎样炼成'}, {'name': '罗贯中', 'book__title': '木棒怎样炼成'}, {'name': '吴承恩', 'book__title': '钢铁怎样炼成'}, {'name': '李白', 'book__title': '钢铁怎样炼成'}, {'name': '李白', 'book__title': '木棒怎样炼成'}, {'name': '苏轼', 'book__title': '三国演义'}]>
进阶2:

手机号以4结尾的作者,出版社过的所有书籍名称以及出版社名称

obj11 = models.AuthorDetail.objects.filter(telephone__endswith='4').values('author__book__title','author__book__publishs__name')
print(obj11) #<QuerySet [{'author__book__title': '水浒传', 'author__book__publishs__name': '新华出版社'}, {'author__book__title': '钢铁怎样炼成', 'author__book__publishs__name': '西安出版社'}, {'author__book__title': '西游记', 'author__book__publishs__name': 'Cc出版社'}]>
obj12 = models.Book.objects.filter(authors__authorDetail__telephone__endswith='4').values('title','publishs__name')
print(obj12) #<QuerySet [{'title': '西游记', 'publishs__name': 'Cc出版社'}, {'title': '水浒传', 'publishs__name': '新华出版社'}, {'title': '钢铁怎样炼成', 'publishs__name': '西安出版社'}]>

补充:
根据团队id查询用户详情信息

userProfile = UserProfile.objects.filter(user_id=user.id).first()
team = userProfile.team

# 两张表关联查询:根据外键team_id查询用户OneToOne profile表的信息
u_pro_all = UserProfile.objects.filter(team__id=team.id) 

# 三表关联查询:根据外键team_id查询user表信息,通过profile的team_id外键关联查询
u_detail_all = User.objects.filter(profile__team__id=team.id)
2.6 聚合查询、分组查询、F查询和Q查询

2.6.1 聚合

查询出所有图书的平均价格

#首先查询出数据,然后调aggregate这个方法里面传对应聚合函数,一旦调用了aggregate方法就不能调用queryset的方法,因为它返回的是字典
from django.db.models import Avg,Max,Min,Sum,Count
avg_price = models.Book.objects.all().aggregate(Avg('price')) #{'price__avg': 30.883333} dict
avg_price = models.Book.objects.all().aggregate(a=Avg('price')) #{'a': 30.883333} 可以给key起个别名 a
print(avg_price)
2.6.2 分组查询

首先在values里面写要分组的字段,分组完成使用分组函数annotate里面写聚会函数,注意如果values里面写了多个值,并且后面annotate,就是按照values里面的最后一个值分组
查询每个出版社出版书的平均价格

from django.db.models import Avg, Max, Min, Sum, Count
#方式1:
avg_price = models.Book.objects.values('publishs_id').annotate(a=Avg('price'))
#SELECT `app01_book`.`publishs_id`, AVG(`app01_book`.`price`) AS `a` FROM `app01_book` GROUP BY `app01_book`.`publishs_id` ORDER BY NULL LIMIT 21
# <QuerySet [{'publishs_id': 1, 'a': 53.2}, {'publishs_id': 2, 'a': 29.7}, {'publishs_id': 3, 'a': 21.5}]>
print(avg_price)
avg_price1 = models.Book.objects.values('publishs_id','nid').annotate(a=Avg('price'))
print(avg_price1)
#SELECT `app01_book`.`publishs_id`, `app01_book`.`nid`, AVG(`app01_book`.`price`) AS `a` FROM `app01_book` GROUP BY `app01_book`.`nid` ORDER BY NULL LIMIT 21;
#<QuerySet [{'publishs_id': 3, 'nid': 1, 'a': 13.5}, {'publishs_id': 3, 'nid': 2, 'a': 29.5}, {'publishs_id': 1, 'nid': 3, 'a': 53.2}, {'publishs_id': 2, 'nid': 4, 'a': 43.5}, {'publishs_id': 2, 'nid': 7, 'a': 17.8}, {'publishs_id': 2, 'nid': 8, 'a': 27.8}]>

#方式2:通过关联并且分组
ret = models.Publish.objects.annotate(a = Avg('book__price'))#<QuerySet [<Publish: Cc出版社>, <Publish: 新华出版社>, <Publish: 西安出版社>]>
print(ret.values()) #<QuerySet [{'id': 3, 'name': 'Cc出版社', 'city': '南京', 'email': 'cy@163.com', 'a': 21.5}, {'id': 1, 'name': '新华出版社', 'city': '上海', 'email': '352@163.com', 'a': 53.2}, {'id': 2, 'name': '西安出版社', 'city': '西安', 'email': 'xian@qq.com', 'a': 29.7}]>
print(ret.values('name','a')) #<QuerySet [{'name': 'Cc出版社', 'a': 21.5}, {'name': '新华出版社', 'a': 53.2}, {'name': '西安出版社', 'a': 29.7}]>

ret1 = models.Publish.objects.annotate(a = Avg('book__price')).values('name','a')
print(ret1)

#分组后过滤
ret2 = models.Publish.objects.values('book__publishs_id').annotate(a= Avg('book__price')).values().filter(a__gt=22)
print(ret2)

2.7 F查询与Q查询

2.71 F对象查询

F对象主要用于模型类的 A 字段属性与 B 字段属性两者的比较,即操作数据库中某一列的值。通常是对数据库中的字段值在不获取的情况下进行操作。F 对象内置在数据包django.db.models中,所以使用时需要提前导入。如下所示:

from django.db.models import F

语法格式

from django.db.models import F
F('字段名')

在使用F对象进行查询的时候需要注意:一个 F() 对象代表了一个 Model 的字段的值;F 对象可以在没有实际访问数据库获取数据值的情况下对字段的值进行引用。

Django 支持对 F对象引用字段的算术运算操作,并且运算符两边可以是具体的数值或者是另一个 F 对象,下面我们通过实例进一步认识 F 对象。

from django.db.models import F
from index.models import Book 
#给Book所有实例价格(retail_price)涨价20元 
Book.objects.all().update(retail_price=F('retail_price')+20) #获取该列所有值并加20
#利用传统的方法实现涨价20元
books = models.Book.objects.all() 
for book in books:     
    book.update(retail_price=book.retail_price+20)     
    book.save()

通过上述实例可以看出,使用 F 对象相对传统的方法要简单的多。那么如何通过 F 对象实现两个字段值(列)之间的比较呢?实例如下所示:

#对数据库中两个字段的值进行比较,列出哪儿些书的零售价高于定价 
books = Book.objects.filter(retail_price__gt=F('price')) 
for book in books: 
    print(book.title, '定价:', book.price, '现价:', book.retail_price)
2.7.2 Q对象查询

Q 对象相比 F 对象更加复杂一点,它主要应用于包含逻辑运算的复杂查询。Q 对象把关键字参数封装在一起,并传递给 filter、exclude、get 等查询的方法。多个 Q 对象之间可以使用&或者|运算符组合(符号分别表示与和或的关系),从而产生一个新的 Q 对象。当然也可以使用~(非)运算符来取反,从而实现NOT查询。Q 对象的导入方式如下所示:

from django.db.models import Q

语法格式

from django.db.models import Q
Q(条件1)|Q(条件2)  # 条件1成立或条件2成立
Q(条件1)&Q(条件2)  # 条件1和条件2同时成立
Q(条件1)&~Q(条件2)  # 条件1成立且条件2不成立
#...等

最简单的 Q 对象的使用方法是将单个字段类属性作为参数进行查询,实例如下:

#查询 书籍的title中包含有字母P的
In [1]: from index.models import Book
In [2]: from django.db.models import Q
In [4]: Book.objects.filter(Q(title__contains="P"))
Out[4]: <QuerySet [<Book: Book object (1)>]>

但时 Q 对象在实际的应用中往往是较为复杂的,和常和逻辑运算符一起使用,如下所示:

#多个Q对象组合
from index.models import Book
from django.db.models import Q 
#查找c语言中文网出版的书或价格低于35的书 
Book.objects.filter(Q(retail_price__lt=35)|Q(pub_id='2'))#两个Q对象是或者的逻辑关系
#查找不是c语言中文出版的书且价格低于45的书 
Book.objects.filter(Q(retail_price__lt=45)&~Q(pub_id='2'))#条件1成立条件2不成立

注意此时的的字段为pub_id 因为此此段建立一对多的关联关系,不可以直接使用C语言中文网,不然会发生如下报错:

ValueError: invalid literal for int() with base 10: 'c语言中文网出版'

报错原因是:字段的条件约束为int类型,但是给了一个字符串类型。

Q 对象也可以与类属性的字段名组合在一起使用,但是在这种情况下,Django 规定,Q 对象必须放在前面,示例如下:

Book.objects.filter(Q(price__lte=100),title__icontains="p")#组合使用
<QuerySet [<Book: Book object (1)>]>

此外,还有一篇关于ORM的实操demo(含实操代码):

以上就是关于django ORM的一些增删改查的基本操作,希望能对你有所帮助,还望不吝三连哦 ^ - ^


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年12月07日 0

暂无评论

推荐阅读
OHxphH6Es7WL