• Django中的表关系实现及操作


    表关系的实现

     

    预备知识

    ORM的正向操作和反向操作:

    1.正向操作:一个模型中定义了一个外键,通过该模型对该外键操作的操作叫做正向操作。

    2.反向操作:被外键所关联的模型,通过该模型对外键所在模型的操作叫做反向操作。

     

    表关系的操作

    我们通过下面的案例,来了解表关系的操作

    首先我们需要几张表:学生表,学生信息表,班级表,报名表,课程表

    他们的关系是:学生表和学生信息表  一对一

           班级表和学生表         一对多

           学生表和课程表         多对多

    下面是Django 模型代码:

    学生表

     1 from django.db import models
     2 
     3 
     4 class Student(models.Model):
     5     name = models.CharField(max_length=20)
     6     age = models.SmallIntegerField(default=0)
     7     sex = models.SmallIntegerField(default=1)
     8     # 班级和学生之间是一对多的关系,不可执行级联删除,所以置空
     9     grade = models.ForeignKey('Grade', on_delete=models.SET_NULL, null=True)
    10     qq = models.CharField(max_length=20, default='')
    11     phone = models.CharField(max_length=20, default='')
    12     c_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
    13 
    14     def __str__(self):
    15         return '<Student:名字:%s,年龄:%s>' % (self.name, self.age)
    View Code

     学生信息表

    1 class StudentDetail(models.Model):
    2     num = models.CharField(max_length=20, default='')
    3     college = models.CharField(max_length=20, default='')
    4     # 学生与学生详情表 一对一关系,执行联级删除
    5     student = models.OneToOneField('Student', on_delete=models.CASCADE)
    View Code

    班级表

    class Grade(models.Model):
        name = models.CharField(max_length=20)
        num = models.CharField(max_length=20)
    
        def __str__(self):
            return '<Grade:班级:%s,班号:%s>' % (self.name, self.num)
    View Code

    课程表

    class Course(models.Model):
        name = models.CharField('课程名称', max_length=20)
        # 科目和学生之间是多对多的关系,一般我们都要指定他们之间的 中间表
        students = models.ManyToManyField('Student', through='Enroll')
    
        def __str__(self):
            return '<Course: 科目:%s>' % self.name
    View Code

    中间表:报名表

    class Enroll(models.Model):
        student = models.ForeignKey('Student', on_delete=models.CASCADE)
        course = models.ForeignKey('Course', on_delete=models.CASCADE)
        pay = models.CharField(max_length=20, default='')
        c_time = models.DateTimeField('报名时间', auto_now_add=True)
    
        def __str__(self):
            return '报名表:学生:%s 科目:%s 缴费金额:%s  报名时间:%s' % (self.student, self.course, self.pay, self.c_time)
    View Code

    注释:在使用外键关联时,需要加入on_delete()参数,此参数为了避免俩个表的数据不一致问题;modles.CASCADE 表示级联删除,models.SET_NULL 表示主表不级联删除,置空。

    一对多:班级只有一个,但每个班级有多个学生

    首先在俩个表中分别插入俩个值:

    #进入Django的交互模式
    python manage.py shell
    
    In [1]: from teacher.models import Student,StudentDetail,Grade,Enroll,Course
    
    In [2]: g1=Grade.objects.create(name='django',num='33期')
    
    In [3]: g2=Grade.objects.create(name='爬虫',num='34期')
    
    In [6]: s1=Student.objects.create(name='cmx',grade=g1)
    
    In [7]: s2=Student.objects.create(name='cmx01',grade=g2)
    View Code

    正向操作:

    In [8]: s1.grade
    Out[8]: <Grade: <Grade:班级:django,班号:33期>>
    
    In [9]: s1.grade.name
    Out[9]: 'django'
    
    In [10]: s1.grade.num
    Out[10]: '33期'
    View Code

    反向操作:借用Django的反向管理器(外键所在模型的小写+‘_set’,就相当于‘objects’)

    ##增加(俩个方法都是立刻执行)
    s3=Student.objects.create(name='cmx02',grade=g2)
    #create方法
    In [20]: g1.student_set.create(name='cmx')
    Out[20]: <Student: <Student:名字:cmx,年龄:0>>
    #add方法
    In [21]: g1.student_set.add(s2)
    #增加后查询检测
    In [22]: g1.student_set.all()
    Out[22]: <QuerySet [<Student: <Student:名字:cmx01,年龄:0>>, <Student: <Student:名字:cmx,年龄:0>>]>
    #add特性:若添加已经有班级的值,会解除原来的关系,将其并入到自己中来,原属班级的该成员被清空。
    In [27]: g1.student_set.add(s3)
    
    In [28]: g1.student_set.all()
    Out[28]: <QuerySet [<Student: <Student:名字:cmx01,年龄:0>>, <Student: <Student:名字:cmx02,年龄:0>>, <Student: <Student:名字:cmx,年龄:0>>]>
    
    In [29]: s3.grade
    Out[29]: <Grade: <Grade:班级:django,班号:33期>>
    
    In [30]: g2.student_set.all()
    Out[30]: <QuerySet []>
    ##查询
    In [31]: g1.student_set.all()
    Out[31]: <QuerySet [<Student: <Student:名字:cmx01,年龄:0>>, <Student: <Student:名字:cmx02,年龄:0>>, <Student: <Student:名字:cmx,年龄:0>>]>
    
    In [32]: g1.student_set.filter(name='cmx')
    Out[32]: <QuerySet [<Student: <Student:名字:cmx,年龄:0>>]>
    ##删除
    #remove方法
    In [36]: g1.student_set.remove(s2)
    
    In [37]: g1.student_set.all()
    Out[37]: <QuerySet [<Student: <Student:名字:cmx02,年龄:0>>, <Student: <Student:名字:cmx,年龄:0>>]>
    #clear清空
    In [45]: g1.student_set.clear()
    
    In [46]: g1.student_set.all()
    Out[46]: <QuerySet []>
    ##修改set方法:先执行清空操作,之后再添加
    In [58]: g1.student_set.all()
    Out[58]: <QuerySet [<Student: <Student:名字:cmx,年龄:0>>, <Student: <Student:名字:cmx01,年龄:0>>, <Student: <Student:名字:cmx02,年龄:0>>]>
    
    In [59]: g1.student_set.set([s1,s2])
    
    In [60]: g1.student_set.all()
    Out[60]: <QuerySet [<Student: <Student:名字:cmx,年龄:0>>, <Student: <Student:名字:cmx01,年龄:0>>]>
    View Code

     一对一:有一对一的表关系的俩表,通过对方模型的小写来相互访问,返回的是对象

    #创建一个学生详情
    In [61]: sd=StudentDetail.objects.create(num='001',college='家里蹲',student=s1)
    
    In [62]: StudentDetail.objects.all()
    Out[62]: <QuerySet [<StudentDetail: <StudentDetail:学号:001,毕业院校:家里蹲>>]>
    #正向
    In [63]: sd.student
    Out[63]: <Student: <Student:名字:cmx,年龄:0>>
    #反向
    In [64]: s1.studentdetail
    Out[64]: <StudentDetail: <StudentDetail:学号:001,毕业院校:家里蹲>>
    View Code

    多对多:该关系的操作,俩端都可以自动的获取API访问,与其他关系不同的是它不需要Django提供的管理器,而是使用自己的属性字段。

    首先建立几个课程:

    In [66]: c1=Course.objects.create(name='python全栈')
    
    In [67]: c2=Course.objects.create(name='java全栈')
    
    In [68]: c3=Course.objects.create(name='c++全栈')
    
    In [69]: Course.objects.all()
    Out[69]: <QuerySet [<Course: <Course: 科目:python全栈>>, <Course: <Course: 科目:java全栈>>, <Course: <Course: 科目:c++全栈>>]>
    View Code

    中间表添加数据:

    In [71]: Enroll.objects.create(student=s1,course=c1)
    Out[71]: <Enroll: 报名表:学生:<Student:名字:cmx,年龄:0> 科目:<Course: 科目:python全栈> 缴费金额:  报名时间:2019-03-05 08:18:41.163008+00:00>
    
    In [72]: Enroll.objects.create(student=s2,course=c2)
    Out[72]: <Enroll: 报名表:学生:<Student:名字:cmx01,年龄:0> 科目:<Course: 科目:java全栈> 缴费金额:  报名时间:2019-03-05 08:19:00.548559+00:00>
    
    In [73]: Enroll.objects.create(student=s1,course=c3)
    Out[73]: <Enroll: 报名表:学生:<Student:名字:cmx,年龄:0> 科目:<Course: 科目:c++全栈> 缴费金额:  报名时间:2019-03-05 08:19:12.081490+00:00>
    View Code

    正向查询:使用课程表的多对多字段名

    In [74]: c1.students.all()
    Out[74]: <QuerySet [<Student: <Student:名字:cmx,年龄:0>>]>

     反向查询:使用课程表明小写+‘_set’

    In [75]: s1.course_set.all()
    Out[75]: <QuerySet [<Course: <Course: 科目:python全栈>>, <Course: <Course: 科目:c++全栈>>]>

    注释:

    Course的多对多关系字段:students = models.ManyToManyField('Student', through='Enroll')

    through这个字段就是指定了中间表的字段,当中间表被指定之后,add,remove,set  方法都不可用,无论正反向操作。

    若是没有给定该字段,系统会自动创建一个中间表,上方的几个方法也可使用。

    跨表查询

    前提:跨表查询,所关联的表必须有一定的关系

    方法:使用双下划线‘__’来达到所需条件的字段

    案例:

    1.查询所有Django班的学生信息

    In [76]: res=Student.objects.filter(grade__name__contains='django')
    
    In [78]: res
    Out[78]: <QuerySet [<Student: <Student:名字:cmx,年龄:0>>, <Student: <Student:名字:cmx01,年龄:0>>]>

    2.查询男生都报名了那些课程

    In [80]: res=Course.objects.filter(students__sex=1)
    
    In [81]: res
    Out[81]: <QuerySet [<Course: <Course: 科目:python全栈>>, <Course: <Course: 科目:c++全栈>>, <Course: <Course: 科目:java全栈>>]>

    综上俩点可以看出:学生表操作grade外键,课程表操作students外键,都属于正向操作,并且都使用已经定义好的外键原字段。

    3.查询报名了python全栈的学生

    分析:学生表和课程表 属于多对多,多对多的模型字段在课程表里,而我们要查的是学生,所以就是反向查询

    反向查询所用的字段不是 模型的小写+’_set‘,而仅是模型的小写

    In [83]: res=Student.objects.filter(course__name__contains='python')
    
    In [84]: res
    Out[84]: <QuerySet [<Student: <Student:名字:cmx,年龄:0>>]>

    4.查询报名了Java全栈 班级为33期的学生

    分析:这是多条件查询,条件一:课程名为java全栈,条件二:班级的期数为33,所要查的学生

    In [89]: res=Student.objects.filter(course__name__contains='java',grade__num__contains='33')
    
    In [90]: res
    Out[90]: <QuerySet [<Student: <Student:名字:cmx01,年龄:0>>]>

    最后的日常技巧记录:django 删除表所有的数据:Student.objects.all().delete()

  • 相关阅读:
    Web service是什么?
    SQL截取字符串
    SQL Server中使用索引性能的比较
    一个C#中webservice的初级例子(一)
    short s1 = 1; s1 = s1 + 1;有错而short s1 = 1; s1 += 1正确。为何?
    SQL索引
    ORDER BY 子句在子查询和公用表表达式中无效的一种解决办法使用表变量
    创建 索引,
    时间的重叠
    SQLServer Datetime数据类型的转换
  • 原文地址:https://www.cnblogs.com/cmxbky1314/p/10476175.html
Copyright © 2020-2023  润新知