除了语法之外,使用 django 抽象模型和使用带有 django 模型的纯 Python 继承有什么区别?优点和缺点?
更新:我认为我的问题被误解了,我收到了关于抽象模型和继承自 django.db.models.Model 的类之间差异的回复。我实际上想知道从 django 抽象类(Meta:abstract = True)继承的模型类和从“object”(而不是 models.Model)继承的普通 Python 类之间的区别。
这是一个例子:
class User(object):
first_name = models.CharField(..
def get_username(self):
return self.username
class User(models.Model):
first_name = models.CharField(...
def get_username(self):
return self.username
class Meta:
abstract = True
class Employee(User):
title = models.CharField(...
我实际上想知道从 django 抽象类(Meta:abstract = True)继承的模型类和从“object”(而不是 models.Model)继承的普通 Python 类之间的区别。
Django 只会为 models.Model
的子类生成表,所以前者...
class User(models.Model):
first_name = models.CharField(max_length=255)
def get_username(self):
return self.username
class Meta:
abstract = True
class Employee(User):
title = models.CharField(max_length=255)
...将导致生成一个表格,按照...
CREATE TABLE myapp_employee
(
id INT NOT NULL AUTO_INCREMENT,
first_name VARCHAR(255) NOT NULL,
title VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
……而后者……
class User(object):
first_name = models.CharField(max_length=255)
def get_username(self):
return self.username
class Employee(User):
title = models.CharField(max_length=255)
...不会导致生成任何表。
你可以使用多重继承来做这样的事情......
class User(object):
first_name = models.CharField(max_length=255)
def get_username(self):
return self.username
class Employee(User, models.Model):
title = models.CharField(max_length=255)
...这将创建一个表,但它会忽略 User
类中定义的字段,因此您最终会得到一个这样的表...
CREATE TABLE myapp_employee
(
id INT NOT NULL AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
抽象模型为每个子子项创建一个包含整组列的表,而使用“普通”Python 继承创建一组链接表(也称为“多表继承”)。考虑您有两个模型的情况:
class Vehicle(models.Model):
num_wheels = models.PositiveIntegerField()
class Car(Vehicle):
make = models.CharField(…)
year = models.PositiveIntegerField()
如果 Vehicle
是一个抽象模型,您将有一个表:
app_car:
| id | num_wheels | make | year
但是,如果您使用纯 Python 继承,您将有两个表:
app_vehicle:
| id | num_wheels
app_car:
| id | vehicle_id | make | model
其中 vehicle_id
是指向 app_vehicle
中的一行的链接,该行也包含汽车的车轮数。
现在,Django 将以对象的形式很好地组合在一起,因此您可以访问 num_wheels
作为 Car
上的属性,但数据库中的底层表示会有所不同。
更新
为了解决您更新的问题,从 Django 抽象类继承和从 Python 的 object
继承之间的区别在于前者被视为数据库对象(因此它的表被同步到数据库)并且它具有Model
。从普通 Python object
继承的类(及其子类)没有这些品质。
models.OneToOneField(Vehicle)
也相当于继承一个模型类,对吧?这将导致两个单独的表,不是吗?
car
上创建一个属性,例如 num_wheels
,而对于 OneToOneField
,您必须自己取消引用。
app_car
表重新生成所有字段,以便它也具有 num_wheels
字段,而不是具有 vehicle_id
指针?
主要区别在于模型的数据库表是如何创建的。如果您在没有 abstract = True
的情况下使用继承,Django 将为父模型和子模型创建一个单独的表,其中包含每个模型中定义的字段。
如果您对基类使用 abstract = True
,则 Django 只会为从基类继承的类创建一个表 - 无论字段是在基类中定义还是在继承类中定义。
优缺点取决于应用程序的架构。给定以下示例模型:
class Publishable(models.Model):
title = models.CharField(...)
date = models.DateField(....)
class Meta:
# abstract = True
class BlogEntry(Publishable):
text = models.TextField()
class Image(Publishable):
image = models.ImageField(...)
如果 Publishable
类不是抽象类,则 Django 将创建一个包含 title
和 date
列的可发布表,并为 BlogEntry
和 Image
创建单独的表。此解决方案的优势在于,您可以跨所有可发布内容查询基本模型中定义的字段,无论它们是博客条目还是图像。但是因此,如果您例如查询图像,Django 将不得不进行连接......如果创建 Publishable
abstract = True
Django 将不会为 Publishable
创建表,而只会为包含所有字段的博客条目和图像创建表(也继承的)。这会很方便,因为 get 之类的操作不需要连接。
另见Django's documentation on model inheritance。
只是想添加一些我在其他答案中没有看到的东西。
与 python 类不同,field name hiding is not permited 具有模型继承。
例如,我用一个用例试验了如下问题:
我有一个继承自 django 的 auth PermissionMixin 的模型:
class PermissionsMixin(models.Model):
"""
A mixin class that adds the fields and methods necessary to support
Django's Group and Permission model using the ModelBackend.
"""
is_superuser = models.BooleanField(_('superuser status'), default=False,
help_text=_('Designates that this user has all permissions without '
'explicitly assigning them.'))
groups = models.ManyToManyField(Group, verbose_name=_('groups'),
blank=True, help_text=_('The groups this user belongs to. A user will '
'get all permissions granted to each of '
'his/her group.'))
user_permissions = models.ManyToManyField(Permission,
verbose_name=_('user permissions'), blank=True,
help_text='Specific permissions for this user.')
class Meta:
abstract = True
# ...
然后我有了我的 mixin,我希望它覆盖 groups
字段的 related_name
。所以它或多或少是这样的:
class WithManagedGroupMixin(object):
groups = models.ManyToManyField(Group, verbose_name=_('groups'),
related_name="%(app_label)s_%(class)s",
blank=True, help_text=_('The groups this user belongs to. A user will '
'get all permissions granted to each of '
'his/her group.'))
我正在使用这2个mixin,如下所示:
class Member(PermissionMixin, WithManagedGroupMixin):
pass
所以,是的,我希望这会起作用,但事实并非如此。但问题更严重,因为我得到的错误根本没有指向模型,我不知道出了什么问题。
在尝试解决这个问题时,我随机决定更改我的 mixin 并将其转换为抽象模型 mixin。错误变成了这样:
django.core.exceptions.FieldError: Local field 'groups' in class 'Member' clashes with field of similar name from base class 'PermissionMixin'
如您所见,此错误确实解释了发生了什么。
在我看来,这是一个巨大的差异:)
主要区别在于您继承 User 类的时间。一个版本的行为类似于一个简单的类,而另一个版本的行为类似于 Django 模型。
如果您继承基本“对象”版本,您的 Employee 类将只是一个标准类,并且 first_name 不会成为数据库表的一部分。您不能创建表单或使用任何其他 Django 功能。
如果您继承 models.Model 版本,您的 Employee 类将拥有 Django Model 的所有方法,并且它将继承 first_name 字段作为可在表单中使用的数据库字段。
根据文档,Abstract Model“提供了一种在 Python 级别分解常见信息的方法,同时仍然在数据库级别为每个子模型创建一个数据库表。”
在大多数情况下,我会更喜欢抽象类,因为它不会创建单独的表,并且 ORM 不需要在数据库中创建连接。在 Django 中使用抽象类非常简单
class Vehicle(models.Model):
title = models.CharField(...)
Name = models.CharField(....)
class Meta:
abstract = True
class Car(Vehicle):
color = models.CharField()
class Bike(Vehicle):
feul_average = models.IntegerField(...)
不定期副业成功案例分享
models.Model
中使用了一些元类骇客,因此不会拾取超类中定义的字段(除非它们也是models.Model
的子类)。ModelBase
的__new__
调用中,它对类的属性执行初始检查,检查属性值是否具有contribute_to_class
属性 - {您在 Django 中定义的 4} 都可以,因此它们包含在一个名为contributable_attrs
的特殊字典中,最终在迁移期间传递以生成 DDL。