十 Django-模型关联关系

一、前期概要

1.1 名词解释

  1. 关系:事物之间相互作用、相互联系的状态。
  2. 关联:名词:表示对象(数据库表)之间的关系;动词:将对象(数据库表)之间通过某种方式联系起来。
  3. 映射:将一种形式转化为另一种形式,包括关系。
  4. 级联:有关系的双方中操作一方,另一方也将采取一些动作
  5. 值类型:对象不具备数据库同一性,属于一个实体实例其持久化状态被嵌入到所拥有的实体的表行中,没有标识符。
  6. 实体类型:具有数据库标识符。

二、数据库关系

  1. 一对一(one-to-one)
  2. 多对一(many-to-one)
  3. 多对多(many-to-many)

三、模型

  1. 完整代码

    from django.db import models
    # Create your models here.
    class ClassRoom(models.Model):
        cls_id = models.AutoField(primary_key=True)
        cname = models.CharField(max_length=32)
        cdata = models.DateField(auto_now_add=True)
        class Meta:
            db_table = 'class_room'
    
        def __str__(self):
            return "%s" % [self.__class__, self.cname]
    class Student(models.Model):
        stu_id = models.AutoField(primary_key=True)
        stu_name = models.CharField(max_length=32)
        # 一对多
        cls_id = models.ForeignKey(to="ClassRoom", on_delete=models.SET_NULL, null=True, to_field='cls_id',db_column='cls_id')
                                   
        class Meta:
            db_table = 'student'
    
        def __str__(self):
            return "%s" % self.stu_name
    class StudentDetail(models.Model):
        stu_detail_id = models.AutoField(primary_key=True)
        height = models.PositiveIntegerField(null=True)
        email = models.EmailField(null=True)
        no = models.CharField(max_length=64)
        stu = models.OneToOneField('Student', on_delete=models.CASCADE, to_field='stu_id', null=True)
    
        class Meta:
            db_table = 'student_detail'
    
    class Teacher(models.Model):
        id = models.AutoField(primary_key=True)
        tea_name = models.CharField(max_length=32)
        cid = models.ManyToManyField(to="Student", name="teacher", db_table='teacher_student')
        class Meta:
            db_table = 'teacher'
    
    

四、一对一映射关系

1.1、概念

所谓的一对一查询,就是说我们在查询一个表的数据的时候,需要关联查询其他表的数据,例如:husband(丈夫)-wife(妻子) User和Account,一个用户对应一个账户,一个订单对应一个用户,一个学生信息对一个学生详情表

1.2、配置方式

  1. 语法

    OneToOneField(to, on_delete=None, to_field=None,db_column=None)
    
  2. 参数说明

    • to

      主表 对应类的名称

    • on_delete

      当一个被外键关联的对象被删除时,Django将模仿on_delete参数定义的SQL约束执行相应操作。比如,你有一个可为空的外键,并且你想让它在关联的对象被删除时,自动设为null

      常用可选值

      1、models.CASCADE 子表相关的数据删除
      2、models.SET_NULL 子表的数据不删除 外键字段设置null=True
      3、odels.SET_DEFAULT 子表关联数据不删除,在外键字段设置自定义的值

      4、DO_NOTHING:什么也不做。

      5、SET():设置为一个传递给SET()的值或者一个回调函数的返回值。注意大小写

    • related_name

      用于关联对象反向引用模型的名称。通常情况下,这个参数我们可以不设置,Django会默认以模型的小写作为反向关联名

    • to_field

      参照主表的字段 默认主键

  3. 关系模型

    image
  4. 对象模型

    class Student(models.Model):
        stu_id = models.AutoField(primary_key=True)
        stu_name = models.CharField(max_length=32)
    
        # 一对多
        cls_id = models.ForeignKey(to="ClassRoom", on_delete=models.SET_NULL, null=True, to_field='cls_id',db_column='cls_id')
                  
        class Meta:
            db_table = 'student'
    
        def __str__(self):
            return "%s" % self.stu_name
    
    class StudentDetail(models.Model):
        stu_detail_id = models.AutoField(primary_key=True)
        height = models.PositiveIntegerField(null=True)
        email = models.EmailField(null=True)
        no = models.CharField(max_length=64)
        stu = models.OneToOneField('Student', on_delete=models.CASCADE, to_field='stu_id', null=True)
        class Meta:
            db_table = 'student_detail'
    

1.3、添加

  1. 方式一

    # 先保存主表数据,然后将主表的id给子表的外键字段
    stu = Student.objects.create(stu_name='小明')
    StudentDetail.objects.create(stu=stu, email='123@163.com', no=175)
    
  2. 方式二

    stu = Student.objects.create(stu_name='小红')
    StudentDetail.objects.create(stu_id=stu.pk, email='345@163.com', no=170)
    
  3. 总结

    跟单表操作表,唯一需要注意的是往子表添加数据,主表的数据一定要先存在

1.4、修改

  1. 通过主表更新子表

    xm = Student.objects.get(pk=1)
    StudentDetail.objects.filter(stu=xm).update(no='123')
    
  2. 通过主表更新子表

    stu = StudentDetail.objects.get(stu_detail_id=2).stu
    stu.stu_name = '小花'
    stu.save()
    
  3. 总结

    跟单表操作比,如果提供的条件是有关系的表,通过关联关系找到相关的表的记录然后去更新,

1.5、查询

  1. 通过子表查询主表相关的信息(正向查询)

    stu = StudentDetail.objects.get(pk=1)
    print(stu.stu_name)
    
  2. 通过主表查询子表相关的信息(反向查询)

    # 系统会自动创建一个 类名小写的字段对象
    stu = Student.objects.get(pk=1)
    # 类名字的小写
    print(stu.stu_name, stu.studentdetail.email, str(stu.studentdetail.stu_detail_id))
    
  3. 通过主表的条件查询子表数据(可以使用双下划线)

    # 一对一的子表字段__母表字段="xxx"
    # 相当于sql里的等值连接
    sd = StudentDetail.objects.filter(stu__stu_name='小明')
    # 等同于
    # stu_id = Student.objects.get(stu_name='小明').stu_id
    # sd = StudentDetail.objects.filter(stu_id=stu_id)
    # 也可以 
    stu = Student.objects.get(stu_name='小明')
    print(stu.studentdetail)
    
  4. 总结

    • 通过查询主表,会自动帮我们关联查询子表的数据, 使用主表对象.类名小写的方式
    • 通过主表查询子表,如果查询的条件是主表可以直接使用子表字段__母表字段

1.6、删除

  1. 通过主表删除子表数据 跟on_delete删除设置有关
stu = Student.objects.get(pk=1)
stu.delete()
# 子表数据删除不影响主表数据跟普通单表的删除一样
  1. 删除子表数据的时候建议使用 假删除

    在主表添加额外的字段

    1 表示正常 0表示删除
    is_delete = models.BooleanField(default=1)

    stu = Student.objects.get(pk=1)
    stu.is_delete = 0
    stu.save()
    # 查询的时候把删除的字段加上
    # Student.objects.filter(stu_name='隔壁小张', is_delete=1)
    
  2. 总结

    删除主表数据对子表数据有影响,主要跟on_delete设置的值有关系,删除子表数据一般情况下跟主表没有太多的关系

五、一对多映射关系

2.1、概念

一对多关联是一方持有多方引用。例如:去京东购物,那么一个京东用户可以对应多个购物订单

2.2、配置方式

  1. 语法格式

    ForeignKey(to, on_delete, **options)
    
  2. 参数说明

    • to

      主表 对应类的名称

    • on_delete

      当一个被外键关联的对象被删除时,Django将模仿on_delete参数定义的SQL约束执行相应操作。比如,你有一个可为空的外键,并且你想让它在关联的对象被删除时,自动设为null

      常用可选值

      1、models.CASCADE 子表相关的数据删除
      2、models.SET_NULL 子表的数据不删除 外键字段设置null=True
      3、odels.SET_DEFAULT 子表关联数据不删除,在外键字段设置自定义的值

      4、DO_NOTHING:什么也不做。

      5、SET():设置为一个传递给SET()的值或者一个回调函数的返回值。注意大小写

    • related_name

      用于关联对象反向引用模型的名称。通常情况下,这个参数我们可以不设置,Django会默认以模型的小写作为反向关联名

    • to_field

      参照主表的字段 默认主键

  3. 关系模型

    image
  4. 对象模型

    class ClassRoom(models.Model):
        cls_id = models.AutoField(primary_key=True)
        cname = models.CharField(max_length=32)
        cdata = models.DateField(auto_now_add=True)
    
        class Meta:
            db_table = 'class_room'
    
        def __str__(self):
            return "%s" % [self.__class__, self.cname]
    
    class Student(models.Model):
        stu_id = models.AutoField(primary_key=True)
        stu_name = models.CharField(max_length=32)
    
        # 一对多
        cid = models.ForeignKey(to="ClassRoom", db_column='cls_id')
     
        class Meta:
            db_table = 'student'
    
        def __str__(self):
            return "%s" % [self.stu_name]
    

2.3、查询操作

  1. 通过主表查询子表

    tea = Teacher.objects.filter(pk=1).first()
    stu_list = tea.student_set.all()
    for stu in stu_list:
        print(stu.stu_name)
    
  2. 也可以使用__双下划线方式(推荐)

    sd = StudentDetail.objects.filter(stu__stu_name='小明')
    print(sd)
    

2.4、添加(跟一对一相同)

  1. 添加子表数据,往子表添加数据,主表的数据必须先存在

    teacher = Teacher.objects.create(tea_name='隔壁小张')
    print(teacher.id)
    for index in range(10):
     Student(stu_name='test' + str(index), teacher_id=teacher.pk).save()
    
  2. 总结

    • 添加主表数据对子表没有影响
    • 往子表添加数据,主表的数据必须先存在

2.4、更新(跟一对一相同)

  1. 更新数据跟普通的操作没有太多的影响

    room = ClassRoom.objects.filter(cname='实训1').first()
    num = room.student_set.filter(stu_id__gt=1).update(stu_name='111')
    print(num)
    
  2. 总结

    通过主表查询子表的时候注意隐藏django会自动给我们创建一个类名_set的一个引用属性

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271