95

除了语法之外,使用 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(...
4

6 回答 6

168

我实际上想知道从 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)
);
于 2013-05-30T14:19:54.643 回答
38

抽象模型为每个子子项创建一个包含整组列的表,而使用“普通”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的类(及其子类)没有这些品质。

于 2013-05-20T17:55:01.917 回答
17

主要区别在于模型的数据库表是如何创建的。如果您在没有abstract = TrueDjango 的情况下使用继承,将为父模型和子模型创建一个单独的表,其中包含每个模型中定义的字段。

如果您使用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单独的表。此解决方案的优势在于,您可以在所有可发布内容中查询基本模型中定义的字段,无论它们是博客条目还是图像。但是因此,如果您例如查询图像,Django 将不得不进行连接......如果让Django 不会为 . 创建表,而只会为包含所有字段(以及继承的字段)的博客条目和图像创建表。这会很方便,因为 get 之类的操作不需要连接。BlogEntryImagePublishable abstract = TruePublishable

另请参阅Django 关于模型继承的文档

于 2013-05-20T17:59:09.503 回答
10

只是想添加一些我在其他答案中没有看到的东西。

与 python 类不同,模型继承不允许字段名称隐藏。

例如,我用一个用例试验了如下问题:

我有一个继承自 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,其中我希望它覆盖related_namegroups字段。所以它或多或少是这样的:

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'

如您所见,此错误确实解释了发生了什么。

在我看来,这是一个巨大的差异:)

于 2013-06-01T13:26:39.043 回答
1

主要区别在于您继承 User 类的时间。一个版本的行为类似于一个简单的类,而另一个版本的行为类似于 Django 模型。

如果您继承基本“对象”版本,您的 Employee 类将只是一个标准类,并且 first_name 不会成为数据库表的一部分。您不能创建表单或使用任何其他 Django 功能。

如果您继承 models.Model 版本,您的 Employee 类将拥有 Django Model的所有方法,并且它将继承 first_name 字段作为可在表单中使用的数据库字段。

根据文档,抽象模型“提供了一种在 Python 级别分解常见信息的方法,同时仍然在数据库级别为每个子模型创建一个数据库表。”

于 2013-06-01T00:43:55.347 回答
1

在大多数情况下,我会更喜欢抽象类,因为它不会创建单独的表,并且 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(...)
于 2020-11-08T21:09:28.767 回答