Django基础四之Django模型层(ORM)多表操作

一,创建模型

1.1 表和表之间的关系

一对一、多对一、多对多 ,用book表和publish表自己来想想关系,想想里面的操作,加外键约束和不加外键约束的区别,一对一的外键约束是在一对多的约束上加上唯一约束。

例如:

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

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

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

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

建立模型:

from django.db import models

# Create your models here.


class Author(models.Model): #比较常用的信息放到这个表里面
    nid = models.AutoField(primary_key=True)
    name=models.CharField( max_length=32)
    age=models.IntegerField()

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

class AuthorDetail(models.Model):#不常用的放到这个表里面

    nid = models.AutoField(primary_key=True)
    birthday=models.DateField()
    telephone=models.BigIntegerField()
    addr=models.CharField( max_length=64)

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

#多对多的表关系,我们学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)

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

 1.2 多对多表的三种创建方式

方式一:自行创建第三张表

class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="书名")


class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="作者姓名")


# 自己创建第三张表,分别通过外键关联书和作者
class Author2Book(models.Model):
    author = models.ForeignKey(to="Author")
    book = models.ForeignKey(to="Book")

    class Meta:
        unique_together = ("author", "book")

 

方式二:通过ManyToManyField自动创建第三张表

class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="书名")


# 通过ORM自带的ManyToManyField自动创建第三张表
class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="作者姓名")
    books = models.ManyToManyField(to="Book", related_name="authors")  #自动生成的第三张表我们是没有办法添加其他字段的

 

方式三:设置ManyToManyField并指定自行创建的第三张表(中介模型)

class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="书名")


# 自己创建第三张表,并通过ManyToManyField指定关联
class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="作者姓名")
    books = models.ManyToManyField(to="Book", through="Author2Book", through_fields=("author", "book"))
    # through_fields接受一个2元组('field1','field2'):
    # 其中field1是定义ManyToManyField的模型外键的名(author),field2是关联目标模型(book)的外键名。


class Author2Book(models.Model):
    author = models.ForeignKey(to="Author")
    book = models.ForeignKey(to="Book")
    #可以扩展其他的字段了
    class Meta:
        unique_together = ("author", "book")
View Code

 

注意:

  当我们需要在第三张关系表中存储额外的字段时,就要使用第三种方式,第三种方式还是可以使用多对多关联关系操作的接口(all、add、clear等等)

  当我们使用第一种方式创建多对多关联关系时,就无法使用orm提供的set、add、remove、clear方法来管理多对多的关系了。

创建一对一关系字段时的一些参数

to
    设置要关联的表。

to_field
    设置要关联的字段。
    
on_delete
    同ForeignKey字段。

创建一对一关系字段时的一些参数
View Code

 

创建一对多关系字段时的一些参数

to
    设置要关联的表

to_field
    设置要关联的表的字段

related_name
    反向操作时,使用的字段名,用于代替原反向查询时的'表名_set'。
related_query_name
    反向查询操作时,使用的连接前缀,用于替换表名。

on_delete
    当删除关联表中的数据时,当前表与其关联的行的行为。

创建一对多关系字段时的一些参数
View Code

 

创建多对多关系字段时的一些参数

多对多的参数:
    to
        设置要关联的表

    related_name
        同ForeignKey字段。

    related_query_name
        同ForeignKey字段。
    through
        在使用ManyToManyField字段时,Django将自动生成一张表来管理多对多的关联关系。

        但我们也可以手动创建第三张表来管理多对多关系,此时就需要通过        
    through来指定第三张表的表名。

    through_fields
        设置关联的字段。

    db_table
        默认创建第三张表时,数据库中表的名称。            

创建多对多字段时的一些参数
View Code

创建表时的一些元信息设置

元信息
    ORM对应的类里面包含另一个Meta类,而Meta类封装了一些数据库的信息。主要字段如下:
class Author2Book(models.Model):
    author = models.ForeignKey(to="Author")
    book = models.ForeignKey(to="Book")
    class Meta:
        unique_together = ("author", "book")

db_table
    ORM在数据库中的表名默认是 app_类名,可以通过db_table可以重写表名。db_table = 'book_model'

index_together
    联合索引。

unique_together
    联合唯一索引。

ordering
    指定默认按什么字段排序。
    ordering = ['pub_date',]
    只有设置了该属性,我们查询到的结果才可以被reverse(),否则是能对排序了的结果进行反转(order_by()方法排序过的数据)

创建表时的一些元信息设置
View Code

获取元信息,可以通过model对象._meta.verbose_name等获取自己通过verbose_name指定的表名,model对象._meta.model_name获取小写的表名,还有model对象.app_label可以获取这个对象的app应用名等等操作。例如:book_obj = models.Book.objects.get(id=1),book_obj._meta.model_name。

关于db_column和verbose_name

  1.指定字段名: 在定义字段的时候,增加参数db_column=’real_field’; 

  2.指定表名: 在model的class中,添加Meta类,在Meta类中指定表名db_table 

  例如在某个models.py文件中,有一个类叫Info:

class Info(models.Model):  
    ''''' 
            信息统计 
    '''  
    app_id = models.ForeignKey(App)  
    app_name = models.CharField(verbose_name='应用名',  max_length=32, db_column='app_name2')  
  
    class Meta:  
        db_table = 'info'  
        verbose_name = '信息统计'  
        verbose_name_plural = '信息统计'

  其中db_column指定了对应的字段名,db_table指定了对应的表明; 

  如果不这样指定,字段名默认为app_name, 而表明默认为app名+类名: [app_name]_info.

  verbose_name指定在admin管理界面中显示中文;verbose_name表示单数形式的显示,verbose_name_plural表示复数形式的显示;中文的单数和复数一般不作区别。

注意事项:

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

   咱们的表里面包含了一对一、一对多、多对多的关系,我们基于这几个表来练习,将来无论有多少张表,都逃脱不了这三个关系,操作起来都是一样的。

关于on_delete

on_delete
当删除关联表中的数据时,当前表与其关联的行的行为。

models.CASCADE
删除关联数据,与之关联也删除


models.DO_NOTHING
删除关联数据,引发错误IntegrityError


models.PROTECT
删除关联数据,引发错误ProtectedError


models.SET_NULL
删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)


models.SET_DEFAULT
删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)


models.SET

删除关联数据,
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)

关于on_delete参数
View Code

ForeignKey的db_contraint参数

关系和约束大家要搞清楚,我不加外键能不能表示两个表之间的关系啊,当然可以

但是我们就不能使用ORM外键相关的方法了,所以我们单纯的将外键换成一个其他字段类型,只是单纯的存着另外一个关联表的主键值是不能使用ORM外键方法的。

#db_constraint=False只加两者的关系,没有强制约束的效果,并且ORM外键相关的接口(方法)还能使用,所以如果将来公司让你建立外键,并且不能有强制的约束关系,那么就可以将这个参数改为False
    customer = models.ForeignKey(verbose_name='关联客户', to='Customer',db_constraint=False)
View Code

 

二,表的增删改查

在modles中创建modle对象:

from django.db import models


# Create your models here.
class Author(models.Model):  # 常用字段
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    authorDetail = models.OneToOneField(to="AuthorDetail",
                                        on_delete=models.CASCADE,  # Django2.0,需要写on_delete=models.CASCADE 级联删除
                                        # on_delete = models.SET_NULL,  # Django2.0,需要写on_delete=models.SET_NULL 不做级联删除
                                        )  # 一对一的作者详细信息表

    # 一对一到AuthorDetail表生成为表字段后,会自动变为authorDetail_id 这样有一个名称
    # 外键字段 --外键字段名_id
    def __str__(self):
        return self.name


class AuthorDetail(models.Model):  # 不常用字段
    birthday = models.DateField()
    # telephone = models.BigIntegerField()  # 长整型
    telephone = models.CharField(max_length=32)
    addr = models.CharField(max_length=64)

    def __str__(self):
        return self.addr


# 出版社 和书籍表是一对多的关系
class Publish(models.Model):
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)
    email = models.EmailField()  # 实际上是charfield

    def __str__(self):
        return self.name


# 书籍表
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)
    publishs = models.ForeignKey(to='Publish', on_delete=models.CASCADE)  # 书籍表和出版社表示多对一关系,需在多的一方加上 models.ForeignKey()
    authors = models.ManyToManyField(to='Author')  # 作者表和书籍表示多对多关系

    def __str__(self):
        return self.title
modle对象

 

2.1,表的增加

单表的增加

new_author_detail = models.AuthorDetail.objects.create(
        birthday='1923-08-08',
        telephone='123456789',
        addr='三国中的蜀国'
    )

 

一对一表的增加

 # 方式一
    # 直接用类的属性=新建modle对象
    models.Author.objects.create(
        name="张飞",
        age=345,
        authorDetail=new_author_detail
    #
    #     authorDetail=obj
    )
    obj = models.AuthorDetail.objects.filter(addr='广东深圳').first()
    # queryset = models.AuthorDetail.objects.filter(addr='广东深圳')
    # 方式二  常用
    # 直接用数据库中的字段
    print(obj)
    models.Author.objects.create(
        name="张飞2",
        age=345,
        authorDetail_id=obj.id
    )

 

一对多表的增加

# 方式一,使用对象
    obj = models.Publish.objects.get(id=2)
    models.Book.objects.create(
        title='资本论',
        publishDate = '1923-02-04',
        price = '67',
        # publishs= models.Publish.objects.get(id = 2)
        publishs= obj
    )
    # # 方式二:常用
    obj = models.Publish.objects.get(id=2)
    models.Book.objects.create(
        title='资本论2',
        publishDate='1923-02-04',
        price='67',
        # publishs= models.Publish.objects.get(id = 2)
        publishs_id=obj.id
    )

 

多对多增加

 # 方式一:常用
    book_obj = models.Book.objects.get(nid=3)
    # book_obj.authors.add([1,2])  # 会报错TypeError: unhashable type: 'list',所以要加* 打散。
    book_obj.authors.add(*[1,2])

    # 方式二:
    author1 = models.Author.objects.get(id=2)
    author2 = models.Author.objects.get(id=4)
    book_obj = models.Book.objects.get(nid=4)
    book_obj.authors.add(*[author1, author2])

 

2.2 删除

一对一表的删除

 # 2.1 一对一删除 ,表一外键关联到表二,表一删除,不影响表2,表2删除会影响表1
    models.AuthorDetail.objects.get(id=1).delete()

 

一对多表的删除

# 2.2 一对多删除
    models.Publish.objects.get(id=3).delete()

 

多对多表的删除

# 2.3 多对多删除(实际上删除的是第三张表里的关系)
    book_obj=models.Book.objects.get(nid=4)
    book_obj.authors.remove(4)
    book_obj.authors.remove(*[4,2])
    # 清空4号书籍所对应的作者关系
    book_obj.authors.clear()
    book_obj.authors.add(*[5])
    # 清空4号书籍所对应的作者关系,并添加2号和4号作者
    book_obj.authors.set(['2','4'])
    # 清空4号书籍所对应的作者关系,并添加3号作者
    book_obj.authors.set('3')  # 删除然后更新

 

2.3 表的修改

一对一表的修改

models.Author.objects.filter(id=3).update(
        name = '明星',
        age = '18',
        # authorDetail =models.AuthorDetail.objects.get(id=6),
        authorDetail_id =3
    )

 

一对多表的修改

# 可以将nid 换成 pk (primary key 的意思)
    models.Book.objects.filter(nid = 3).update(
        title = '鬼谷子2',
        # publishs = models.Publish.objects.get(id=2)
        publishs_id = 1
    )
    # 级联更新
    models.Publish.objects.filter(pk=2).update(
        id = 7   # Django 没有级联更新。。。
    )

 

多对多表的修改

# 多对多更新,只能用set() 同上
    # 清空4号书籍所对应的作者关系,并添加2号和4号作者
    book_obj.authors.set(['2','4'])
    # 清空4号书籍所对应的作者关系,并添加3号作者
    book_obj.authors.set('3')  # 删除然后更新

 

2.4 表的查询(重点)

 2.4.1  基于对象的跨表查询 ----类似于子查询

关系属性(字段)写在哪个类(表)里面,从当前类(表)的数据去查询它关联类(表)的数据叫做正向查询,反之叫做反向查询

一对一的查询

 # 一对一查询
    # 查询胡的手机号码
    author_obj = models.Author.objects.filter(name="胡惠明").first()
    print(type(author_obj))
    ren = author_obj.authorDetail.telephone
    print(ren)

    # 反向查询
    # 查询14785296312号码是谁的
    author_detail_obj = models.AuthorDetail.objects.get(telephone='12345678910')
    print(author_detail_obj)
    print(author_detail_obj.author.name)

    '''        正向查询:Authorobj.authorDetail,对象.关联属性名称
            Author----------------------------------->AuthorDetail
                  <-----------------------------------
                  反向查询:AuthorDetailobj.author  ,对象.小写类名
        '''

 

一对多的查询

 # 一对多查询
    # 查询一下资本论这本书的出版社是哪个
    # 正向查询
    book_obj = models.Book.objects.get(title='资本论')
    print(book_obj.publish.name)
    # 反向查询
    pub_obj = models.Publish.objects.get(name='沙河出版社')
    print(pub_obj.book_set.all())

    '''   正向查询 book_obj.publishs  对象.属性
        Book ---------------------------------------------> Publish
            <----------------------------------------------
              反向查询 publish_obj.book_set.all()  对象.表名小写_set
        '''

 

多对多的查询

  # 多对多查询
    # python编程是谁写的
    book_obj = models.Book.objects.get(title='python编程')
    print(book_obj.authors.all())
    # 李作者写了什么书
    author_obj = models.Author.objects.get(name='李作者')
    print('李作者写的', author_obj.book_set.all())

    '''       正向查询 book_obj.authors.all()  对象.属性
            Book ---------------------------------------------> Author
                <----------------------------------------------
                  反向查询 author_obj.book_set.all()  对象.表名小写_set
        '''

 

2.4.2 基于双下划线(queryset)的跨表查询 --连接join

一对一

 # 一对一
    # 正向查询
    # 1,查询李作者的手机号码是多少?
    obj = models.Author.objects.filter(name='李作者').values('authorDetail__telephone')
    print(obj)  # <QuerySet [{'authorDetail__telephone': 12345678910}]>
    # 反向查询
    obj = models.AuthorDetail.objects.filter(author__name='李作者').values('telephone', 'author__age')
    print(obj)  # <QuerySet [{'telephone': 12345678910, 'author__age': 18}]>
    # 哪个老师的电话是:12345678910
    # 正向查询
    obj = models.Author.objects.filter(authorDetail__telephone='12345678910').values('name')
    print(obj)
    # 反向查询
    obj = models.AuthorDetail.objects.filter(telephone='12345678910').values('author__name')
    print(obj)

 

一对多

# 一对多:
    # 查询python编程这本书的出版社是哪一个
    # 正向查询
    obj = models.Book.objects.filter(title='资本论').values('publish__name')
    print(obj)
    # 方向查询
    obj = models.Publish.objects.filter(book__title='python编程').values('name')
    print(obj)
    # 沙河出版社出版了哪些书?
    # 正向查询
    obj = models.Book.objects.filter(publish__name='沙河出版社').values('title')
    print(obj)  # <QuerySet [{'title': 'python编程'}, {'title': '老李的床头故事'}]>
    # 反向查询
    obj = models.Publish.objects.filter(name="沙河出版社").values('book__title')
    print(obj)  # <QuerySet [{'book__title': 'python编程'}, {'book__title': '老李的床头故事'}]>

 

多对多

# 多对多
    # 正向查询
    # 查询python编程这本书是谁写的
    obj = models.Book.objects.filter(title='python编程').values('authors__name','authors__authorDetail__telephone')
    print(obj)
    # 反向查询
    obj = models.Author.objects.filter(book__title='python编程').values('name')
    print(obj)

    # 李作者写了哪些书
    # 正向 查询
    obj = models.Book.objects.filter(authors__name='李作者').values('title')
    print(obj)
    # 反向查询
    obj = models.Author.objects.filter(name='李作者').values('book__title')
    print(obj)

 

2.4.3 进阶练习

# 练习: 查询人民出版社出版过的所有书籍的名字以及作者的姓名


    # 正向查询
    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开头的作者出版过的所有书籍名称以及出版社名称
    # 方式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")

2.4.4 related_name

反向查询时,如果定义了related_name ,则用related_name替换 表名,例如:

publish = ForeignKey(Blog, related_name='bookList')
# 练习: 查询人民出版社出版过的所有书籍的名字与价格(一对多)# 反向查询 不再按表名:book,而是related_name:bookList
    queryResult=Publish.objects
              .filter(name="人民出版社")
              .values_list("bookList__title","bookList__price")

 

三,聚合查询,分组查询,F查询和Q查询

 

posted @ 2020-09-15 07:35  勇敢面对difficult  阅读(121)  评论(0)    收藏  举报