django抽象模型与常规继承

rpq*_*rpq 75 python django django-models

除了语法之外,使用django抽象模型和使用普通Python继承与django模型之间的区别是什么?优点和缺点?

更新:我认为我的问题被误解了,我收到了关于抽象模型和继承自django.db.models.Model的类之间差异的回复. 我实际上想知道继承自django抽象类(Meta:abstract = True)的模型类与继承自say,'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(...
Run Code Online (Sandbox Code Playgroud)

Aya*_*Aya 130

我实际上想知道继承自django抽象类(Meta:abstract = True)的模型类与继承自say,'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)
Run Code Online (Sandbox Code Playgroud)

...将导致生成一个表,沿着......的行...

CREATE TABLE myapp_employee
(
    id         INT          NOT NULL AUTO_INCREMENT,
    first_name VARCHAR(255) NOT NULL,
    title      VARCHAR(255) NOT NULL,
    PRIMARY KEY (id)
);
Run Code Online (Sandbox Code Playgroud)

......而后者......

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)
Run Code Online (Sandbox Code Playgroud)

...不会导致生成任何表.

您可以使用多重继承来执行此类操作...

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)
Run Code Online (Sandbox Code Playgroud)

...会创建一个表,但它会忽略User类中定义的字段,所以你最终会得到一个像这样的表...

CREATE TABLE myapp_employee
(
    id         INT          NOT NULL AUTO_INCREMENT,
    title      VARCHAR(255) NOT NULL,
    PRIMARY KEY (id)
);
Run Code Online (Sandbox Code Playgroud)

  • @rpq你必须检查Django的源代码,但是IIRC它在`models.Model`中使用了一些元类hackery,所以超类中定义的字段不会被拾取(除非它们也是`models.Model的子类`). (4认同)
  • 谢谢,这回答了我的问题.目前还不清楚为什么没有为Employee创建first_name. (3认同)
  • 每次看到DJANGO我就想哭,为什么???为什么所有这些都是废话,为什么框架必须以与语言完全不同的方式运行?那么最小惊讶原则呢?这种行为(就像 django 中的几乎所有其他行为一样)不是任何理解 python 的人所期望的。 (2认同)

mip*_*adi 32

抽象模型为每个子项创建一个包含整个列集的表,而使用"普通"Python继承创建一组链接表(也称为"多表继承").考虑一下你有两个模型的情况:

class Vehicle(models.Model):
  num_wheels = models.PositiveIntegerField()


class Car(Vehicle):
  make = models.CharField(…)
  year = models.PositiveIntegerField()
Run Code Online (Sandbox Code Playgroud)

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

app_car:
| id | num_wheels | make | year
Run Code Online (Sandbox Code Playgroud)

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

app_vehicle:
| id | num_wheels

app_car:
| id | vehicle_id | make | model
Run Code Online (Sandbox Code Playgroud)

到那vehicle_id一行的链接在哪里app_vehicle也会有汽车的轮子数量.

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


更新

为了解决您更新的问题,继承Django抽象类和继承Python的区别在于object前者被视为数据库对象(因此它的表被同步到数据库)并且它具有a的行为Model.继承普通的Python object赋予类(及其子类)这些特性.


Ber*_*ant 8

主要区别在于如何创建模型的数据库表.如果在没有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(...)
Run Code Online (Sandbox Code Playgroud)

如果Publishable该类不是抽象的Django会创建一个表与列publishables titledate和单独的表BlogEntryImage.此解决方案的优势在于,无论是博客条目还是图像,您都可以在所有可发布项中查询基本模型中定义的字段.但是因此,如果您对图像进行查询,Django将不得不进行连接...如果让Publishable abstract = TrueDjango不会为Publishable包含所有字段(也包括继承的字段)的博客条目和图像创建表格.这样很方便,因为get等操作不需要连接.

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


Adr*_*ián 8

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

与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

    # ...
Run Code Online (Sandbox Code Playgroud)

然后我得到了我的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.'))
Run Code Online (Sandbox Code Playgroud)

我使用这2个mixins如下:

class Member(PermissionMixin, WithManagedGroupMixin):
    pass
Run Code Online (Sandbox Code Playgroud)

所以,是的,我希望这可行,但事实并非如此.但问题更严重,因为我得到的错误根本没有指向模型,我不知道出了什么问题.

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

django.core.exceptions.FieldError: Local field 'groups' in class 'Member' clashes with field of similar name from base class 'PermissionMixin'
Run Code Online (Sandbox Code Playgroud)

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

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