Django-ORM-3:orm跨表查询,子查询(基于对象的跨表查询)和联表查询(基于双下划线的跨表查询)

正反向的概念:

外键在哪里,哪里就是正向。

1、由外键所在的表去查其他表,这就是正向

2、由没有外键所在的表去查,这就是反向

例子

书和出版社:外键设置在书表中

1、通过书表去查对应的出版社,这是正向查询
2、通过出版社表去查出版的所有书,这就是反向

正向查询按字段
	在多对多关系表中,正方向也要加all()

反向查询按表名小写.__set
	查询结果是多个时,再加上  .all()
子查询口诀
  • 正向查询:点外键
    • 返回结果是多个,需要加 .all()
  • 反向查询:表名小写
    • 多个结果时,需要加 _set.all()
  • 两行代码实现
    • 先拿到当前表指定的数据对象
    • 通过该数据对象+.all()或_set.all()实现跨表查,拿到另一张的整张表数据
联表查询口诀:
  • 正向查询:外键__字段
  • 反向查询:表名小写__字段
  • 一行代码实现
    • 通过filter拿到当前表指定的数据
    • 再通过values ,获取当前表字段和跨表字段
子查询:基于对象的跨表查询,分步骤

正向查询:点外键字段

​ 特殊:查询结果 是多个时加 .all(), (多对多关系表的正向查询要加all())

反向查询:点表名小写_set.all()

​ 特殊:查询结果只有一个时,直接点表名(一对一关系表)

联表查询:基于双下划线的跨表查询,一行代码搞定

多表查询

一、子查询(基于对象的跨表查询)

1、一对一关系表:

表数据,人与人的详细信息,外键设置在人表上


class Person(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=20)
    persondetail = models.OneToOneField(to='PersonDetail',on_delete=models.CASCADE,db_constraint=False)

    def __str__(self):
        return self.name


class PersonDetail(models.Model):
    id = models.AutoField(primary_key=True)
    phone = models.CharField(max_length=13)
    address = models.CharField(max_length=100,null=True)

    def __str__(self):
        return self.phone
1.1、子查询的反向查询
#外键在person表中,通过个人详细信息找到人
persondetail = models.PersonDetail.objects.get(id=1)

#反向查询,一对一的反向查询,直接点Person表名小写
person = persondetail.person
1.2、子查询的正向查询
#外键在person表中
person = models.Person.objects.get(id=1)
#正向查询,直接点外键字段获取表对象
persondetail = person.persondetail
print(persondetail)
2、一对多关系表:

表数据:书与出版社,一对多关系

class Book(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=20)
    publish = models.ForeignKey(to='Publish',on_delete=models.DO_NOTHING,db_constraint=False)
    
    def __str__(self):
        return self.name
    
    
class Publish(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=20)
    
    def __str__(self):
        return self.name

2.1、子查询的反向查询

'''
反向查询,是给没有外键的表使用
1、一对多关系表中,外键在多上,所以这种方式的反向查询,一定要加上all()
2、对于多对多关系表中,反向查询也一定要加上all()
3、对于一对一关系表中,反向查询就不需要加all()
'''

#外键设置在book上,查询id=1的出版社出版的所有书

publish = models.Publish.objects.filter(id=1).first()
#反向查询使用对方表小写+_set,数据有多条再加 .all()
books = publish.book_set.all()
for book in books:
	print(book)

2.2、子查询的正向查询

#书与出版社是一对多,外键在书上,查询某本书是哪个出版社出版的,是正向查询
book = models.Books.objects.filter(id=3).first()
#通过外键字段获取数据对象
publish = book.publish
print(publish)
3、多对多的关系表

数据准备:书和作者,是多对多的关系,是通过第三张表实现多对多关系,哪个表的字段设置了ManyToManyField的,该字段就算是外键了。

外键设置到书上了

半自动生成第三张表
class Book(models.Model):
    id = models.AutoField(primary_key=True,verbose_name='主键')
    name = models.CharField(max_length=100,verbose_name='书名')
    price = models.DecimalField(max_digits=10,decimal_places=2,verbose_name='价格')
    authors = models.ManyToManyField(to='Author',through='Book2Author',through_fields=('book','author'))


class Author(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=50,verbose_name='作者')
    address = models.CharField(max_length=100,verbose_name='作者地址')


class Book2Author(models.Model):
    id = models.AutoField(primary_key=True,verbose_name='书作者关系主键')
    book = models.ForeignKey(to='Book',on_delete=models.DO_NOTHING,db_constraint=False)
    author = models.ForeignKey(to='Author',on_delete=models.DO_NOTHING,db_constraint=False)
    create_time = models.DateTimeField(auto_now_add=True,verbose_name='创建时间')

    class Meta:
        #书与作者联合唯一,book=1  和 author=1在表中只记录一次
        unique_together=['book','author']

3.1、子查询的反向查询
#查看某个作者的写过的所有书,外键在书表中
author = models.Author.objects.get(id=1)
#多对多都是:表名小写_set.all()
books = author.book_set.all()
print(books)
3.2、子查询的正向查询
book = models.Book.objects.get(id=2)
#多对多的正向查询,需要加all()  因为是多对多关系,系统默认就是一本书有多个作者的,即使已经知道该书只有一个作者也是需要使用all()的
authors = book.authors.all()
print(authors)
注意,输入数据对象时:输出了index.Author.None

你的查询没有错误,只是少了.all() , 因为你这个查询默认是返回多条数据的,所以需要使用all()方法来拿。

子查询的总结
1、正向查询时,通过外键跨表,返回多个数据时,要使用.all()获取。[只有多对多关系需要.all(),系统默认会拿到多个数据]

2、反向查询时,通过表名小写[_set.all()],返回多个数据时就必须加_set.all(). [在一对一关系表中,就直接表名小写就可以了]、

3、带all()的结果是列表套字典
4、不带all()的结果就是query对象了

二、联表查询(基于双下划线的跨表查询)

1、一对一关系表查询

正向查询:通过 外键__字段 获取另一张的字段数据

反向查询:通过 表名小写__字段 获取另一张的字段数据

数据准备:人和人的详细信息,一对一关系,外键在人表上。

1.1、正向查询

#查询id=1的人的姓名,地址和电话(在另一张表上),正向查询直接外键跨到另一张表中,通过__字段,获取另一张表的字段数据
person = models.Person.objects.filter(id=1).values('persondetail__address','persondetail__phone','name')
#下面拿到的是一个字典
print(person.first())
print(person.first().get('persondetail__address'))

1.2、反向查询

#通过个人详细信息找到对应的名字,反向查询,通过表名小写进行跨表,__字段获取指定字段数据
persondetail = models.PersonDetail.objects.filter(id=2).values('person__name','address','phone')
#拿到的是一个列表套字段,values决定的
print(persondetail)
#拿到个人的名字  print(persondetail.first().get('person__name'))
2、一对多关系的联表查询,基于双下划线

数据准备:books 和publish,多对一关系

class Books(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=20)
    publish = models.ForeignKey(to='Publish',on_delete=models.DO_NOTHING,db_constraint=False)

    def __str__(self):
        return self.name


class Publish(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=20)

    def __str__(self):
        return self.name

1、正向查询

'''拿到聊斋书的出版社信息和聊斋书的信息'''
#正向查询通过外键__字段,拿到另一张表的字段数据,多查一,返回一个数据,
books = models.Books.objects.filter(name='聊斋').values('name','id','publish__name','publish__id')
print(books)

结果:格式列表套字典,vlaues决定
<QuerySet [{'name': '聊斋', 'id': 3, 'publish__name': '南京出版社', 'publish__id': 1}]>

2、反向查询

'''拿到南京出版社出版的所有书籍'''
#反向查询,通过表名小写__字段,获取另一张表名中的字段数据
publish = models.Publish.objects.filter(name='南京出版社').values('name','id','books__name','books__id')
print(publish)
#结果:列表套字典,values决定
<QuerySet [{'name': '南京出版社', 'id': 1, 'books__name': '水浒传', 'books__id': 5}, {'name': '南京出版社', 'id': 1, 'books__name': '西游记', 'books__id': 4}, {'name': '南京出版社', 'id': 1, 'books__name': '聊斋', 'books__id': 3}]>
3、多对多关系的联表查询,基于双下划线

数据准备: book和author,外键authors在book上

class Book(models.Model):
    id = models.AutoField(primary_key=True,verbose_name='主键')
    name = models.CharField(max_length=100,verbose_name='书名')
    price = models.DecimalField(max_digits=10,decimal_places=2,verbose_name='价格')
    authors = models.ManyToManyField(to='Author',through='Book2Author',through_fields=('book','author'))
    def __str__(self):
        return self.name


class Author(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=50,verbose_name='作者')
    address = models.CharField(max_length=100,verbose_name='作者地址')

    def __str__(self):
        return self.name


class Book2Author(models.Model):
    id = models.AutoField(primary_key=True,verbose_name='书作者关系主键')
    book = models.ForeignKey(to='Book',on_delete=models.DO_NOTHING,db_constraint=False)
    author = models.ForeignKey(to='Author',on_delete=models.DO_NOTHING,db_constraint=False)
    create_time = models.DateTimeField(auto_now_add=True,verbose_name='创建时间')

    class Meta:
        #书与作者联合唯一,book=1  和 author=1在表中只记录一次
        unique_together=['book','author']

1、正向查询

#正向查询,通过  外键__字段  ,返回的是一个列表套字典,即使拿到的一个数据,也是这种格式
'''拿到西游记的所以作者'''
book = models.Book.objects.filter(name__startswith='西游').values('name','authors__name','price')
    print(book)

打印:
<QuerySet [{'name': '西游记', 'authors__name': 'lhz', 'price': Decimal('58.40')}, {'name': '西游记', 'authors__name': 'zzh', 'price': Decimal('58.40')}]>

    print(book.first().get('authors_name'))

2、反向查询

'''找到lhz作者的所有书籍'''
#外键在book中,不在author中
#反向查询通过 表名小写__字段名
author = models.Author.objects.filter(name__startswith='lhz').values('name','book__name','book__price')

print(author)
打印:
<QuerySet [{'name': 'lhz', 'book__name': '水浒传', 'book__price': Decimal('56.40')}, {'name': 'lhz', 'book__name': '西游记', 'book__price': Decimal('58.40')}]>
   
联表查询总结:

1、通过filter过滤当前表数据

2、通过values或values_list 来获取指定 的字段数据 (一般使用values,结果列表套字典),拿到当前表字段或通过外键/表名小写__字段拿到跨表的字段数据。

3、正向查询,直接通过外键__字段,获取跨表的数据

4、反向查询,直接通过表名小写__字段,获取跨表的数据

5、无论正向还是反向,结果都是被列表套住的字典或元组。

你可能感兴趣的:(orm,django)