深入Django ORM的繼承關係

來源:互聯網
上載者:User
文章目錄
  • JOINED映射
  • TABLE_PER_CLASS映射
  • SINGLE_TABLE映射
  • 代理模型
  • 多重繼承
  • 小結

ORM中通常將對象引用映射到外鍵,但是對於繼承,關聯式資料庫中沒有自然有效方法來對應。從資料存放區的角度來看,在映射繼承關係時,可以採用幾種方式(參考JPA中的InheritanceType.定義):

  1. 使用單個表,在JPA中稱作SINGLE_TABLE。整個繼承樹共用一張表。使用唯一的表,包含所有基類和子類的欄位。
  2. 每個具體類一張表,在JPA中稱作TABLE_PER_CLASS。這種方式下,每張表都包含具體類和繼承樹上所有父類的欄位。因為多個表中有重複欄位,從整個繼承樹上來說,欄位是冗餘的。
  3. 每個類一張表,繼承關係通過表的JOIN操作來表示。在JPA中稱作JOINED。這種方式下,每個表只包含類中定義的欄位,不存在欄位冗餘,但是要同時操作子類和所有父類所對應的表。

Django的ORM也支援上述三種繼承策略,同時,得益於python的動態特性,還支援代理模型和多重繼承關係的映射。

JOINED映射

如果在Django中實現了Model的繼承關係,如下:

from django.db import models

class Person(models.Model):
name = models.CharField(maxlength=10)

class Man(Person):
job = models.CharField(maxlength=20)

class Woman(Person):
makeup = models.CharField(maxlength=20)

則使用manage.py執行sqlall命令時,會看到這樣的結果:

CREATE TABLE "uom_person" (
"id" integer NOT NULL PRIMARY KEY,
"name" varchar(10) NOT NULL
)
;

CREATE TABLE "uom_man" (
"person_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "uom_person" ("id"),
"job" varchar(20) NOT NULL
)
;

CREATE TABLE "uom_woman" (
"person_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "uom_person" ("id"),
"makeup" varchar(20) NOT NULL
)
;

可見,Django ORM中預設使用JOINED方式來實現繼承關係的映射。

TABLE_PER_CLASS映射

如果要實現每個具體類一張表,只需要將父類指定為抽象類別(abstract),這樣就不會建立父類對應的表,而將父類的欄位複製到子類中去映射。如下:

from django.db import models

class Person(models.Model):
name = models.CharField(max_length=10)

class Meta:
abstract = True

class Man(Person):
job = models.CharField(max_length=20)

class Woman(Person):
makeup = models.CharField(max_length=20)

sqlall 的結果:

CREATE TABLE "uom_man" (
"id" integer NOT NULL PRIMARY KEY,
"name" varchar(10) NOT NULL,
"job" varchar(20) NOT NULL
)
;

CREATE TABLE "uom_woman" (
"id" integer NOT NULL PRIMARY KEY,
"name" varchar(10) NOT NULL,
"makeup" varchar(20) NOT NULL
)
;

將父類聲明為abstract時,該類將沒有objects屬性,也就是說沒有Manager方法,所有無法進行資料操作,只有子類才能進行。

SINGLE_TABLE映射

在TABLE_PER_CLASS的基礎上,如果進一步指定子類的映射表名與父類的相同,則子類和父類將映射到同一張表,對所有的子類都這樣指定,就可以實現SINGLE—_TABLE映射:

from django.db import models

class Person(models.Model):
name = models.CharField(max_length=10)
class Meta:
abstract = True

class Man(Person):
job = models.CharField(max_length=20)
class Meta:
db_table = 'oum_person'

class Woman(User):
makeup = models.CharField(max_length=20)

sqlall 的結果:

CREATE TABLE "oum_person" (
"id" integer NOT NULL PRIMARY KEY,
"name" varchar(10) NOT NULL,
"job" varchar(20) NOT NULL
)
;

CREATE TABLE "uom_woman" (
"user_ptr_id" integer NOT NULL PRIMARY KEY,
"makeup" varchar(20) NOT NULL
)
;

上面的例子中只指定了一個子類,可以看出因為是在子類上指定,所以Django ORM更加靈活,可以控制單個子類的映射方式,從而實現任意的映射結構。

代理模型

有這樣一種常見的情境:使用某些庫(lib)中的類,只是想擴充一些方法,而不想改變其資料存放區結構。在Python中,可以通過在Meta類中增加約束proxy=True來實現。此時“子類”稱為“父類”的代理類,子類中只能增加方法,而不能增加屬性。比如上面的例子中,如果希望Person繼承Django內建的User類,又不希望破壞User類的資料存放區,則可以指定Person的proxy=True:

from django.db import models
from django.contrib.auth.models import User

class Person(User):
# name = models.CharField(max_length=10)
class Meta:
proxy = True

def do_something(self):
...

class Man(Person):
job = models.CharField(max_length=20)

class Woman(Person):
makeup = models.CharField(max_length=20)

sqlall的結果為:

CREATE TABLE "uom_man" (
"user_ptr_id" integer NOT NULL PRIMARY KEY,
"job" varchar(20) NOT NULL
)
;

CREATE TABLE "uom_woman" (
"user_ptr_id" integer NOT NULL PRIMARY KEY,
"makeup" varchar(20) NOT NULL
)
;
多重繼承

python支援多重繼承,儘管在Model層不推薦使用多重繼承,但Django的ORM還是支援這樣的使用方式:

class Mixin1(models.Model):
attr1 = models.CharField(max_length=10)
class Mixin2(models.Model):
attr1 = models.CharField(max_length=10)
class Multiple(Mixin1,Mixin2):
attr3 = models.CharField(max_length=10)

sqlall的結果是:

CREATE TABLE "uom_mixin1" (
"id" integer NOT NULL PRIMARY KEY,
"attr1" varchar(10) NOT NULL
)
;

CREATE TABLE "uom_mixin2" (
"id" integer NOT NULL PRIMARY KEY,
"attr1" varchar(10) NOT NULL
)
;

CREATE TABLE "uom_multiple" (
"mixin2_ptr_id" integer NOT NULL UNIQUE REFERENCES "uom_mixin2" ("id"),
"mixin1_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "uom_mixin1" ("id"),
"attr3" varchar(10) NOT NULL
)
;

多重繼承的時候,子類的ORM映射會選擇第一個父類作為主鍵管理,其他的父類作為一般的外鍵管理。

小結

Django ORM在映射繼承關係時非常靈活,不僅能夠實現JPA約定的SINGLE_TABLE、TABLE_PER_CLASS、JOINED三種方式,還可以靈活的自訂;甚至通過python的動態語言特性,支援代理模型和多重繼承的功能。但是正因為靈活,所以在使用的時候一定要非常注意,通過manage.py的sqllall功能,觀察產生的sql語句,可以驗證繼承的實現機制,避免帶來意想不到的問題。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.