博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
django ORM模型表的一对多、多对多关系、万能双下划线查询
阅读量:4361 次
发布时间:2019-06-07

本文共 6127 字,大约阅读时间需要 20 分钟。

一.外键使用

在 MySQL 中,如果使用InnoDB引擎,则支持外键约束。(另一种常用的MyIsam引擎不支持外键)

定义外键的语法为fieldname=models.ForeignKey(to_class,on_delete=' ',options),第一个参数表示引用哪个模型,第二个参数表示如果外键引用的模型删除,该字段对应的的值应该怎么处理,第三个语法为其他字段参数。

django ORM模型常用的on_delete的值

models.CASCADE:级联删除,即外键对应的那条数据删除了,这条数据也会被删除

models.SET_NULL:设置为空,即外键对应的那条数据删除了,这条数据对应的字段设置为null,前提是这个字段可以设置为空

models.SET_DEFAULT:设为默认值,即外键对应的那条数据删除了,这条数据对应的字段设置为默认值,前提是这个字段要设置一个默认值

models.PRODECT:受保护,即不允许删除外键对应的那条数据

 

创建两个模型,学生和班级,一个学生只能属于一个班级,一个班级可以有多个学生,学生与班级为多对一的关系。

class Students(models.Model):#学生模型    sname=models.CharField(max_length=20)    age=models.IntegerField()    gender=models.BooleanField()    cls=models.ForeignKey('Classes',on_delete=models.CASCADE)#通过cls创建学生模型与班级模型的外键关系    def __str__(self):#自定义返回样式        return '%s,%s,%s,%s'%(self.sname,self.age,self.gender,self.cls)    class Meta:#自定义映射到数据库的表名        db_table='students'class Classes(models.Model):#班级模型    cname=models.CharField(max_length=50)    headmaster=models.CharField(max_length=20)    def __str__(self):        return '%s,%s'%(self.cname,self.headmaster)    class Meta:        db_table='classes'
创建学生模型和班级模型

上述cls字段的参数'Classes'表示引用Classes表的主键id作为外键,完整写法为cls=models.ForeignKey(to=''Classes,to_field='id',on_delete=models.CASCADE),to表示表,to_field表示字段。

需要注意的是,在Students模型定义时,外键属性名称为cls,但是在映射到数据库时django会将字段名称加上_id,即Student模型的cls属性在数据内对应的字段名称为cls_id。 

 

如果引用的模型在另外一个app中,那么需要在各自的urls.py的文件中先定义命名空间app_name='appname',再在to_class中加上app的名字,以上如果Classes是在另外一个名叫app01的app中,那么Students的cls的属性定义为cls = models.ForeignKey("app01.Classes",on_delete=models.CASCADE)。

如果要引用自己作为外键,to_class可以写为self,或者app的名字。

二.一对多(多对一)操作

如果模型A被模型B引用用作外键,django会自动给A模型添加一个属性,属性名称为B模型的小写_set,即b_set属性,表示并且该属性同样可以调用all()、objects.get()、objects.filter()、objects.first()等方法进行查询。

例如上述例子,班级会有一个students_set属性,要想获取某个班级c下的所有学生,可通过c.students_set.all()获取。

如果在B模型创建外键字段时自定义一个related_name=‘’,则会覆盖系统自动创建的属性名称,例如上述Students的cls的属性如果定义为cls = models.ForeignKey("Classes",on_delete=models.CASCADE,related_name=‘allstudents’),那么再要获取班级c下的所有学生,则应该通过c.allstudent.all()获取,并且班级不再有students_set属性。通常建议建议自定义related_name。

①增加记录

插入一个班级c和一个学生s记录

c=Classes.objects.create(cname='高三一班',headmaster='王老师')#由于学生模型会引用班级模型,因此需要先创建班级,否则后面创建学生的时候会报错 #方法一:直接对底层数据库进行赋值 s=Students.objects.create(sname='张三',age=19,gender='1',cls_id=1)#方法二:设置Students模型的cls属性等于要引用的模型实例 s=Students.objects.create(sname='张三',age=19,gender='1',cls=c)

②查询记录

对于Students模型来说,对象s的cls属性即为对应的班级对象,可通过type(s.cls)查看,结果为<class 'app01.models.Classes'>,因此可通过s.cls.属性再获取班级相关信息

例如要获取学生s所在的班级名称:

cname1=s.cls.cname #方法一cname2=Students.objects.filter(sname='张三').values('cls__cname') #方法二cname3=Classes.objects.filter(students__sname='张三').values('cname') #方法三

再例如要获取班级c下的学生姓名:

sname1=c.students_set.all().values('sname') #方法一sname2=Classes.objects.filter(cname='高三一班').values('students__sname') #方法二sname3=Students.objects.filter(cls__cname='高三一班').values('sname') #方法三

③万能的双下划线查询

双下划线可实现跨表查询。学生模型引用班级模型做外键,通过学生查询班级时,通过外键属性__进行跨表,通过班级查询学生时,直接通过表名__进行跨表。

例如上述的cname2,'cls__cname'表示通过外键跨到班级表,并且获取班级表的cname属性

而对上述的cname3,'students__sname'表示通过表名跨到学生表,并且获取学生表的sname属性。

上述例子,如果要将一篇文章加到某个作者下面,除了上面的方法外,还可以使用如下方法

user=User.objects.first()article=Article(title='abc',content='abcdefghijklmn')article.save()user.articles.add(article)#将article加入user用户下user.save()

上面这种写法,在article加入user作者前必须先保存article,这个前提是article的author字段可以为空,如果不能为空则这种写法会报错。而用下面这种方法则不会存在该问题。

user=User.objects.first()article=Article(title='abc',content='abcdefghijklmn')user.articles.add(article,bulk=False)

三.多对多操作

新创建两个模型,老师和班级,一个老师可以教多个班级,一个班级也会有多个老师。

class Teachers(models.Model):    tname=models.CharField(max_length=20)    age=models.IntegerField()    gender=models.BooleanField()    cls=models.ManyToManyField('Classes')    def __str__(self):        return '%s,%s,%s,%s'%(self.tname,self.age,self.gender,self.cls)    class Meta:        db_table='teachers'class Classes(models.Model):    cname=models.CharField(max_length=50)    def __str__(self):        return self.cname    class Meta:        db_table='classes'
创建老师和班级模型

将上述两个模型映射到数据库,会生成三张表,teachers表、classes表和teachers_cls表。

其中teachers表只有四个字段,id、tname、age和gender,并没有cls字段。

teachers_cls为django为多对多关系自动创建的一张表,命名规则为创建多对多关系的模型名称小写_多对多字段小写,只包含三个字段,id、teachers_id和classes_id。

如果不想使用django自动创建的第三张表,可以自己创建,如下,并使用该表来维护模型的多对多关系。自己创建的第三张表还可以增加另外的字段。

class Teachers(models.Model):    tname=models.CharField(max_length=20)    age=models.IntegerField()    gender=models.BooleanField()    #cls=models.ManyToManyField('Classes')    def __str__(self):        return '%s,%s,%s,%s'%(self.tname,self.age,self.gender,self.cls)    class Meta:        db_table='teachers'class Classes(models.Model):    cname=models.CharField(max_length=50)    def __str__(self):        return self.cname    class Meta:        db_table='classes'class Teachers_Classes(models.Model)    t = models.ForeignKey('Teachers')    c = models.ForeignKey('Classes')    ctime = models.DateField()    class Meta:        db_table='teachers_classes'        unique_together=[('t','c'),]    #表示对t和c字段创建联合唯一索引
手动创建多对多的第三张表

上述方法手动创建第三张表,原本自动生成的第三张表还是会生成。如果要手动创建第三张表并且不生成django自动创建的表,需要使用ManyToManyField并且添加参数m = models.ManyToManyField( to='Classes',through='Teachers_Classes',through_fields=['t','c'])。这种方法不推荐。

①增加记录并绑定关系

c1=Classes.objects.filter(cname='一年级').first()c2 = Classes.objects.filter(cname='二年级').first()t1=Teachers.objects.filter(tname='李老师').first()t2 = Teachers.objects.filter(tname='王老师').first()c1.teachers_set.add(t1)#将t1老师绑定到c1班级,此处参数可以为老师对象,也可为老师idt2.cls.add(c2) #将c2班级绑定到t2老师,此处参数可以为班级对象,也可以为班级id

上述,c1.teachers_set即外键中的用法,t2.cls为t2对应的班级集合

②查询记录

c=Teachers.objects.filter(tname='李老师').first().cls.all().values('cname') #李老师所带班级的名称t=Classes.objects.filter(cname='一年级').first().teachers_set.all().values('tname') #一年级老师的名字

③删除绑定关系

Teachers.objects.filter(tname='李老师').first().cls.remove(5)#解除李老师与id为5班级的绑定关系,此处参数也可以为班级对象Classes.objects.filter(cname='一年级').first().teachers_set.remove(5)#解除一年级与id为5老师的绑定关系,此处参数也可以为老师对象

④重置绑定关系

Teachers.objects.filter(tname='李老师').first().cls.set([4,5]) #将id为4和5的班级与李老师绑定,此处参数也可以为班级对象Classes.objects.filter(cname='一年级').first().teachers_set.set([5,6]) #将id为5和6的老师与一年级绑定,此处参数也可以为老师对象

 

转载于:https://www.cnblogs.com/Forever77/p/10154070.html

你可能感兴趣的文章
数据结构(二)栈与队列---栈的应用:四则运算实现
查看>>
自定义Notification实现例子
查看>>
已知空间三个点,解算外接圆圆心坐标,C++编程实现
查看>>
Android上HDMI介绍(基于高通平台)
查看>>
Python 时间复杂度
查看>>
Kubernetes 学习6 Pod控制器应用进阶
查看>>
antd typescript 可编辑单元格
查看>>
二)CodeIgniter源码分析之CodeIgniter.php
查看>>
python---Celery分布式任务队列了解
查看>>
算法习题---栈与队列之栈的数学性质
查看>>
三大主流软件负载均衡器对比(LVS VS Nginx VS Haproxy)
查看>>
Simulation
查看>>
sqli-labs(33)
查看>>
js队列排序
查看>>
Spring 简单配置连接数据库
查看>>
通用前端开发框架(一)
查看>>
B150主板Win7系统出现蓝屏且提示错误代码0x000000C5的原因及解决方法
查看>>
自动回复消息-微信公众平台开发4(asp.net)
查看>>
android开发 更新升级安装到一半自动闪退
查看>>
unity3d + photon + grpc + nodejs + postgis/postgresql 游戏服务器设计
查看>>