Django学习之十四:Django ORM继承关系
Django ORM继承关系
参考:https://www.cnblogs.com/holbrook/archive/2012/03/18/2405036.html
因为关系数据库是没有自然有效的方法来表示表与表之间的继承关系。而model layer 是用类来表示表,而类是面向对象的范畴,继承是它的一大特性。自然可以使用model class 通过继承来表示它们之间的关系,但是要映射到关系数据库应该是怎样表示。这就得从数据库存储的角度来看,大牛们采用了几种在数据库中映射继承关系的方式:主要三种
single_table/table_per_class/joined 主要三种
由于python语言的动态性,python类是可以进行多重继承关系,那么还有多重继承关系的映射。
最后还有一种代理模型继承。
对于抽象model的定义,必须在其内嵌类中指定一个abstract = True的metadata,需要注意的是,虽然subclass继承了抽象类,同时继承了class meta内嵌类的内容;但是有些内嵌类中的属性是不会被继承的,如对于abstract = True 这个metadata是没有继承的。也就是说在构建subclass 的时候,会将abstract 设置为Flase。如果要构建一个抽象类继承自一个抽象类, 那么就需要在子抽象类中显示的声明abstract = True。相同的如db_table也不会继承到子类中,子类需要自己声明。
因为orm的可继承和三种继承方式的特点,那么一个model映射一个数据库表的说法就错了。抽象model是不会映射任何数据库表的。
1. SINGLE_TABLE(django好像不支持)
使用单个表,且数据库中只有一个映射表,这个表就代表了整个继承树,继承树中的多个子类model只会操作自己新增的字段,不会因为整个继承树使用一个表而能操作其它非自己的字段;这种就是一表收揽所有字段,字段虽然没有冗余了,但是数据父类数据对象是冗余的。同一个父对象有多个子对象。
single_table 在model layer上定义就是父类和子类
from django.db import models
class Vehicle(models.Model): # 抽象类在migration时不会创建表
name = models.CharField(max_length=32)
class Meta:
abstract = True
class Car(Vehicle):
Color = models.CharField(max_length=32)
class Meta:
db_table = 'mbook_vehicle'
class Bike(Vehicle):
Weight = models.IntegerField()
class Meta:
db_table = 'mbook_vehicle'
2. TABLE_PER_CLASS
有字段冗余,每个类一张表,父类是抽象类不创建父类映射的表,而是将父类的字段复制到子类映射表中去。
from django.db import models
class Animal(models.Model):
name = models.CharField(max_length=32)
class Meta:
abstract = True
class Dog(Animal):
Color = models.CharField(max_length=32)
class Bird(Animal):
Size = models.CharField(max_length=32)
使用python manage.py sqlmigrate mbook 0005 查看sql如下:
BEGIN;
--
-- Create model Bird
--
CREATE TABLE `mbook_bird` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `name` varchar(32) NOT NULL, `Size` varchar(32) NOT NULL);
--
-- Create model Dog
--
CREATE TABLE `mbook_dog` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `name` varchar(32) NOT NULL, `Color` varchar(32) NOT NULL);
COMMIT;
-- 只创建了具体类表,抽象父类是没有创建的。并且name字段是冗余的。
因为Animal类是抽象类,所以没有objects属性,即没有manager管理器,所以不能操作数据对象,映射表也没创建,自然也不能操作数据。只有子类才能操作。
抽象继承操作没什么说的,就是操作继承的具体子类就可以了。这个是用的比较多的。
3. JOINED
没有冗余,每个类一张表,要同时操作父表和子表
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=10)
class Man(Person):
job = models.CharField(max_length=20)
class Woman(Person):
makeup = models.CharField(max_length=20)
使用python manage.py sqlmigrate mbook 0004 可以看到创建表的sql语句
BEGIN;
--
-- Create model Person
--
CREATE TABLE `mbook_person` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `name` varchar(32) NOT NULL); -- id是django自动生成的
--
-- Create model Man
--
CREATE TABLE `mbook_man` (`person_ptr_id` integer NOT NULL PRIMARY KEY, `job` varchar(32) NOT NULL);
--
-- Create model Woman
--
CREATE TABLE `mbook_woman` (`person_ptr_id` integer NOT NULL PRIMARY KEY, `makeup` varchar(32) NOT NULL);
ALTER TABLE `mbook_man` ADD CONSTRAINT `mbook_man_person_ptr_id_9ab049ad_fk_mbook_person_id` FOREIGN KEY (`person_ptr_id`) REFERENCES `mbook_person` (`id`);
-- 添加一个外键
ALTER TABLE `mbook_woman` ADD CONSTRAINT `mbook_woman_person_ptr_id_67db6679_fk_mbook_person_id` FOREIGN KEY (`person_ptr_id`) REFERENCES `mbook_person` (`id`);
COMMIT;
这种,在子类没有新建父类的字段,但是子类可以使用父类的字段,创建子类的数据时,因为子类是继承自父类,在python-level,子类是有父类的字段的,所以子类在创建新实例是,在python-level看上去是在自己的表中添加了,其实是操作了了两个表,即子类自己映射的表中,和其继承的父类表中都添加了数据。所以叫做多表继承,也就是子类和父类映射的表都会参与继承。实质是添加了onetoone的关系。
下面是数据操作:
1. 通过Man创建数据,相应Person中是否有数据
>>> m = Man.objects.create(job='programmer')
mbook_man:
1 programmer
mbook_person:
1
两个表都有数据,但是由于没有指定name,所以person表没有name.
从效果,可以看出应用途径,多表继承字段不冗余,也达到了继承表的效果。
2. 通过删除Man记录,
>>> m.delete()
(2, {'mbook.Man': 1, 'mbook.Person': 1})
结果是两个表的数据都被删除了;进一不说明多表继承是有效的,继承的效果在python层面和数据库层面得到了理想相同的效果。
4. 代理继承
也就是一个model继承自一个具体类,但是只是一个代理类,也就是通过这个继承出来的代理类,来操作父类,这个代理类只需要在class meta中添加proxy = True 的metadata就可以。
出现代理类这种继承,是有这样一个需求,就是:因为多表继承,子类会创建表,现在需求是不想创建新的表,只是在子类中添加在python-level的一些行为(因为不能去修改父类,在父类中添加新的python层面行为),所以只能通过继承来添加,但是又不用子类有新的field字段,也不用创建一个新的子类映射的表。基于这种需求,就出现了,代理继承模式,就是像抽象继承的的反过程(抽象类没表,子类具体类有表),代理是父类是具体类有表,代理继承的类是没有表的,只在python代码层面新增了方法,而不用代理子类创建新的表。
和抽象不同的是,抽象类是没有manager的,是不能够通过抽象类访问数据库具体数据的。
而代理类则不同了,它是有manager的,是可以通过manager操作它所代理的父类具体类中的数据的。
代理继承的好处:
你可以在代理 model 中改变默认的排序设置和默认的 manager ,更不会对原始 model 产生影响。
如代理类 设置排序,这样问代理类获取数据是排序的结果;使用具体类父类获取数据得到的非排序结果;这个应用是非常常见的,在不改变model的情况下。
代理类的限制:
- 代理 model 必须继承自一个非抽象基类
- 你不能继承自多个非抽象基类,这是因为一个代理 model 不能连接不同的数据表
- 代理 model 也可以继承任意多个抽象基类,但前提是它们没有定义任何 model 字段
代理类的manager
- 如果你没有在代理 model 中定义任何 manager ,代理 model 就会从父类中继承 manager
- 如果你在代理 model 中定义了一个 manager ,它就会变成默认的 manager ,不过定义在父类中的 manager 仍是有效的
Django学习之十四:Django ORM继承关系的更多相关文章
- Django学习笔记〇四——数据库ORM的使用(有待修改)
Django框架基本上都是要和数据库结合使用的,我在以前讲过SQLAlchemy框架的使用,Django支持的不是SQLAlchemy,但是也内嵌了ORM框架,可以不需要直接面对数据库编程,而可以通过 ...
- Django学习之十: staticfile 静态文件
目录 Django学习之十: staticfile 静态文件 理解阐述 静态文件 Django对静态文件的处理 其它方面 总结 Django学习之十: staticfile 静态文件 理解阐述 ...
- “全栈2019”Java第四十四章:继承
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例
python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例 新浪爱彩双色球开奖数据URL:http://zst.aicai.com/ssq/openInfo/ 最终输出结果格 ...
- Linux学习之十四、管线命令
Linux学习之十四.管线命令 地址:http://vbird.dic.ksu.edu.tw/linux_basic/0320bash_6.php
- 风炫安全WEB安全学习第二十四节课 利用XSS钓鱼攻击
风炫安全WEB安全学习第二十四节课 利用XSS钓鱼攻击 XSS钓鱼攻击 HTTP Basic Authentication认证 大家在登录网站的时候,大部分时候是通过一个表单提交登录信息. 但是有时候 ...
- Django框架(十四)-- forms组件、局部钩子、全局钩子
一.什么是forms组件 forms组件就是一个类,可以检测前端传来的数据,是否合法. 例如,前端传来的邮箱数据,判断邮件格式对不对,用户名中不能以什么开头,等等 二.forms组件的使用 1.使用语 ...
- Django学习(三)---Models(ORM框架)
1) Django 中Models是与数据库相关的,与数据库相关的代码一般写在 models.py中,Django 支持 sqlite3, MySQL, PostgreSQL等数据库,只需要在sett ...
- Django学习笔记之Models与ORM操作
一.ORM增加 from django.db import models class Publisher(models.Model): name = models.CharField(max_leng ...
随机推荐
- LG5200 「USACO2019JAN」Sleepy Cow Sorting 树状数组
\(\mathrm{Sleepy Cow Sorting}\) 问题描述 LG5200 题解 树状数组. 设\(c[i]\)代表\([1,i]\)中归位数. 显然最终的目的是将整个序列排序为一个上升序 ...
- ASP.NET Core 新建项目(Windows)
对于任何语言和框架,都是从 Hello World 开始的,这个非常简单,但却有十分重大的意义,ASP.NET Core 基础教程也会以 Hello World 开始 为什么呢? 因为能够运行 Hel ...
- Java Web 之 SSM笔记
好久没有写博文了呀呀呀........博客园的MarkDown还是...算了吧 自定义 Restful 风格结果集 参考资料 [SpringBoot专题]统一异常处理和统一数据返回前言实践运行结果 如 ...
- git diff比较版本差异(生成补丁)
1.git diff [<options>] <commit> <commit> options 使用--name-only(git diff HEAD cd504 ...
- RHEL8/CentOS8的基础防火墙配置-用例
systemctl systemctl unmask firewalld #执行命令,即可实现取消服务的锁定 systemctl mask firewalld # 下次需要锁定该服务时执行 syste ...
- [原创]浅谈在创业公司对PMF的理解
[原创]浅谈在创业公司对PMF的理解 在创业时,大多数人都常谈一个词叫"MVP“,但PMF谈的比较少,PMF在创业公司尤为重要,以下谈谈个人一些看法. 1.什么是PMF? 创业公司:一种是找 ...
- Kubernetes 学习(十)Kubernetes 容器持久化存储
0. 前言 最近在学习张磊老师的 深入剖析Kubernetes 系列课程,最近学到了 Kubernetes 容器持久化存储部分 现对这一部分的相关学习和体会做一下整理,内容参考 深入剖析Kuberne ...
- BCompare注册文件+密钥被撤销解决方案
注册码: rssAPVg2OpBjDVo3E0DhGWrjPIq0hsTSuNz13wTuzVHfb2mRgO9bZKn9Bl42D5YEyMSYPXsxzcb08dqbRlbzWNJzJXE6YVa ...
- LeetcCode 27:移除元素 Remove Element(python、java)
公众号:爱写bug 给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度. 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) ...
- 如何写APA格式的论文
一.一般准则 FONT : TIMES NEW ROMAN SIZE : 12 DOUBLE-SPACING INDENT : ...