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 ...
随机推荐
- 【作用域】词法作用域(静态作用域,如:js)、动态作用域(如:.bash脚本)
作用域 作用域是指程序源代码中定义变量的区域. 作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限. JavaScript 采用词法作用域(lexical scoping),也就是静态作 ...
- node端console.log输出不同颜色文字
我们知道console.log直接输出是按着终端的默认颜色来显示的, console.log('message') 那么如何指定他们的颜色显示呢?很简单,直接再加一个参数就可以了,例如: consol ...
- shell脚本的输入以及脚本拥有特效地输出
shell脚本的输入以及脚本拥有特效地输出 shell脚本输入之read命令 之前是直接在sh 后加参数 现在是另一种方式 语法:read -参数 -p:给出提示符.默认不支持"\n&quo ...
- Python进阶-Ⅷ 匿名函数 lambda
1.匿名函数的引入 为了解决那些功能很简单的需求而设计的一句话函数 def func(i): return 2*i # 简化之后 func = lambda i:2*i #todo 其中:func是函 ...
- Apex API 请求
Salesforce与网络服务的通信 在Salesforce中可以利用Apex类与远程站点的网络服务进行通信.当远程网络服务支持REST方法时,开发者可以利用Apex代码进行数据的操作. 设置远程站点 ...
- 记一次linux下安装redis, 设置redis服务, 及添加环境变量
一. redis的安装 cd /opt # ...
- Xamarin.Forms移动开发系列2:创建和调试
摘要 本文将介绍如何通过VS2019创建Xamarin.Forms应用程序,以及如何进行调试. 前言 本文介绍Xamarin.Froms应用程序的创建和调试. 开发环境 1.Visual Studio ...
- [LeetCode] 99. Recover Binary Search Tree 复原二叉搜索树
Two elements of a binary search tree (BST) are swapped by mistake. Recover the tree without changing ...
- MySQL学习记录(导入Excel表到数据库,并筛选条件输出)
附上:重置mysql账号密码方法 ubuntu系统下mysql重置密码和修改密码操作 - skh2015java的博客 - CSDN博客(改完重启,登录mysql要root/sudo权限) Cento ...
- mac生成iOS证书(配图)
当我们完成一个APP的时候,需要发布到各大平台让用户下载.而iOS用户是一个不可忽视的群体. 想要在 App Store发布APP,总结了下需要三个步骤. 第一步生成APPID. 第二步生成证书 ce ...