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

时间:2018-04-29 10:36:55

标签: python-3.x django-models abc

我正在尝试使用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
        """

我知道我可以避免继承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

我已阅读以下帖子:

Using ABC, PolymorphicModel, django-models gives metaclass conflict

python 3: TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

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

2 个答案:

答案 0 :(得分:3)

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

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

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

答案 1 :(得分:1)

我找到了一个适合我的解决方案,所以我想在这里发布它以防万一。我决定不继承ABC类,而只是在" abstract"中引发异常。方法(派生类必须实现的方法)。我确实在Django文档中找到了有用的信息,描述了将Django数据模型用作抽象基类以及多表继承。

Django数据模型作为抽象基类

引自the docs

  

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

     

一个例子:

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)
     

学生模型将有三个字段:姓名,年龄和home_group。   CommonInfo模型不能用作普通的Django模型,因为它   是一个抽象的基类。它不会生成数据库表或   有经理,无法直接实例化或保存。

     

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


使用Django数据模型的多表继承

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

引自the docs

  

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)
     

Place的所有田地也将在餐厅提供,   虽然数据将驻留在不同的数据库表中。所以这些   都是可能的:

>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")