目录
前言
模型中的映射关系
1 物理外键与逻辑外键
2 Django中的外键(一对一、一对多关系)
3 Django中的多对多关系
3.1 使用ManyToManyField自动建立多对多关系
3.2 另一种建立多对多表的方式
3.3 多对多关系的建立与查询
4 自关联外键
5 外键删除时
Model的Meta内部类
使用Model时遇到的一些问题
1 使用mysql报错 Did you install mysqlclient?
2 You are trying to add a non-nullable field 'xxx' to xxx
前言
本文为Django框架下Model模块的进阶教程,承接的是本专栏的文章《初识Django(七):模型》。本文主要介绍的是Model下外键(一对一、一对多)、多对多关系的使用和Meta类的使用,以及解决一些疑难问题。
模型中的映射关系
1 物理外键与逻辑外键
外键是表中一个特殊的字段,被用于与其他表建立关系。
外键分为物理外键与逻辑外键两种,物理外键在数据库中定义,其级联关系由数据库本身维护;而逻辑外键的关系由自定义的代码维护。由于物理外键会强制要求数据库在删除具有外键的记录时检查数据,在数据量大的情况下会造成数据库资源的浪费,加上mysql对外键的设计不合理,所以一般不建议使用物理外键。
2 Django中的外键(一对一、一对多关系)
Django中的ForeignKey为逻辑外键。当你在Django中声明一个列为ForeignKey时,其实际上存入的是连接到的表的主键,并为此列建立一个索引。ForeignKey的使用如下例所示:
class ForeignModel(models.Model):
data=models.CharField(max_length=100)
class ExampleModel(models.Model):
id=models.CharField(default=CALLABLE_FUNC) #置为随机数函数
username=models.CharField(max_length=30,blank=True)
data=models.ForeignKey(ForeignModel)
如果要建立的两张表记录是一对一关系,记得在外键上添加unique=True属性,当记录的是一对多关系时,则需要在“多”的表中建立外键。
3 Django中的多对多关系
事实上,它原理上和一般的数据库多对多关系的实现并无二致。但在Django中,你可以直接使用ManyToManyField声明一个多对多字段,django会自动为你创建一张记录多对多关系的表。
3.1 使用ManyToManyField自动建立多对多关系
一个例子:
class Example1Model(models.Model):
data1=models.CharField(max_length=50)
class Example2Model(models.Model):
data2=models.CharField(max_length=50)
release=models.ManyToManyField(Example1Model) #多对多关系
运行migrate命令后,查看数据库,可以发现生成了一个app名_example2model_release表,即为记录多对多关系的表。
3.2 另一种建立多对多表的方式
使用Django ManyToMantField字段全自动建立第三张表总是差点味道,也不方便维护,那么,为何不直接将第三张表写成一个模型类呢?这种方式的扩展性更高,而且对强迫症非常友好^ ^。
手动书写联系表,然后在ManyToManyField字段中用through参数指定django使用此联系表做映射,如下所示:
class Example1Model(models.Model):
data1=models.CharField(max_length=50)
class Example2Model(models.Model):
data2=models.CharField(max_length=50)
release=models.ManyToManyField(Example1Model,through="Model1_Model2_Release") #多对多关系
class Model1_Model2_Release(models.Model):
model1=models.ForeignKey(Example1Model,on_delete=DO_NOTHING)
model2=models.ForeignKey(Example2Model,on_delete=DO_NOTHING)
3.3 多对多关系的建立与查询
请看以下代码:
e1=Example1Model.objects.create(data1="1234")
e2=Example2Model.objects.create(data2="5678")
#分别创建两个实例
e2.release.add(e1)
#使用add直接添加对象建立映射关系
e2.release.all()
#正向查询 ,只需要引用ManyToManyField字段
#=> <QuerySet [<Example1Model: Example1Model object (1)>]>
e1.example2model_set.all()
#反查 ,example1model为被查询的类名全小写(即使是django官网那边也没写这个_set怎么来的,我只能自己写了一遍代码确认这玩意- -)
#=> <QuerySet [<Example2Model: Example2Model object (1)>]>
请注意反查引用的字段为[被查询的模型类全小写_set]。
4 自关联外键
有时候会发生这样的情况:表中的一条记录与同一张表中的另一条记录相关联,这种情况就叫做自关联。
自关联的三种关系写法与上文相同,只是将引用的类名改成self即可。
5 外键删除时
对于外键而言,由于其引用的是另一条数据库记录,故而不得不考虑一个问题:当它所引用的记录被删除时,它本身所处的记录又该如何?由于一对一、一对多、多对多关系中都需要用到外键,所以这也是上面这些关系中需要共同考虑的内容。
我们在声明外键时,必须设置一个on_delete参数,通过这个参数决定Django在外键依赖的记录删除时会如何处理这个外键对应的数据。
这个参数有以下几个取值:
- CASCADE:级联删除,当被引用记录删除时,本记录也删除
- DO_NOTHING:什么也不做
- SET_NULL:设为空
- SET_DEFAULT:设为默认值
- PROTECT:保护模式,当删除引用记录时,会报错
- SET():设置一个对象,其中填写一个callable对象(比如说一个方法,返回一个默认模型对象)
Model的Meta内部类
如果说模型中字段的参数是对这个字段的设置,那Meta类就是对整个模型的设置。我们可以通过在模型类中建立Meta类并声明特定名称的字段来调整模型。
下方列出一些常用的Meta字段及其意义:
class ExampleModel(models.Model):
id=models.CharField(default=CALLABLE_FUNC) #置为随机数函数
username=models.CharField(max_length=30,blank=True)
class Meta():
db_table="声明在数据库中对应的表名,不声明django会自动命名为app名_类名"
app_label="这个字段仅在你的模型类不在默认的models.py文件内,用于指定此模型所属的app,内容为所属app的名字"
get_latest_by="这个字段指示django的get_latest()按照哪个字段查询。应为一个模型字段名,此字段为DateField或者DateTimeField"
managed="Boolean。当其为真,django按照Model类修改数据库表,当其为假,django不会进行这些操作。"
ordering="这个字段指示django返回的数据按照哪个字段排序。为一个DateField或DateTimeField。"
verbose_name="此模型的别名。这个字段也可用于模型中的字段(有点拗口,这个选项也可以在模型中的列名中单独设置,这里设置的是表的别名,那里设置的是列的别名)。"
使用Model时遇到的一些问题
1 使用mysql报错 Did you install mysqlclient?
发生在manage.py的makemigrations命令中。这问题呢,是一个连环坑。
最开始的报错图如下所示:
意思是我们没有安装mysqlclient,第一反应肯定是打开命令行pip安装一下包,但pip安装又会报错,说打不开文件“mysql.h”。pip安装不成,只能自己去下whl文件安装了。
下载地址如下所示:
https://www.lfd.uci.edu/~gohlke/pythonlibs/#mysqlclient
下载后以安装whl文件的方式安装完毕再次运行,问题解决。
2 You are trying to add a non-nullable field 'xxx' to xxx
这个其实也不算报错,但是需要使用者注意。即使你确定这个字段在后续生成记录时始终不为空,也最好为其设置一个默认值,否则在为表增加字段时django也会强制你添加一个值。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)