Django模型--MySQL-表关系

文章目录

  • Django模型--MySQL-表关系
    • 1. 表关系
      • 1.1 实例说明
      • 1.2 实例展示
    • 2. [on_delete](https://docs.djangoproject.com/en/2.1/ref/models/fields/#django.db.models.ForeignKey.on_delete)
      • 2.1 CASCADE
      • 2.2 PROTECT
      • 2.3 SET_NULL
      • 2.4 SET_DEFAULT
    • 3. 一对多操作
      • 3.1 正向
      • 3.2 反向
        • 1. 简介
        • 2. 实例
        • 3 注意
      • 3.3 访问外表字段
    • 4. 多对多操作
      • 4.1 简介
      • 4.2 访问
      • 4.3 增删改查
    • 5. 一对一操作
      • 1. 简介
      • 2. 访问
      • 2.1 正向
      • 2.2 反向

Django模型–MySQL-表关系

阅读本文前,博主强烈推荐大家花心思阅读:官方文档

基本概念引自官方文档,欲深入了解某一知识点,点击相应链接。

1. 表关系

1.1 实例说明

表关系有三个:

一对一:Student -> StuDetail

一对多:College -> Student

多对多:Student -> Course

1.2 实例展示

class Student(models.Model):
    name = models.CharField(max_length=30)  # 主键可以省略,django 自动加上
    age = models.IntegerField(default=0)
    gender = models.SmallIntegerField(default=1)
    c_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
    # 一对多关系的模型表示,通过外键实现,参数1是关联的外表
    # 数据库中对应的字段为:company_id 
    college = models.ForeignKey('College', on_delete=models.SET_DEFAULT, default='')

    def __str__(self):
        show_gender = '男'
        if self.gender == 0:
            show_gender = '女'
        return 'id<%s>name[%s]gender(%s)age|%s|c_time:%s' % (
            self.id, self.name, show_gender, self.age, self.c_time
        )


# 项目开发中,把一张大表拆分为 一对一 关系的两张子表是常用手段
class StuDetail(models.Model):
    qq = models.CharField(max_length=20, unique=True, default='')
    #指定关系 第一个参数推荐使用 字符串类型的 ,毕竟python是解释型的语言
    student = models.OneToOneField(Student, on_delete=models.CASCADE) 
    #on_delete参数指定的SQL约束的行为 ,后面会讲到
    

class College(models.Model):
    name = models.CharField(max_length=20)
    address = models.TextField(default='')


class Course(models.Model):
    name = models.CharField('课程名称', max_length=20)
    book = models.CharField(max_length=20)
    student = models.ManyToManyField('Student', through='Enroll') 
    #示例中中间表需要其他字段,需要指定 Enroll


#多对多中间表 
class Enroll(models.Model):
    # student 和 course 是多对多关系,则在中间表建立两者外键
    student = models.ForeignKey('Student', on_delete=models.CASCADE)
    course = models.ForeignKey('Course', on_delete=models.CASCADE)
    pay = models.FloatField('缴费金额', default=0)
    c_time = models.DateTimeField('报名时间', auto_now_add=True)
    
  • 对于外键,幕后Django追加"_id"字段名称来创建其数据库列名。上例中USer表中会出现company_id字段。
  • 模型对于外键的调用加以简化,使用对象追加.字段即可。

1.2 一对一

通过类 OneToOneField 实例创建映射

class models.OneToOneField():
    def __init__(self, to, on_delete, to_field=None, **kwargs):
        kwargs['unique'] = True
        super().__init__(to, on_delete, to_field=to_field, **kwargs)
  • to:关联的目标模型,推荐使用字符串。
  • on_delete:约束行为,即关联表中数据删除时,对本表数据的影响。后面会详解

1.3 多对一

通过建立外键实现

class ForeignKey(ForeignObject):
        def __init__(self, to, on_delete, related_name=None, related_query_name=None,
                 limit_choices_to=None, parent_link=False, to_field=None,
                 db_constraint=True, **kwargs):
  • 关键参数就两个:toon_delete,参数具体含义与一对一相同

1.4 多对多

通过中间表中建立外键实现

1.1 的实例中,studentcourse 就是多对多关系。我们引入中间表 enroll来连接他们。


2. on_delete

ForeignKey删除外键引用的对象时,Django将模拟on_delete参数指定的SQL约束的行为 。

#举个栗子
grade = models.ForeignKey(
    Grade,
    models.SET_NULL,
    blank=True,
    on_delete = models.CASCADE,  # 表示级联删除
    null = True
)

常用参数:(注–参数引自django官方文档,详细页面

2.1 CASCADE

级联删除。Django模拟SQL约束ON DELETE CASCADE的行为,并删除包含ForeignKey的对象。Model.delete()不会在相关模型上调用,但 会为所有已删除的对象发送pre_deletepost_delete信号。

2.2 PROTECT

通过引发ProtectedError子类来 防止删除引用的对象 django.db.IntegrityError

2.3 SET_NULL

将ForeignKey其设置为null; ForeignKey必须设置null=true值 。

2.4 SET_DEFAULT

将ForeignKey其设置为默认值; ForeignKey必须设置的默认值 。


3. 一对多操作

3.1 正向

正向:通过定义外键的模型访问外键是正向。

In [1]: from book.models import Student, StuDetail, C
   ...: ollege, Course, Enroll                       

#因为多(Student)对一(College),所以先写入college记录
In [2]: c1 = College(name='飞院', address='德阳')    

In [3]: c2 = College(name='川大', address='成都')    

In [4]: c1.save()                                    

In [5]: c2.save()                                    

# 依据外键关联数据,外键字段赋值有两种
# 第一种指定对象
In [6]: s1 = Student(name='柯南', age=16 , gender=1, college=c1)      
	#college是student类中定义的外键属性

In [7]: s1.save()                                    

#第二种直接赋值外键
In [8]: s2 = Student(name='步美', age=16, gender=0, college_id = c2.pk)                           
	#给外键college_id赋值

In [9]: s2.save()  
    

3.2 反向

1. 简介

反向:假设模型A通过外键关联了模型B,那么通过B的实例b可以获取到A实例的管理器。这就是反向,反向获取的管理器名称默认为(模型名)A_set。

2. 实例

提前创建了College模型实例

# c1 是主键为1的学校;c2,c3 是主键为2,3的学校
In [64]: c1 = College.objects.get(pk=1)              

In [65]: c1                                          
Out[65]: <College: <飞院>[德阳]>

# 反向访问
In [95]: c1.student_set.all() 
'''
在Foreignkey里面设置related_name='student',
这样就可以直接用自定义名称而不是默认_set的形式了  
c1.student_set ->返回Student关联模型管理器,注意与Student.objects不同
管理器才有add,create, clear, remove方法
'''
     

  • 添加数据

    # create
    In [40]: c1.student_set.create(name='元太', age=16, college=c1)    
                              
    Out[40]: <Student: id<6>name[元太]gender()age|16|c_time:2019-03-18 05:26:58.097584+00:00>
                            
    # add
    In [71]: s1 = Student(name='高木', college=c1, age=26)                                         
    In [72]: s2 = Student(name='白合', college=c2, age=26)  
    
    In [74]: s1.save()    # 要先将实例刷入数据库                                   
    In [75]: s2.save() 
    
    In [76]: c1.student_set.add(s1, s2)  #这里会解除s2 和 c2 的关联。
    

  • 查看数据

    In [11]: c1.student_set.filter(age=16)                                           
    Out[11]: <QuerySet [
        <Student: id<1>name[步美]gender()age|16|c_time:2019-03-18 03:44:42.817462+00:00>, 			<Student: id<2>name[柯南]gender()age|16|c_time:2019-03-18 03:45:15.162859+00:00>, 		<Student: id<6>name[元太]gender()age|16|c_time:2019-03-18 05:26:58.097584+00:00>
    ]>
    
    #模型管理器其他方法同样可用
    '''
     'all',
     'annotate',
     'count',
     'defer',
     'exclude',
     'filter',
     'first',
     'only',
     'order_by',
     'reverse',
    '''
    

  • 修改数据

    # update
    In [13]: c1.student_set.update(college_id = 2)                                   
    Out[13]: 5
    
    In [14]: c1.student_set.all()                                                    
    Out[14]: <QuerySet []>
    
    # c1对应的学生为空,此处创建,用于删除展示
    In [15]: c1.student_set.update_or_create(college_id=1)                           
    Out[15]: 
    (<Student: id<11>name[]gender()age|0|c_time:2019-03-18 13:45:15.737306+00:00>,
     True)
    
    # 此外还有 set 方法 
    In [17]:c1.student_set.set(s1, s2)
      #set方法分两步:首先c1.student_set.clear();再调用c1.student.add(s1, s2)
    
    
    

  • 删除数据

    #delete 删除空白记录
    In [24]: c1.student_set.all().delete()                                           
    Out[24]: (1, {'book.Enroll': 0, 'book.StuDetail': 0, 'book.Student': 1})
    
    #clear 清空对应集合,删库跑路
    In [24]: c1.student_set.clear() #打死不按回车键,只删除学校为c1的学生集合
    Out [24]: ~~~~不知道结果,有人告诉我吗~~~
    
    

3 注意

  • 使用反向获取管理器时,必须用实例

    # College.student_set
    In [12]: smgr = College.student_set                  
    
    In [13]: dir(smgr)                                   
    Out[13]: 
    ['__class__',
     '_get_set_deprecation_msg_params',
     'field',
     'rel',
     'related_manager_cls',
     '...'
    ]
    
    # College().student_set
    In [14]: smgr = College().student_set                
    
    In [15]: dir(smgr)                                   
    Out[15]: 
    [
     '_update',
     'add',
     'aggregate',
     'all',
     'annotate',
     '...'
    
    ]
    
    
    

  • 定义模型时设置related_name 参数来覆盖默认名称.

    class Student(models.Model):
        college = models.ForeignKey(
         	'College',
            related_name='student',
            ondelete=models.CASCADE
        )
    
    

3.3 访问外表字段

可以通过外键__字段名访问外表字段

# 取出学校名为 飞院 的学校的学生
In [50]: Student.objects.filter(college__name='飞院')
                                           
Out[50]: <QuerySet [
    <Student: id<1>name[步美]gender()age|16|c_time:2019-03-18 03:44:42.817462+00:00>, 
    <Student: id<2>name[柯南]gender()age|16|c_time:2019-03-18 03:45:15.162859+00:00>
]>
#对应的 sql 语句
Out[51]: 
    '''
SELECT `book_student`.`id`, `book_student`.`name`, `book_student`.`age`, `book_student`.`gender`, `book_student`.`c_time`, `book_student`.`college_id` 
FROM `book_student` INNER JOIN `book_college` 
	ON (`book_student`.`college_id` = `book_college`.`id`)
 WHERE `book_college`.`name` = 飞院'
    '''



4. 多对多操作

4.1 简介

多对多关系的使用类似于多对一关系的反向操作,同样是通过获取相关表的管理器来操作相关表。不同的是:多对多通过相关表的小写模型名来获取管理器,多对一通过小写模型名加_set来获取。

举个栗子:

# Student 和 College是多对一
In [2]: co = College.objects.get(pk=1) 
In [6]: type(co.student_set)  #小写模型名加_set即student_set                       
Out[6]: django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager.<locals>.RelatedManager

# Student 和 Course 是多对多
In [3]: c1 = Course.objects.get(name='案情分析')
In [11]: type(c1.students)                           
Out[11]: django.db.models.fields.related_descriptors.create_forward_many_to_many_manager.<locals>.ManyRelatedManager



4.2 访问

多对多关系中,如果指定了中间表,set, add, remove方法都必须用中间表使用。

#创建实例
In [3]: c1 = Course.objects.get(name='案情分析')     
In [4]: c2 = Course.objects.get(name='火遁')         

In [5]: s1 = Student.objects.get(name='柯南')        
In [6]: s2 = Student.objects.get(name='纳鲁淘')
    
In [12]: Enroll.objects.create(students=s1, courses=c1)                          
Out[12]: <Enroll: Enroll object (1)>
In [13]: e1 = Enroll.objects.get(students=s1)  

In [15]: e2=Enroll(students=s2, courses=c2)                                      
In [16]: e2.save() 
    
#访问
In [19]: c1.students.all()                                                       
Out[19]: <QuerySet [<Student: id<1>name[柯南]gender()age|16|c_time:2019-03-20 15:25:09+00:00>]>
	#是在模型 Course 中指定了 ManyToManyField ,所以c1 可以通过关联模型名访问。而s1只能通过模型名_set来访     问
    
In [20]: s1.course_set.all()                                                     
Out[20]: <QuerySet [<Course: <案情分析>[名侦探柯南]>]>



4.3 增删改查

增删改就是对中间表的简单操作,不做赘述,

关于查询,由于ManyRelatedMany即多对多关系管理器的原因,可以通过关联表名称访问。

例如:Student 和 Course是多对多关系

In [21]: c2.students.all()                                                       
Out[21]: <QuerySet [<Student: id<8>name[纳鲁淘]gender()age|16|c_time:2019-03-20 15:28:16+00:00>]>

  • c2 是Course模型实例。
  • c2可以直接通过students属性访问关联表。

5. 一对一操作

1. 简介

同样提供正反双向访问,访问时直接通过小写模型名即可,无需添加_set(引用一位帅比的原话:set是集合,一对一返回对象就可以了!!!)

2. 访问

一对一操作中访问的不再是相关表管理器,而是实例的相关对象。

下面直接举例,Student和StuDetail是一对一模型。

2.1 正向

In [23]: sd1 = StuDetail.objects.create(qq='123456', student=s1)                 

#返回的是对象而非管理器
In [27]: sd1.student                                                             
Out[27]: <Student: id<1>name[柯南]gender()age|16|c_time:2019-03-20 15:25:09+00:00>

2.2 反向

#同样返回对象,并且不需要后缀_set
In [28]: s1.studetail                                                            
Out[28]: <StuDetail: StuDetail object (1)>

你可能感兴趣的:(Django,Django,模型,表)