oreignKey对从特定抽象模型继承的ContentType的

oreignKey对从特定抽象模型继承的ContentType的

本文介绍了Django GenericForeignKey对从特定抽象模型继承的ContentType的限制的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的应用程序模型中,我需要一种链接问题解决方案的方法-每问题可以有多个解决方案,给定的解决方案可以映射回多个问题

In my application's models I need a way of linking Problems and Solutions -- every Problem can have multiple Solutions and a given Solution can map back to multiple Problems.

解决方案是抽象的基类,因为 Solutions 。因此,我发现我需要一个映射表 ProblemSolutionMapping ,该表使用 GenericForeignKey 来容纳所有这些子类。但是,我正在尝试找出如何将类限制为仅 Solutions 的子类,而不是整个应用程序中可用的所有类,这正在发生。

Solution is an abstract base class, since there can be many varieties of Solutions. So, I figured out I need a mapping table ProblemSolutionMapping which uses a GenericForeignKey to accommodate all those child classes. But I'm trying to figure out how to limit the classes to just the children of Solutions and not all the classes available in the whole application, which is what is currently happening.

# Thanks to http://stackoverflow.com/a/23555691/1149759
class Solution(models.Model):
    ...
    @classmethod
    def get_subclasses(cls):
        content_types = ContentType.objects.filter(app_label=cls._meta.app_label)
        models = [ct.model_class() for ct in content_types]
        return [model for model in models
                if (model is not None and
                    issubclass(model, cls) and
                    model is not cls)]

    class Meta:
        abstract = True


class ProblemSolutionMapping(models.Model):
    problem = models.ForeignKey(Problem)
    content_type = models.ForeignKey(ContentType,
        limit_choices_to=Solution.get_subclasses()) # <==== This is the issue
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

The issu e是当我启动Django应用程序时,对 ContentType.objects.filter(app_label = cls._meta.app_label)的调用会引发错误:

The issue is that when I start up my Django app, the call to ContentType.objects.filter(app_label=cls._meta.app_label) throws the error:

django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.

不确定该怎么做-我尝试将映射表制作为相关模型文件中的最后一个(所有子类均在同一文件中的上方定义),但没有区别。 这是我必须进入管理表单的内容吗?还是在模型级别还有其他方法可以做到这一点?

Not sure what to do -- I tried making the mapping table the last one in the relevant models file (all the child classes are defined above it in the same file), but it made no difference. Is this something that I have to move into the admin form? Or is there some other way to do this at the model level?

(Django 1.9,以防万一。)

(Django 1.9, in case it matters.)

预先感谢您的帮助!

推荐答案

所以我来到这里寻找答案。根据Mehran的帖子,我开发了以下与您相似的方法。相反, limit_choice_to 调用一个返回运行时创建的Q对象的方法。

So I arrived here looking for the answer. Based on Mehran's post, I developed the below approach which is similar to yours. Instead the limit_choice_to calls a method that returns a runtime created Q object.

下面是与您的get_subclasses。

Below is the part that is similar to your get_subclasses.

def get_subclasses(cls, *args, **kwargs):
    for app_config in apps.get_app_configs():
        for app_model in app_config.get_models():
            model_classes = [c.__name__ for c in inspect.getmro(app_model)]
            if cls.__name__ in model_classes:
                yield app_model

这会为我们创建Q过滤器(在我的实现中,这只是一个普通的旧方法,未附加到任何类,但我想可能是):

This creates the Q filter(s) for us (in my implementation, this is just a plain old method not attached to any class, but I suppose it could be):

def get_content_choices():
    query_filter = None

    for cls in Solution.get_subclasses():

        app_label, model = cls._meta.label_lower.split('.')
        current_filter = models.Q(app_label=app_label, model=model)

        if query_filter is None:
            query_filter = current_filter
        else:
            query_filter |= current_filter

    return query_filter

最后,在我们的模型中:

And finally, in our model:

class ProblemSolutionMapping(models.Model):
    ...
    content_type = models.ForeignKey(ContentType, limit_choices_to=get_content_choices())
    ...

这篇关于Django GenericForeignKey对从特定抽象模型继承的ContentType的限制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-30 23:55