从 ABC 和 django.db.models.Model 继承都会引发元类异常

Mik*_*eyE 8 abc django-models python-3.x

我正在尝试使用 Python 3 实现一个 Django 数据模型类,它也是一个接口类。我这样做的原因是,我正在为我的同事编写一个基类,并且需要他在所有这些中实现三个方法他从我的派生的类。我试图给他一种简化的方式来使用我设计的系统的功能。但是,他必须重写一些方法来为系统提供足够的信息来执行他继承的类中的代码。

我知道这是错误的,因为它会抛出异常,但我想要一个类似以下示例的类:

from django.db import models
from abc import ABC, abstractmethod

class AlgorithmTemplate(ABC, models.Model):
    name = models.CharField(max_length=32)

    @abstractmethod
    def data_subscriptions(self):
        """
        This method returns a list of topics this class will subscribe to using websockets

        NOTE: This method MUST be overriden!
        
        :rtype: list
        """
Run Code Online (Sandbox Code Playgroud)

我知道我可以避免继承 ABC类,但我想使用它是因为我不会在这里让你感到厌烦。

问题


在将一个类(如上面的类)包含到我的项目中并运行后,python manage.py makemigrations我收到错误:TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases. 我搜索了 Stack Overflow,但只找到了如下解决方案:

class M_A(type): pass
class M_B(type): pass
class A(metaclass=M_A): pass
class B(metaclass=M_B): pass

class M_C(M_A, M_B): pass
class C:(A, B, metaclass=M_C): pass
Run Code Online (Sandbox Code Playgroud)

我已阅读以下帖子:

使用 ABC、PolymorphicModel、django-models 产生元类冲突

解决元类冲突

我已经尝试了这些解决方案的许多变体,但我仍然遇到了可怕的metaclass异常。帮助我欧比旺克诺比,你是我唯一的希望。:-)

ste*_*308 14

我有同样的需求并发现了这个。为了清晰和完整,我修改了代码。基本上,您需要一个可用于所有模型接口的额外类。

import abc

from django.db import models


class AbstractModelMeta(abc.ABCMeta, type(models.Model)):
    pass


class AbstractModel(models.Model, metaclass=AbstractModelMeta):    
    # You may have common fields here.

    class Meta:
        abstract = True

    @abc.abstractmethod
    def must_implement(self):
        pass


class MyModel(AbstractModel):
    code = models.CharField("code", max_length=10, unique=True)

    class Meta:
        app_label = 'my_app'


test = MyModel(code='test')
> TypeError: Can't instantiate abstract class MyModel with abstract methods must_implement
Run Code Online (Sandbox Code Playgroud)

现在您拥有了两全其美的优势。


Mik*_*eyE 6

我找到了一个对我有用的解决方案,所以我想我会把它张贴在这里,以防它对其他人有帮助。我决定不从ABC类继承,而是在“抽象”方法(派生类必须实现的方法)中引发异常。我确实在 Django 文档中找到了有用的信息,描述了使用 Django 数据模型作为抽象基类以及多表继承。

Django 数据模型作为抽象基类


引用自文档

当您想将一些公共信息放入许多其他模型时,抽象基类很有用。您编写基类并将 abstract=True 放在 Meta 类中。此模型将不会用于创建任何数据库表。相反,当它用作其他模型的基类时,其字段将添加到子类的字段中。

一个例子:

from django.db import models

class CommonInfo(models.Model):
    name = models.CharField(max_length=100)
    age = models.PositiveIntegerField()

    class Meta:
        abstract = True

class Student(CommonInfo):
    home_group = models.CharField(max_length=5)
Run Code Online (Sandbox Code Playgroud)

Student 模型将包含三个字段:name、age 和 home_group。CommonInfo 模型不能用作普通的 Django 模型,因为它是一个抽象基类。它不生成数据库表,也没有管理器,不能直接实例化或保存。

从抽象基类继承的字段可以用另一个字段或值覆盖,或者用 None 删除。


带有 Django 数据模型的多表继承


我对“多表继承”的理解是,您可以定义一个数据模型,然后将其用作第二个数据模型的基类。第二个数据模型将继承第一个模型的所有字段,以及它自己的字段。

引用自文档

Django 支持的第二种模型继承是层次结构中的每个模型都是一个模型。每个模型对应自己的数据库表,可以单独查询和创建。继承关系引入了子模型与其每个父模型之间的链接(通过自动创建的 OneToOneField)。例如:

from django.db import models

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

class Restaurant(Place):
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)
Run Code Online (Sandbox Code Playgroud)

Place 的所有字段也将在 Restaurant 中可用,但数据将驻留在不同的数据库表中。所以这两个都是可能的:

>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")
Run Code Online (Sandbox Code Playgroud)