• Django 学习 之ORM多表操作


    .创建模型

    1.模型关系整理

    创建一对一的关系:OneToOne("要绑定关系的表名")

    创建一对多的关系:ForeignKey("要绑定关系的表名")

     

    创建多对多的关系:ManyToMany("要绑定关系的表名")  会自动创建第三张表

     

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

     一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)

     

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

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

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

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

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

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

    下面我们通过图书管理系统,来设计出每张表之间的对应关系。

    通过上图关系,来定义一下我们的模型类。

    2.模型关系类的编写

    from django.db import models

     

    class Book(models.Model):

    # nid = models.AutoField(primary_key=True)  # 自增id(可以不写,默认会有自增id)

    title = models.CharField(max_length=32)     # 书名

    price = models.DecimalField(max_digits=5, decimal_places=2)  

    # 价格 一共5位,保留两位小数

    pub_date = models.DateField()  #出版日期

    # 一个出版社有多本书,关联字段要写在多的一方

    # 不用命名为publish_id,因为django为我们自动就加上了_id

    # foreignkey(表名)建立的一对多关系

    publish = models.ForeignKey("Publish", on_delete=models.CASCADE)

    authors = models.ManyToManyField("Author")  # 建立的多对多的关系

     

     

    class Publish(models.Model):

    # 不写id的时候数据库会自动给你增加自增id

        name = models.CharField(max_length=32)

        city = models.CharField(max_length=64)

        email = models.EmailField()

     

    class Author(models.Model):

        name = models.CharField(max_length=32)

        age = models.SmallIntegerField()

        au_detail = models.OneToOneField("AuthorDetail", on_delete=models.CASCADE)

     

    class AuthorDetail(models.Model):

        gender_choices = (

            (0, ""),

            (1, ""),

            (2, "保密"),

        )

        gender = models.SmallIntegerField(choices=gender_choices)

        tel = models.CharField(max_length=32)

        addr = models.CharField(max_length=64)

        birthday = models.DateField()

     

    3.注意事项

    1、 表的名称myapp_modelName,是根据 模型中的元数据自动生成的,也可以覆写为别的名称  

    2id 字段是自动添加的

    3、对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名

    4、这个例子中的CREATE TABLE SQL 语句使用MySQL 语法格式,要注意的是Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句。

    5、定义好模型之后,你需要告诉Django _使用_这些模型。你要做的就是修改配置文件中的INSTALL_APPSZ中设置,在其中添加models.py所在应用的名称。

    6、外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None

    4.关于字段选项

    每个字段有一些特有的参数,例如,CharField需要max_length参数来指定VARCHAR数据库字段的大小。还有一些适用于所有字段的通用参数。 这些参数在文档中有详细定义,这里我们只简单介绍一些最常用的:

    1)null

    如果为TrueDjango 将用NULL 来在数据库中存储空值。 默认值是 False.

    (1)blank

    如果为True,该字段允许不填。默认为False

    要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。

    如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。

    (2)default

    字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用。

    (3)primary_key

    如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True

    Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,

    否则没必要设置任何一个字段的primary_key=True

    (4)unique

    如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的

    (5)choices

    由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices

    默认的表单将是一个选择框而不是标准的文本框,而且这个选择框的选项就是choices 中的选项。

    这是一个关于 choices 列表的例子:

    YEAR_IN_SCHOOL_CHOICES = (

        ('FR', 'Freshman'),

        ('SO', 'Sophomore'),

        ('JR', 'Junior'),

        ('SR', 'Senior'),

        ('GR', 'Graduate'),

    )

    每个元组中的第一个元素,是存储在数据库中的值;第二个元素是在管理界面或 ModelChoiceField 中用作显示的内容。

    在一个给定的 model 类的实例中,想得到某个 choices 字段的显示值,就调用 get_FOO_display 方法(这里的 FOO 就是 choices 字段的名称 )。例如:

     

    from django.db import models

     

    class Person(models.Model):

        SHIRT_SIZES = (

            ('S', 'Small'),

            ('M', 'Medium'),

            ('L', 'Large'),

        )

        name = models.CharField(max_length=60)

        shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)

     

     

    >>> p = Person(name="Fred Flintstone", shirt_size="L")

    >>> p.save()

    >>> p.shirt_size

    'L'

    >>> p.get_shirt_size_display()

    'Large

     

    一旦你建立好数据模型之后,django会自动生成一套数据库抽象的API,可以让你执行关于表记录的增删改查的操作。

    二.操作表记录

    1.添加一些简单的数据

    (1)publish

    insert into app01_publish(name,city,email) values("华山出版社", "华山", "hs@163.com"), ("明教出版社", "黑木崖", "mj@163.com");

    (2)authordatail

    insert into app01_authordetail(gender,tel,addr,birthday) value (1,13432335433,"华山","1994-5-23"),(1,13943454554,"黑木崖","1961-8-13"),(0,13878934322,"黑木崖","1996-5-20");

    (3)author

    insert into app01_author(name,age,au_detail_id) value ("令狐冲",25,1),("任我行",58,2),("任盈盈",23,3);

    多表存在关联的情况下,插入数据需要先将被关联的表的数据先插入,在插入关联的表的数据。

     

    2.一对多添加记录

    # 一对多的添加

    # 方式一:如果是这样直接指定publish_id字段去添加值,前提是你的主表里面必须有数据

    # 主表:没有被关联的(因为book表是要依赖于publish这个表的)也就是publish

    # 子表:关联的表

    # ##一对多新增

     

    # 方式一 传对象的形式

    pub_obj = models.Publish.objects.get(pk=1)

    book = models.Book.objects.create(title="独孤九剑", price=180, pub_date="2018-10-23", publish=pub_obj)

     

    # 方式二 传对象的id(用的更多)

    # new_book = models.Book.objects.create(title="冲灵剑法", price=120, pub_date="2018-08-23", publish_id=pub_obj.pk)

    # print(new_book)

     

    3.多对多

    # ##多对多新增

    # 方式一 传对象的形式

    # book_obj = models.Book.objects.get(pk=1)

    # print(book_obj)

    # ling = models.Author.objects.get(pk=1)

    # print(ling)

    # ying = models.Author.objects.get(pk=3)

    # print(ying)

    # book_obj.authors.add(ling, ying)

    # 传入的是对象,通过模型会自动转为相应的id,为book_authors新增信息

     

    #

    # # 方式二 传对象id

    book_obj = models.Book.objects.get(pk=2)

    ling = models.Author.objects.get(pk=1)

    # print(ling.pk)

    book_obj.authors.add(ling.pk)

    book_obj.authors.remove()      # 将某个特定的对象从被关联对象集合中去除。    ======   book_obj.authors.remove(*[])

    book_obj.authors.clear()       #清空被关联对象集合

    book_obj.authors.set()         #先清空再设置

    总结:removeclear的区别

      remove:得吧你要清除的数据筛选出来,然后移除

      clear:不用查,直接就把数据都清空了。

    各有应用场景

     

    关联管理器(RelatedManager)

    关于关联管理器大前提:

    (1)多对多,双向都有

    (2)一对多时,对应多的一方才有(出版社的角度)

    (3)一对多时,反向才能使用关联管理器

     

    正向按字段名

    反向按小写表名+  _set

    4.基于对象的跨表查询

    """

              正向: 字段名称

            ---------------->

    Book                        Publish

            <----------------

            反向: 小写表名+_set

    """

    (1)一对多查询(Publish Book

    # 正向例子

    # 查询主键为1的书籍的出版社所在的城市

    book_obj = models.Book.objects.get(pk=1)

    print(book_obj.publish.city)

     

    # 反向例子

    # 查询华山出版社出版的书籍名

    pub_obj = models.Publish.objects.filter(name="华山出版社").first()

    # 反向获取到存在这个pub_obj对象的书,在遍历出来

    for book in pub_obj.book_set.all():

        print(book.title)

     

    (2)一对一查询(Author AuthorDetail

    正向查询(按字段:au_detail

    # 查询令狐冲的电话

    author_obj = models.Author.objects.filter(name="令狐冲").first()

    print(author_obj.au_detail.tel)

     

    # 反向查询(按表名:author):

    # 查询所有住址在黑木崖的作者的姓名

    au_detail_obj = models.AuthorDetail.objects.filter(addr="黑木崖")

    for au in au_detail_obj:

    print(au.author.name)

     

    (3)多对多查询 (Author Book)

     # 独孤九剑所有作者的名字以及手机号

    book_obj = models.Book.objects.filter(title="独孤九剑").first()

    for au in book_obj.authors.all():

        print(au.name, au.au_detail.tel)

     

    # 反向查询(按表名:book_set)

    # 查询令狐冲出过的所有书籍的名字

    au_obj = models.Author.objects.filter(name="令狐冲").first()

    for book in au_obj.book_set.all():

        print(book.title)

     

    注意:

    你可以通过在 ForeignKey() ManyToManyField的定义中设置 related_name 的值来覆写 FOO_set 的名称。例如,如果 Book model 中做一下更改:

    publish = models.ForeignKey("Publish", on_delete=models.CASCADE, related_name="book_list")

    那么接下来就会如我们看到这般:

    # 查询 明教出版社出版过的所有书籍

    publish=Publish.objects.get(name="明教出版社")

    book_list=publish.book_list.all()  # 与明教出版社关联的所有书籍对象集合

     

    5.基于双下划线的跨表查询

    Django 还提供了一种直观而高效的方式在查询(lookups)中表示关联关系,它能自动确认 SQL JOIN 联系。要做跨关系查询,就使用两个下划线来链接模型(model)间关联字段的名称,直到最终链接到你想要的model 为止。

     正向查询按字段,反向查询按表名小写用来告诉ORM引擎join哪张表

        """

                      正向: 字段名称__跨表的字段名称

                    ----------------------------->

            Book                                    Publish

                    <-----------------------------

                      反向: 小写表名__跨表的字段名称

        """

    (1)一对多查询

    # 练习:  查询华山出版社出版过的所有书籍的名字与价格(一对多)

    # 正向查询 按字段:publish

    book_title_price1 = models.Book.objects.filter(publish__name="华山出版社").values_list("title", "price")

    print(book_title_price1)

     

    # 反向查询 按表名:book

    book_title_price2 = models.Publish.objects.filter(name="华山出版社").values_list("book__title", "book__price")

    print(book_title_price2)

     

    (2)多对多查询 

    # 练习: 查询令狐冲出过的所有书籍的名字(多对多)

    # 正向查询 按字段:authors:

    book_name = models.Book.objects.filter(authors__name="令狐冲").values("title")

    print(book_name)

     

    # 反向查询 按表名:book

    book_name = models.Author.objects.filter(name="令狐冲").values("book__title")

    print(book_name)

     

    (3)一对一查询

    # 查询令狐冲的手机号

    au_tel =models.Author.objects.filter(name ="令狐冲").values("au_detail__tel")

    print(au_tel)

     

    # 反向查询

    au_tel = models.AuthorDetail.objects.filter(author__name="令狐冲").values("tel")

    print(au_tel)

     

    (4)进阶练习(连续跨表)

    # # 正向查询

    bname_aname = models.Book.objects.filter(publish__name="华山出版社").values("title","authors__name")

    print(bname_aname)

    # 反向查询

    bname_aname = models.Publish.objects.filter(name="华山出版社").values("book__title","book__authors__name")

    print(bname_aname)

    # 练习2: 手机号以132开头的作者出版过的所有书籍名称以及出版社名称

    # 正向查询

    bname_pname = models.Book.objects.filter(authors__au_detail__tel__startswith="134").values("title", "publish__name")

    print(bname_pname)

    # 反向查询1

    bname_pname = models.Author.objects.filter(au_detail__tel__startswith="134").values("book__title", "book__publish__name")

    print(bname_pname)

    # 反向查询2

    bname_pname = models.AuthorDetail.objects.filter(tel__startswith="134").values("author__book__title", "author__book__publish__name")

    print(bname_pname)

  • 相关阅读:
    2021“MINIEYE杯”中国大学生算法设计超级联赛(2)(1002 I love tree)(树状数组+树链剖分)
    周末随笔_有关变化
    20210808心情随笔
    离开那个傻叉的地方了
    如何建设符合ISO9000要求的企业文控中心
    企业云盘部署极其简单的分布式文件系统的方法
    企业云盘安全机制-文件加密存储与原文存储的优劣
    查看tomcat打开的文件数
    Centos7 Memcached 安装
    centos7 快速安装rabbitmq
  • 原文地址:https://www.cnblogs.com/hszstudypy/p/11153758.html
Copyright © 2020-2023  润新知