前言
这几年一直在it行业里摸爬滚打,一路走来,不少总结了一些python行业里的高频面试,看到大部分初入行的新鲜血液,还在为各样的面试题答案或收录有各种困难问题
于是乎,我自己开发了一款面试宝典,希望能帮到大家,也希望有更多的Python新人真正加入从事到这个行业里,让python火不只是停留在广告上。
微信小程序搜索:Python面试宝典
或可关注原创个人博客:https://lienze.tech
也可关注微信公众号,不定时发送各类有趣猎奇的技术文章:Python编程学习
多对一关系
Django
使用django.db.models.ForeignKey
定义多对一关系。
ForeignKey
需要一个位置参数:与该模型关联的类
class Info(models.Model):
user = models.ForeignKey(other_model,on_delete=models.SET_NULL)
生活中的多对一关系:班主任,班级关系。一个班主任可以带很多班级,但是每个班级只能有一个班主任
class Headmaster(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Class(models.Model):
class_name = models.CharField(max_length=50)
teacher = models.ForeignKey(Headmaster,null=True,on_delete=models.SET_NULL)
def __str__(self):
return self.class_name
>>> H1 = Headmaster(name='渔夫')
>>> H1.save()
>>> H1
<Headmaster: 渔夫>
>>> H2 = Headmaster(name='农夫')
>>> H2.save()
>>> Headmaster.objects.all()
[<Headmaster: 渔夫>, <Headmaster: 农夫>]
以上创建了两条老师数据
由于我们设置外键关联可以为空null=True
,所以此时在班级表创建时,可以直接保存,不需要提供老师数据
>>> C1 = Class(class_name='一班')
>>> C2 = Class(class_name='二班')
#如果外键设置不为空时,保存会引发以下错误
# IntegrityError: NOT NULL constraint failed: bbs_class.teacher_id
>>> C1.teacher = H1
>>> C2.teacher = H2
>>> C1.save()
>>> C2.save()
将老师分配个班级之后,由于班级表关联了老师字段,我们可以通过班级找到对应老师
虽然老师表中没有关联班级字段,但是也可以通过老师找到他所带的班级,这种查询方式也叫作关联查询
通过模型类名称后追加一个_set
,来实现反向查询
>>> H1.class_set.all()
<QuerySet [<Class: 一班>]>
由于我们这是一个多对一的关系,也就说明我们的老师可以对应多个班级
- 我们可以继续给H1老师分配新的班级
>>> C3 = Class(class_name='三班')
>>> C3.teacher = H1
>>> C3.save()
>>> H1.class_set.all()
[<Class: 一班>, <Class: 三班>]
一个班级只能对应一个老师,外键是唯一的,那么你在继续给C1班级分配一个新的老师时,会覆盖之前的老师信息,并不会保存一个新的老师
>>> H3 = Headmaster(name='伙夫')
>>> H3.save()
>>> C1.teacher
<Headmaster: 渔夫>
>>> C1.teacher=H3
>>> C1.save()
>>> C1.teacher
<Headmaster: 伙夫>
- 把这个班级的老师删除,由于设置了外键字段可以为
null
,此时班级的老师选项为null
>>> t1 = Headmaster.objects.all().first()
>>> t1
>>> c1 = Class.objects.all().first()
<Headmaster: 渔夫>
>>> c1
<Class: 一班>
>>> c1.teacher
<Headmaster: 渔夫>
>>> t1.delete()
(1, {'modelsapp.Headmaster': 1})
>>> c1 = Class.objects.all().first()
>>> c1
<Class: 一班>
>>> c1.teacher
>>> #这里什么都没有,因为此时C1的老师已经是个None了
- 注意:要记得删除之后要重新获取一次数据,否则查看到的结果中还是之前获取到的有老师的班级数据
多对多关系
多对多关系在模型中使用
ManyToManyField
字段定义
多对多关系可以是具有关联,也可以是没有关联,所以不需要明确指定on_delete
属性
生活中,多对多关系:一个音乐家可以隶属于多个乐队,一个乐队可以有多个音乐家
class Artist(models.Model):
artist_name = models.CharField(max_length=50)
def __str__(self):
return self.artist_name
class Band(models.Model):
band_name = models.CharField(max_length=50)
artist = models.ManyToManyField(Artist)
def __str__(self):
return self.band_name
- 创建音乐家以及乐队
>>> from bbs.models import Artist,Band
>>> A1 = Artist.objects.create(artist_name='Jack')
>>> A2 = Artist.objects.create(artist_name='Bob')
>>> B1 = Band.objects.create(band_name='FiveMonthDay')
>>> B2 = Band.objects.create(band_name='SHE')
创建出两个乐队之后对其进行音乐家的添加
多对多字段添加时,可以使用add
函数进行多值增加
>>> B1.artist.add(A1,A2)
>>> B2.artist.add(A2)
B1
乐队含有A1
,A2
两名成员
B2
乐队含有A1
成员
>>> B1.artist.all()
[<Artist: Bob>, <Artist: Jack>]
>>> B2.artist.all()
[<Artist: Jack>]
- 可以在音乐家表中查找某个乐家属于哪些乐队
>>> Band.objects.filter(artist=A1) # 这里使用的是我们模型类来进行查找。
[<Band: SHE>, <Band: FiveMonthDay>] # A1乐家属于,SHE以及FiveMonthDay
>>> Band.objects.filter(artist=A2)
[<Band: SHE>]
- 也可以查找这音乐家在哪个乐队
>>> A1.band_set.all() # 直接通过具体数据对象进行查找
[<Band: SHE>, <Band: FiveMonthDay>]
>>> A2.band_set.all()
[<Band: SHE>]
多对多关联字段的删除,要使用remove
来进行关系的断开
而不是直接使用delete
,remove
只会断开数据之间的联系,但是不会将数据删除
- 在B1乐队中删除A1乐家
>>> B1.artist.remove(A1)
>>> B1.artist.all()
<QuerySet [<Artist: Bob>]>
关联表的查询
如果想要查询的字段在关联表,则使用
表名小写__字段
来进行跨表查询操作
- 创建一个多对一关系的父子表,一个父亲可能有多个儿子
class Father(models.Model):
name = models.CharField(max_length=30)
age = models.CharField(max_length=30)
def __str__(self):
return self.name
class Son(models.Model):
father = models.ForeignKey(Father,on_delete=models.CASCADE)
name = models.CharField(max_length=30)
def __str__(self):
return self.name
- 创建父亲和👦们
>>> f1 = Father.objects.create(name='Jack',age='30')
>>> s1 = Son.objects.create(name='Json',father=f1)
>>> s2 = Son.objects.create(name='Json2',father=f1)
>>> f2 = Father.objects.create(name='Bob',age='40')
>>> s3 = Son.objects.create(name='Json3',father=f2)
- 查询所有父亲名字是
jack
的孩子
>>> Son.objects.filter(father__name__exact='Jack')
[<Son: Json>, <Son: Json2>]
- 查询所有儿子名开头为
J
的父亲
>>> Father.objects.filter(son__name__startswith='J')
[<Father: Jack>, <Father: Jack>, <Father: Bob>]
- 获取到某一个父亲的所有孩子,通过某一条数据的
小写表名_set
反向查询
>>> f1.son_set.all()
>>> [<Son: Json>, <Son: Json2>]
数据的反向查询
默认的,当有某一条数据获取到之后,我们可以通过模型类名称加上一个
_set
,来实现反向查询
- 现在设计两个表为军队和士兵表,并且士兵多对一关联军队
class Aramy(models.Model):
name = models.CharField(max_length=30)
def __str__(self):
return self.name
class Soldier(models.Model):
aramy = models.ForeignKey(Aramy,on_delete=models.CASCADE)
name = models.CharField(max_length=30)
def __str__(self):
return self.name
- 创建一些数据
>>> a1 = Aramy(name='一军')
>>> a1.save()
>>> s1 = Soldier(name='张三',aramy=a1)
>>> s1.save()
>>> s2 = Soldier(name='李四',aramy=a1)
>>> s2.save()
通过soldier_set
我们就可以关联到对应的士兵表
并且对应返回结果可以执行我们常用的filter
,exclude
等查询操作
>>> a1.soldier_set.all()
[<Soldier: 张三>, <Soldier: 李四>]
>>> a1.soldier_set.filter(name='张三')
[<Soldier: 张三>]
也可以通过定义关联字段中的related_name
值,来实现自定义的反向查询名字
- 注意:
related_name
的值必须唯一
class Aramy(models.Model):
name = models.CharField(max_length=30)
def __str__(self):
return self.name
class Soldier(models.Model):
aramy = models.ForeignKey(Aramy,on_delete=models.CASCADE,related_name='soldier')
name = models.CharField(max_length=30)
def __str__(self):
return self.name
接下来通过某条数据反向查询
>>> a1 = Aramy.objects.all()[0]
>>> s1 = Soldier.objects.get(name='张三')
>>> a1.soldier.all()
[<Soldier: 张三>, <Soldier: 李四>]
注意:related_name
一定是一个唯一的值,否则反向查找时会出现二异性错误,也可以将related_name
初始化为+
,来取消反向查询