【问题标题】:django abstract models versus regular inheritancedjango 抽象模型与常规继承
【发布时间】:2013-05-15 08:10:03
【问题描述】:

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

【问题讨论】:

标签: python django django-models


【解决方案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(...)

【讨论】:

    【解决方案2】:

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

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

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

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

    【讨论】:

      【解决方案3】:

      主要区别在于模型的数据库表是如何创建的。 如果你在没有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 将创建一个包含titledate 列的可发布表,并为BlogEntryImage 创建单独的表。此解决方案的优势在于,您可以跨所有可发布内容查询基本模型中定义的字段,无论它们是博客条目还是图像。但是因此,如果您例如,Django 将不得不进行连接。查询图片... 如果创建Publishable abstract = True Django 将不会为Publishable 创建表,而只会为包含所有字段(以及继承的字段)的博客条目和图像创建表。这会很方便,因为 get 等操作不需要连接。

      另见Django's documentation on model inheritance

      【讨论】:

      • 这是涵盖 abstract = True 和 abstract = False 的最佳答案
      【解决方案4】:

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

      与 python 类不同,field name hiding is not permited 具有模型继承。

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

      我有一个模型继承自 django 的身份验证 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'
      

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

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

      【讨论】:

      • 我有一个与这里的主要问题无关的问题,但你是如何在最后实现这个(覆盖 groups 字段的 related_name)的?
      • @kalo IIRC 我只是将名称完全更改为其他名称。无法删除或替换继承的字段。
      • 是的,当我找到一种使用contribute_to_class 方法的方法时,我几乎准备放弃它。
      【解决方案5】:

      抽象模型为每个子子项创建一个包含整组列的表,而使用“普通”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) 也相当于继承了一个模型类,对吧?这会导致两个单独的表,不是吗?
      • @MohammadRafayAleem:是的,但是当使用继承时,Django 会在 car 上创建一个属性,例如,num_wheels,而对于 OneToOneField,你必须自己取消引用.
      • 如何通过常规继承强制为app_car 表重新生成所有字段,以便它也具有num_wheels 字段,而不是具有vehicle_id 指针?
      • @DorinGrecu:让基类成为抽象模型,然后继承它。
      • @mipadi 问题是我不能将基类抽象化,它正在整个项目中使用。
      【解决方案6】:

      我实际上想知道模型类之间的区别 继承自 django 抽象类 (Meta: abstract = True) 和 简单的 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)
      );
      

      【讨论】:

      • 谢谢,这回答了我的问题。目前尚不清楚为什么没有为 Employee 创建 first_name。
      • @rpq 你必须检查 Django 的源代码,但是 IIRC 它在models.Model 中使用了一些元类黑客,所以在超类中定义的字段不会被拾取(除非它们也是models.Model 的子类)。
      • @rpq 我知道这是 7 年前你问的,但是在 ModelBase__new__ 调用中,它对类的属性进行初始检查,检查属性值是否具有contribute_to_class 属性 - 您在 Django 中定义的 Fields 都可以,因此它们包含在名为 contributable_attrs 的特殊字典中,最终在迁移期间传递以生成 DDL。
      • 每次看 DJANGO 都想哭,为什么??????为什么所有的废话,为什么框架必须以与语言完全不同的方式运行?那么最小惊讶原则呢?这种行为(就像 django 中的几乎所有其他东西一样)不是任何了解 python 的人所期望的。
      猜你喜欢
      • 2013-10-21
      • 2013-12-22
      • 1970-01-01
      • 1970-01-01
      • 2017-04-09
      • 2018-03-07
      • 1970-01-01
      • 2023-03-05
      • 1970-01-01
      相关资源
      最近更新 更多