如何在Django中使用基类

时间:2016-09-26 19:59:07

标签: python django django-models

我试图改变我创建的应用程序,以便它可以重复使用。它基于单个模型,使用该应用程序的网站将进行子类化。就目前而言,我的不可重用版本具有以下类型的结构:

# models.py
class Document(models.Model):
     contents = models.TextField()
     date = models.DateTimeField()

# views.py
from .models import SiteModel
# ...
class MyView(ListView):
    def some_method(self, list_of_pks):
        model_vals = Document.objects.filter(pk__in = list_of_pks).values()

def perform_action(request):
    obj_pk = request.POST.get('obj_pk')
    obj = Document.objects.filter(pk = obj_pk)

    MySignal.send(sender=Document, instance = obj)

#etc, etc

这很好用。但是我的用例要求使用不同类型的Document,每个站点一个,这将具有事先不知道的其他字段。基于阅读abstract base classes上的文档,我认为合理的解决方案看起来像:

# models.py for the app 
class BaseDocument(models.Model):
    contents = models.TextField()

    class Meta:
        abstract = True

# models.py for a hypothetical site using the app
class SiteDocument(myapp.BaseDocument):
    date = models.DateTimeField()
    # other site-specific fields

我不明白的是如何在应用的views.pyforms.py等中引用该模型。我知道BaseDocument.objects.all(),例如,因为它没有连接到数据库,所以不会返回任何内容。相反,我无法Document.objects.all()因为Document尚未创建并且特定于每个网站。抽象基类不是正确的解决方案,如果是,那是什么?

修改

看起来使用OneToOneField可能最适合我的用例,虽然它看起来像是阻止了从超类中继承方法,并且BaseDocument.objects.all()不会列出其所有子项

或者,我想知道我是否可以在get_user_model()的样式中向我的抽象基类添加get_document_model()方法?

2 个答案:

答案 0 :(得分:0)

您不能直接查询您的抽象类,因为它们不具有管理器,只有继承的类。如果你真的必须继承,你可以使用一个具体的基础模型,并以每次查询的连接为代价从中继承。

长期而艰难地考虑这是否真的有必要,或者您是否能够以更通用的方式表示您的数据。模型使继承看起来很容易,但它们并不神奇。有非常实际的性能和复杂性考虑因素。

可能就像在模型中添加type字段一样简单

class Document(models.Model):
    DOCUMENT_TYPES = ['site', 'another', 'whatever']

    document_type = models.CharField(choices=DOCUMENT_TYPES)
    ...

有关抽象vs具体类和查询的更多信息,请访问How to query abstract-class-based objects in Django?

答案 1 :(得分:0)

我最终选择了我的编辑中提到的解决方案,即创建一个受get_user_model()启发的get_document_model()方法。这给了我完全理想的行为。

# models.py in app1
from django.db import models
from django.apps import apps as django_apps

class BaseDocument(models.Model):
    contents = models.TextField()

    class Meta:
        abstract = True

    def get_document_model():
        # exception handling removed for concision's sake
        return django_apps.get_model(settings.DOCUMENT_MODEL)

# models.py in app2
from django.db import models
from app1.models import BaseDocument

class SiteDocument(BaseDocument):
    date = models.DateTimeField()

在整个views.py和其他地方,我将Document.objects.all()形式的内容更改为BaseDocument().get_document_model().objects.all()