欢迎第 位访客

Django-7 ORM多表操作

 多表操作

 创建模型

 

实例:我们来假定下面这些概念,字段和关系

作者模型:一个作者有姓名和年龄。

作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)

出版商模型:出版商有名称,所在城市以及email。

书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。

模型建立如下:

 

from django.db import models

# Create your models here.

# 出版社表

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()

# 作者详情表

class AuthorDetail(models.Model):
    uid = models.AutoField(primary_key=True)
    name= models.CharField(max_length=32)
    tel = models.BigIntegerField()
    addr = models.CharField(max_length=255)



# 作者表

class Author(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.IntegerField()
#     建立外键关联(一对一关系:建立在哪张表都可以,一对一要使用onetoone)
#     AuthorDetail = models.OneToOneField(to='AuthorDetail',to_field='nid')  如果不添加一个on_delete,会在迁移数据库时报错
    AuthorDetail = models.OneToOneField(to='AuthorDetail',to_field='uid',on_delete=models.CASCADE)

#书籍表

class Book(models.Model):
    bid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8,decimal_places=2)

    # 建立外键关联(一对多关系:一个出版社可以出版多本书籍,关联要建在多的那张表中(book表))
    publish = models.ForeignKey(to='Publish',to_field='nid',on_delete=models.CASCADE)


# 多对多关系:manytomany,只需要to另外一张表就OK了,

    author = models.ManyToManyField(to="Author")

    '''
    上面的author语句相当于执行下面的sql语句:
    create table book2author(
        id int auto_increment,
        book_id int ,
        author_id int,
        foreign key (book_id) references book(id),
        foreign key (author_id) refernces author(id)    
    
    )
    
    
    '''
表字段模型

然后通过 python manage.py makemigrations和 python manage.py migrate来迁移数据库(记得要导入pymysql和在settings中设置DATABASE)

注意事项:

  •  表的名称myapp_modelName,是根据 模型中的元数据自动生成的,也可以覆写为别的名称  
  •  id 字段是自动添加的
  •  对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名
  •  这个例子中的CREATE TABLE SQL 语句使用PostgreSQL 语法格式,要注意的是Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句。
  •  定义好模型之后,你需要告诉Django _使用_这些模型。你要做的就是修改配置文件中的INSTALL_APPSZ中设置,在其中添加models.py所在应用的名称。
  • 外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None 。

 添加表记录

一对一

 # publish = Publish.objects.create(name='人民出版社',city="北京",email="people@people.com")
    # print(publish)
    #--------------------------------添加一对多关联记录------------------------------------
    # 添加书籍和出版社信息
    #     方式一:
    # book_obj = Book.objects.create(name="三国演义",price=85.5,publish_id=1)
    # print(book_obj)
    # print(book_obj.publish)  # 注意:这个publish是与这本书籍相关联的出版社对象
    #     方式二:
    # pub_obj = Publish.objects.filter(name='人民出版社').first()
    # orm会自动把最后一个参数pub_obj的主键取出
    # book_obj = Book.objects.create(name="三国演义2", price=85.5, publish=pub_obj)
    # print(book_obj.name)
    '''
        在添加书籍时,无论是是以publish_id=1,还是publish=pub_obj,最终都会生成一个publish对象
    
    '''


    # print(book_obj.publish) #注意:book_obj.publish是与这本书籍相关联的出版社对象
    # print(book_obj.publish.email)  #取书籍对应的email

多对多


#
--------------------------------添加多对多关联记录------------------------------------ # book_obj = Book.objects.create(name='白雪公主',price=99,publish_id=1) # 取作者对象,注意,这里要取的是一个obj,而不是一个queryset对象,如果是qset对象,则会报错.如果用qset对象来添加的话,可以使用book_obj.author.add(nick.first().id,joseph.first().id) # nick = Author.objects.filter(name='nick').first() # joseph = Author.objects.filter(name='joseph').first() # print(nick.first().id) # 取obj对象方法二: # nick = Author.objects.get(name='nick') # joseph = Author.objects.get(name='joseph') # print(nick) # # book_obj.author.add(nick,joseph) # book_obj.author.add(1,2,3) # book_obj.author.add(*[1,2,3]) # book_obj.author.add(nick.first().id,joseph.first().id) # # 移除对应关系 # book_obj = Book.objects.filter(name='追风筝的人').first() # book_obj.author.remove(2) # 移除书籍所有的作者对应信息 # book_obj.author.clear() # 查询与书籍相关的所有作者对象,qset对象,book_obj.author.all()是一个作者集合的queryset对象 book_obj = Book.objects.filter(name='白雪公主').first() ret = book_obj.author.all() print(ret.values_list('name'))

基于对象的跨表查询

'''
两张有关系的表,A和B,关联字段在A表中.
正向查询:从A表查询B表中的内容,正向查询按 字段
反向查询:从B表查询A表中的内容,反向查询按 表名小写_set.all()


一对多查询:
    正向查询:从A表查询B表中的内容,正向查询按 字段
    反向查询:从B表查询A表中的内容,反向查询按 表名小写_set.all()
           正向查询: book_obj.publish.email:关联属性在book表中是publish,所以查email用publish.email,也就是关联属性(对象)的属性
    book(关联对象:publish)------------------------------------>publish
           反向查询:book_obj.book_set.all()
    publish--------------------------------->book
    
多对多查询:
    正向查询:从A表查询B表中的内容,正向查询按 字段
    反向查询:从B表查询A表中的内容,反向查询按 表名小写_set.all()
           正向查询(查书籍作者):  book_obj.author.all()
    book(关联对象:author)-------------------------------------------------------------->author
           反向查询(查作者所有书籍):author.book_set.all()
    author------------------------------------------------------------>book    
    
一对一查询:
    正向查询:从A表查询B表中的内容,正向查询按 字段
    反向查询:从B表查询A表中的内容,反向查询按 表名小写
                             正向查询(查作者详细信息): author.AuthorDetail.tel
    author(关联对象:authorDetail)-------------------------------------------->authorDetail
                                反向查询(查作者所有书籍):auth.name
    authorDetail------------------------------------------------------------>author  
    
'''

 

一对多表记录查询

    # 一对多的正向查询: 1 查询白雪公主这本书的出版的邮箱


    ret =Book.objects.filter(name='白雪公主').first().publish.email
    print('<白雪公主>的出版社email:',ret)
    # 一对多的反向查询:2 查询人民出版社出版的所有书籍

    # ret = Publish.objects.filter(name='人民出版社').first().book_set.all()
    book_obj = Publish.objects.filter(name='人民出版社').first()
    book_obj.book_set.all()
    print(book_obj.book_set.all())
    for obj in ret:
        print(obj.name,obj.publish.name)

查询语法:

'''
两张有关系的表,A和B,关联字段在A表中.
正向查询:从A表查询B表中的内容,正向查询按 字段
反向查询:从B表查询A表中的内容,反向查询按 表名小写_set.all()


一对多查询:
    正向查询:从A表查询B表中的内容,正向查询按 字段
    反向查询:从B表查询A表中的内容,反向查询按 表名小写_set.all()
           正向查询: book_obj.publish.email:关联属性在book表中是publish,所以查email用publish.email,也就是关联属性(对象)的属性
    book(关联对象:publish)------------------------------------>publish
           反向查询:book_obj.book_set.all()
    publish--------------------------------->book
   
'''

 

多对多正反向查询

   # 1.正向查询:查询白雪公主这本书的作者
    book_obj = Book.objects.filter(name='白雪公主').first()
    book_obj.author.all() #书籍的所有作者信息,qset对象
    for obj in book_obj.author.all():
        print(obj.name)

    # 2.反向查询:查询nick出版的所有书籍

    author = Author.objects.filter(name='nick').first()
    ret = author.book_set.all()
    for obj in ret:
        print(obj.name)

语法:

'''
多对多查询:
    正向查询:从A表查询B表中的内容,正向查询按 字段
    反向查询:从B表查询A表中的内容,反向查询按 表名小写_set.all()
           正向查询(查书籍作者):  book_obj.author.all()
    book(关联对象:author)-------------------------------------------------------------->author
           反向查询(查作者所有书籍):author.book_set.all()
    author------------------------------------------------------------>book    
    
'''

一对一正反向查询

# 1.正向查询:查询作者nick的详细信息
    author = Author.objects.filter(name='nick').first()
    print(author.AuthorDetail.tel)

    # 2.反向查询:查询作者id=2的作者名称
    auth = Author.objects.filter(id=2).first()  #拿到的是一个obj对象,可以直接通过点语法获取属性
    print(auth.name)

语法:

'''
一对一查询:
    正向查询:从A表查询B表中的内容,正向查询按 字段
    反向查询:从B表查询A表中的内容,反向查询按 表名小写
                             正向查询(查作者详细信息): author.AuthorDetail.tel
    author(关联对象:authorDetail)-------------------------------------------->authorDetail
                                反向查询(查作者所有书籍):auth.name
    authorDetail------------------------------------------------------------>author  


'''

 基于双下划线的跨表查询(join)

'''

基于双下划线的多表查询
正向查询按字段
反向查询按表名小写,按表名小写是告诉ORM用来join哪个表

1.正向查询白雪公主这本书的出版社名称
SQL语句:
select app01_publish.name from app01_book inner join app01_publish 
on app01_book.publish_id = app01_publish.nid
where app01_book.name='白雪公主';

ORM语句:

ret = book.objects.filter(name='白雪公主').values('publish__name')
注意,这是正向查询,所以这里是publish是指的book表中的publish字段(其实拿到的还是Publish表)
,而不是Publish表

filter(name='白雪公主'):相当于sql中的where语句

values():则相当于select的内容

1.反向查询白雪公主这本书的出版社名称

SQL:
select app01_publish.name from app01_publish inner join app01_book
on app01_publish.nid = app01_book.publish_id
where app01_book.name='白雪公主';

ORM语句:
  ret =Publish.objects.filter(book__name='白雪公主').values('name')

filter(book__name='白雪公主'):告诉ORM引擎,inner join book表,然后查询name=白雪公主的书籍
values('name'):拿到和白雪公主这本书籍相对应的出版社的名字

'''

 

 

跨表查询的本质:把多个表join成一张表,然后进行分组查询!

 

跨表查询总结:
每个后表模型.objects.values('基表主键 pk').annotate(聚合函数(关联表__查询字段)).values('表模型的所拥有的字段','聚合函数字段')
例如:查询每个作者的名字以及出版过的书籍的最高价格
这里每个后面的字符,就是基表,这个基表就是作者表

 

 

 

 

 

 

 

 

 

一对多正向查询

  # 正向查询:查白雪公主这本书籍的出版社名称
    ret = Book.objects.filter(name='白雪公主').values('publish__name')
    print(ret)
    for name in ret:
        print(name.get('publish__name'))
'''
正向查询按字段
反向查询按表名小写,按表名小写是告诉ORM用来join哪个表

1.正向查询白雪公主这本书的出版社名称
SQL语句:
select app01_publish.name from app01_book inner join app01_publish
on app01_book.publish_id = app01_publish.nid
where app01_book.name='白雪公主';

ORM语句:

ret = book.objects.filter(name='白雪公主').values('publish__name')
注意,这是正向查询,所以这里是publish是指的book表中的publish字段(其实拿到的还是Publish)
,而不是Publish

filter(name='白雪公主'):相当于sql中的where语句

values():则相当于select的内容
'''
 

 

一对多反向查询

 # 反向查询:查白雪公主这本书籍的出版社名称
 ret =Publish.objects.filter(book__name='白雪公主').values('name')
    print(ret)
'''
1.反向查询白雪公主这本书的出版社名称

SQL:
select app01_publish.name from app01_publish inner join app01_book
on app01_publish.nid = app01_book.publish_id
where app01_book.name='白雪公主';

ORM语句:
  ret =Publish.objects.filter(book__name='白雪公主').values('name')

filter(book__name='白雪公主'):告诉ORM引擎,inner join book表,然后查询name=白雪公主的书籍
values('name'):拿到和白雪公主这本书籍相对应的出版社的名字

'''

 多对多正向查询

#--------------多对多跨表查询

    # 正向多表查询:查询白雪公主这本书的所有作者的名字
    '''正向查询按关联表的字段,下面是SQL语句
    select app01_author.name
    from app01_book inner join app01_book_author on app01_book.bid = app01_book_author.book_id
    inner  join app01_author on app01_book_author.author_id=app01_author.id
    where app01_book.name='白雪公主';
    '''
    ret = Book.objects.filter(name='白雪公主').values('author__name')#这里的author是Book表中关联的字段
    print(ret)

   

多对多反向查询

 # 反向多表查询:查询白雪公主这本书的所有作者的名字
    '''
    SQL:
    select app01_author.name from app01_author inner  join app01_book_author
    on app01_author.id = app01_book_author.author_id
    inner join app01_book
    on app01_book.bid = app01_book_author.book_id
    where app01_book.name='白雪公主';
  反向查询按表名__字段值
''' ret = Author.objects.filter(book__name='白雪公主').values('name') print(ret)

 一对一正向查询

 #1.正向查询:查询作者nick的电话号码
    '''正向是按照字段名称(必须和对应的字段名称大小写一致)
    流程:通过author表join与其关联的authordetail表,按照字段AuthorDetail(我在author表中设置的字段就是大写开头的),告诉ORM引擎
    inner join authordetail 表,使用'表名__tel'来取出所需要的电话号码
    '''
    ret =Author.objects.filter(name='nick').values('AuthorDetail__tel')
    print(ret)

一对一反向查询

    #2.反向查询:查询作者nick的电话号码
    '''反向查询:按照表名小写.values('tel'),相当于select tel,filter() 相当于 where
    流程:通过authordetail表join与其关联的author表,按照表名小写(author__name),告诉ORM引擎来inner join author表
    
    '''
    ret=AuthorDetail.objects.filter(author__name='nick').values('tel')
    print(ret)

 跨多表查询

#1.查询手机号码以133开头的作者姓名,出版过的书籍,以及出版社名称
    '''
    SQL:
    select app01_book.name as book_name,app01_publish.name as publish_name,app01_author.name as author_name
    from app01_book inner join app01_book_author
    on app01_book.bid = app01_book_author.book_id
    inner join app01_author
    on app01_book_author.author_id = app01_author.id
    inner join app01_publish
    on app01_book.publish_id = app01_publish.nid
    inner join app01_authordetail
    on app01_authordetail.uid = app01_author.id
    where app01_authordetail.tel like "%133%";  
    
    
    '''

    ret = Book.objects.filter(author__AuthorDetail__tel__startswith='133').values('publish__name','author__name')
    print(ret)

 

posted @ 2019-04-25 10:27  大橡皮  阅读(163)  评论(0编辑  收藏  举报