关于python:django抽象模型与常规继承

django abstract models versus regular inheritance

除了语法之外,使用django抽象模型和对django模型使用纯Python继承有什么区别?利弊?

更新:我认为我的问题被误解了,我收到了对抽象模型和继承自django.db.models.model的类之间差异的响应。实际上,我想知道继承自Django抽象类(meta:abstract=true)的模型类与继承自say"object"(而不是models.model)的纯Python类之间的区别。

下面是一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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(...


I actually want to know the difference between a model class that
inherits from a django abstract class (Meta: abstract = True) and a
plain Python class that inherits from say, 'object' (and not
models.Model).

Django只为models.Model的子类生成表,所以前者…

1
2
3
4
5
6
7
8
9
10
11
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)

…将导致沿着以下行生成单个表…

1
2
3
4
5
6
7
CREATE TABLE myapp_employee
(
    id         INT          NOT NULL AUTO_INCREMENT,
    first_name VARCHAR(255) NOT NULL,
    title      VARCHAR(255) NOT NULL,
    PRIMARY KEY (id)
);

…而后者……

1
2
3
4
5
6
7
8
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)

…不会导致生成任何表。

您可以使用多个继承来执行这样的操作…

1
2
3
4
5
6
7
8
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类中定义的字段,因此您将得到这样的表…

1
2
3
4
5
6
CREATE TABLE myapp_employee
(
    id         INT          NOT NULL AUTO_INCREMENT,
    title      VARCHAR(255) NOT NULL,
    PRIMARY KEY (id)
);


一个抽象模型为每个子类创建一个具有完整列集的表,而使用"纯"python继承创建一组链接表(又称"多表继承")。考虑您有两个模型的情况:

1
2
3
4
5
6
7
class Vehicle(models.Model):
  num_wheels = models.PositiveIntegerField()


class Car(Vehicle):
  make = models.CharField()
  year = models.PositiveIntegerField()

如果Vehicle是一个抽象模型,您将拥有一个表:

1
2
app_car:
| id | num_wheels | make | year

但是,如果使用纯python继承,则会有两个表:

1
2
3
4
5
app_vehicle:
| id | num_wheels

app_car:
| id | vehicle_id | make | model

其中vehicle_idapp_vehicle中一行的链接,该行还具有汽车车轮的数量。

现在,Django将以对象形式将其很好地组合在一起,这样您就可以访问num_wheels作为Car的属性,但数据库中的底层表示形式将不同。

更新

为了解决更新后的问题,从django抽象类继承和从python的object继承的区别在于前者被视为数据库对象(因此它的表被同步到数据库),并且它的行为类似于Model。继承一个普通的python object,类(及其子类)没有这些特性。


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

与Python类不同的是,模型继承不允许字段名隐藏。

例如,我对一个用例的问题进行了如下的实验:

我从Django的auth permissionMixin继承了一个模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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

    # ...

然后我有了我的混音器,除其他外,我想要它覆盖groups场的related_name。所以大致是这样的:

1
2
3
4
5
6
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.'))

我使用的这两种混音器如下:

1
2
class Member(PermissionMixin, WithManagedGroupMixin):
    pass

所以,是的,我希望这能奏效,但没有。但问题更严重,因为我得到的错误根本没有指向模型,我不知道出了什么问题。

在尝试解决这个问题时,我随机决定更改我的mixin并将其转换为抽象模型mixin。错误更改为:

1
django.core.exceptions.FieldError: Local field 'groups' in class 'Member' clashes with field of similar name from base class 'PermissionMixin'

正如您所看到的,这个错误确实解释了正在发生的事情。

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


主要区别在于如何创建模型的数据库表。如果使用不带abstract = True的继承,django将为父模型和子模型创建一个单独的表,其中包含每个模型中定义的字段。

如果将abstract = True用于基类,Django将只为从基类继承的类创建一个表-无论字段是在基类或继承类中定义的。

优缺点取决于应用程序的体系结构。给出以下示例模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
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将创建一个包含列titledate的可发布表,并为BlogEntryImage分别创建表。此解决方案的优点是,无论是博客条目还是图片,您都可以跨所有发布文件查询基本模型中定义的字段。但因此,如果您查询图像,Django将不得不进行连接…如果制作Publishableabstract = Truedjango,则不会为Publishable创建表,而是只为包含所有字段(也包括继承字段)的日志和图像创建表。这很方便,因为对于诸如get这样的操作不需要任何联接。

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


主要区别在于继承用户类的时间。一个版本表现得像一个简单的类,另一个版本表现得像一个django model。

如果继承基本的"对象"版本,那么Employee类将只是一个标准类,而first_name不会成为数据库表的一部分。您不能创建表单,也不能在其中使用任何其他Django功能。

如果继承models.model版本,则Employee类将具有django模型的所有方法,并且它将继承First_Name字段作为可以在表单中使用的数据库字段。

根据文档,抽象模型"提供了一种在Python级别分解公共信息的方法,同时在数据库级别为每个子模型只创建一个数据库表。"