目录结构
3.1.第一步:在【helloworld/hello/models.py】里新增模型类
3.2.第二步:在【helloworld/hello/admin.py】里注册模型类
3.3.第三步:在【helloworld/】根目录下执行生成数据表的相关语句
3.6.第六步:在后台新增1条银行卡基本信息(包含银行卡拓展信息)
4.1.类OneToOneField里的两个个必填参数对应入参值的简单分析
4.1.1.第一步:看类OneToOneField源码里的【__init__】方法里的内容
4.1.2.第二步:结合实际业务代码,对必填参数【to】的参数值和必填参数【on_delete】的参数值做分析
4.1.2.2.对必填参数【on_delete】的参数值的理解
4.2.类【StackedInline】和类【TabularInline】的具体运用
1.写这篇博客的目的
主要记录:
- 如何通过django去创建【一对一】的表关系;
- 以及如何通过【一对一】的表关系去走对应的完整业务操作;
- 通过修改django里的哪些代码配置,使当一张【一】表的数据被删除后,对应另外一张【一】表的数据会有对应的哪些变化?
2.【一对一】表关系对应的业务例子
我们在现实生活里会遇到很多一对一的场景。
比如一个最常见的例子是:一张银行卡记录了银行卡账号和银行卡用户名,现在我们要开发一个小功能用来记录用户的银行卡绑定的手机号和绑定的邮箱和绑定的住址,为了不影响最初的存储银行卡数据的数据表【CardInfo】的表结构,可以新增一张数据表【Card_of_Expand_information】来存储这些新增的表字段;
数据表【CardInfo】里的一条数据A的主键是数据表【Card_of_Expand_information】里的一条数据B的外键,且数据A只能关联数据B,且数据B只能关联数据A,那么这两张表就是一对一的关系;
完整操作流程可以看接下来的内容;
3.完整操作流程
3.1.第一步:在【helloworld/hello/models.py】里新增模型类
细节:
- 模型类【Bank】和模型类【CardInfo】是之前就已经创建过的模型类,我们这次只是新增了模型类【Card_of_Expand_information】,但为了体现这三个模型类存在表关系就都把这三个模型类的代码都贴出来了;
from django.db import models class Bank(models.Model): '''银行信息''' bank_name = models.CharField(max_length=50,verbose_name="银行名称") city = models.CharField(max_length=30,verbose_name="所在城市") point = models.CharField(max_length=60,verbose_name="所在网点") class Meta: verbose_name_plural = "银行" def __str__(self): return self.bank_name class CardInfo(models.Model): '''银行卡基本信息''' card_id = models.CharField(max_length=30, verbose_name="卡号") card_name = models.CharField(max_length=10, verbose_name="姓名") card_info = models.ForeignKey(to=Bank,on_delete=models.CASCADE,verbose_name="选择银行",) # card_info = models.ForeignKey(to=Bank,on_delete=models.PROTECT,verbose_name="选择银行",) # card_info = models.ForeignKey(to=Bank,on_delete=models.SET_NULL,verbose_name="选择银行",null=True) # card_info = models.ForeignKey(to=Bank,on_delete=models.SET_DEFAULT,verbose_name="选择银行",default=999999999) # card_info = models.ForeignKey(to=Bank,on_delete=models.SET(value=7777777),verbose_name="选择银行",) # card_info = models.ForeignKey(to=Bank,on_delete=models.DO_NOTHING,verbose_name="选择银行",) class Meta: verbose_name_plural = '银行卡基本信息' verbose_name = '银行卡基本信息详情页' # def __str__(self): # return self.card_id class Card_of_Expand_information(models.Model): '''银行卡拓展信息''' card_number = models.OneToOneField(to=CardInfo, on_delete=models.CASCADE, ) tel = models.CharField(max_length=30, verbose_name="电话", default="") mail = models.CharField(max_length=30, verbose_name="邮箱", default="") city = models.CharField(max_length=10, verbose_name="城市", default="") address = models.CharField(max_length=30, verbose_name="详细地址", default="") class Meta: verbose_name_plural = '银行卡拓展信息' verbose_name = "银行卡拓展信息详情页"
3.2.第二步:在【helloworld/hello/admin.py】里注册模型类
细节:
- 标注红色的代码是这次新增的;
- 为了能让数据表【Card_of_Expand_information】的数据能跟数据表【CardInfo】的数据在同一个详情页面展示,即为了让银行卡基本信息和银行卡拓展信息在同一个详情页展示,就需要在注册类【ControlCardInfo】里重写属性inlines的值,且属性inlines的值的数据类型必须是list,且list里的值必须是类tackedInline对应的一个子类类名。
- 类【StackedInline】的作用:让银行卡拓展信息表的表字段在详情页纵向展示(主流都选择用这个类);
- 类【TabularInline】的作用:让银行卡拓展信息表的表字段在详情页横向展示;
具体实际运用可以看下面的代码块:
from django.contrib import admin # Register your models here. from hello import models class ControlBank(admin.ModelAdmin): '''银行信息''' # 显示的字段 list_display = ["id","bank_name","city","point"] class MoreInfo(admin.StackedInline): model = models.Card_of_Expand_information class ControlCardInfo(admin.ModelAdmin): '''银行卡信息''' # 显示的字段 list_display = ["id","card_id","card_name","card_info"] # 在银行卡基本信息详情页面里显示银行卡拓展信息 inlines = [MoreInfo] admin.site.register(models.Bank,ControlBank) admin.site.register(models.CardInfo,ControlCardInfo)
3.3.第三步:在【helloworld/】根目录下执行生成数据表的相关语句
执行的相关语句和顺序是:
- 首先,先执行语句【python manage.py makemigrations】
- 最后,再执行语句【python manage.py migrate】
相关结果如下:
- 会生成一张对应的数据表【hello_card_of_expand_information】;
3.4.第四步:重启服务
3.5.第五步:成功登陆admin管理后台
3.6.第六步:在后台新增1条银行卡基本信息(包含银行卡拓展信息)
4.相关知识点
4.1.类OneToOneField里的两个必填参数对应入参值的简单分析
细节:
- 从类OneToOneField的类名,可以直观知道单词OneToOneField的中文含义是【一对一】;
4.1.1.第一步:看类OneToOneField源码里的【__init__】方法里的内容
class OneToOneField(ForeignKey): """ A OneToOneField is essentially the same as a ForeignKey, with the exception that it always carries a "unique" constraint with it and the reverse relation always returns the object pointed to (since there will only ever be one), rather than returning a list. """ # Field flags many_to_many = False many_to_one = False one_to_many = False one_to_one = True related_accessor_class = ReverseOneToOneDescriptor forward_related_accessor_class = ForwardOneToOneDescriptor rel_class = OneToOneRel description = _("One-to-one relationship") def __init__(self, to, on_delete, to_field=None, **kwargs): kwargs['unique'] = True super().__init__(to, on_delete, to_field=to_field, **kwargs)
截取这部分源码:
def __init__(self, to, on_delete, to_field=None, **kwargs): kwargs['unique'] = True super().__init__(to, on_delete, to_field=to_field, **kwargs)
从这部分源码可以简单看出来:类OneToOneField被实例化时,必须给两个必填参数【to】和【on_delete】分别赋值,其他入参的入参值一般情况下保持默认值即可;
4.1.2.第二步:结合实际业务代码,对必填参数【to】的参数值和必填参数【on_delete】的参数值做分析
实际业务代码如下:
class Card_of_Expand_information(models.Model): '''银行卡拓展信息''' card_number = models.OneToOneField(to=CardInfo, on_delete=models.CASCADE, ) tel = models.CharField(max_length=30, verbose_name="电话", default="") mail = models.CharField(max_length=30, verbose_name="邮箱", default="") city = models.CharField(max_length=10, verbose_name="城市", default="") address = models.CharField(max_length=30, verbose_name="详细地址", default="") class Meta: verbose_name_plural = '银行卡拓展信息' verbose_name = "银行卡拓展信息详情页"
截取这部分业务代码:
card_number = models.OneToOneField(to=CardInfo, on_delete=models.CASCADE, )
4.1.2.1.对必填参数【to】的参数值的理解
参数【to】的含义是:关联到对应的一张表(这张表在一对一表关系里是两张表里被关联的那张表);
参数【to】的参数值:必须只能是这个【一对一表关系里的】两个模型类里的其中一个被关联的模型类的模型类名;
4.1.2.2.对必填参数【on_delete】的参数值的理解
参数【on_delete】的含义是:通过类OneToOneField关联起来的表关系为一对一的两张表,当为一的表的表数据被物理删除后,在为一的另外一张表的表里跟这些被物理删除的表数据关联的表数据会发生对应的变化;
参数【on_delete】的参数值有哪些并各自有哪些使用场景,可以参考之前写的这篇博客(因为这篇博客已经写的很详细了,所以在这篇博客里就不重复写一遍了),这篇博客地址为:4.1.2.2.对必填参数【on_delete】的参数值的理解;
4.2.类【StackedInline】和类【TabularInline】的具体运用
首先,我们要知道这两个类分别实现的功能是:
- 类【StackedInline】的作用:让银行卡拓展信息表的表字段在详情页纵向展示(主流都选择用这个类);
- 类【TabularInline】的作用:让银行卡拓展信息表的表字段在详情页横向展示;
4.2.1.类【StackedInline】的具体运用
假如现在实际代码块如下(看标注红色的代码):
class ControlBank(admin.ModelAdmin): '''银行信息''' # 显示的字段 list_display = ["id","bank_name","city","point"] class MoreInfo(admin.StackedInline): model = models.Card_of_Expand_information class ControlCardInfo(admin.ModelAdmin): '''银行卡信息''' # 显示的字段 list_display = ["id","card_id","card_name","card_info"] # 在银行卡基本信息详情页面里显示银行卡拓展信息 inlines = [MoreInfo] admin.site.register(models.Bank,ControlBank) admin.site.register(models.CardInfo,ControlCardInfo)
对应的详情页面里的内容展示效果如下:
4.2.2.类【TabularInline】的具体运用
假如现在实际代码块如下(看标注红色的代码):
class ControlBank(admin.ModelAdmin): '''银行信息''' # 显示的字段 list_display = ["id","bank_name","city","point"] class MoreInfo(admin.TabularInline): model = models.Card_of_Expand_information class ControlCardInfo(admin.ModelAdmin): '''银行卡信息''' # 显示的字段 list_display = ["id","card_id","card_name","card_info"] # 在银行卡基本信息详情页面里显示银行卡拓展信息 inlines = [MoreInfo] admin.site.register(models.Bank,ControlBank) admin.site.register(models.CardInfo,ControlCardInfo)
对应的详情页面里的内容展示效果如下: