【问题标题】:Django GenericForeignKey limit to ContentTypes that inherit from a particular abstract modelDjango GenericForeignKey 限制从特定抽象模型继承的 ContentType
【发布时间】:2016-01-05 05:28:03
【问题描述】:

在我的应用程序模型中,我需要一种链接ProblemsSolutions 的方法——每个Problem 可以有多个Solutions,并且给定的Solution 可以映射回多个Problems

Solution 是一个抽象基类,因为Solutions 可以有很多变体。所以,我发现我需要一个映射表ProblemSolutionMapping,它使用GenericForeignKey 来容纳所有这些子类。但我试图弄清楚如何将类限制为Solutions 的子级,而不是整个应用程序中可用的所有类,这是目前正在发生的事情。

# 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')

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

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

不知道该怎么做——我尝试将映射表设置为相关模型文件中的最后一个(所有子类都在同一个文件中定义在其上方),但没有任何区别。 这是我必须进入管理表单的东西吗?还是在模型级别有​​其他方法可以做到这一点?

(Django 1.9,以防万一。)

提前感谢您的帮助!

【问题讨论】:

    标签: django django-models


    【解决方案1】:

    django 1.7 不再支持在导入时引用模型。您应该在加载所有应用程序后使用您的模型。因此,您应该将列表静态传递给您的 limit_choices_to使用 Q 对象,如下所示:

    limit_choices_to=models.Q(app_label = 'app', model = 'a') | models.Q(app_label = 'app', model = 'b')
    

    Also you can limit what shows to user in form level

    【讨论】:

    • 问题是我不能静态地将列表传递给limit_choice_to bc Solution 的子类的处理方式。不过,谢谢。
    【解决方案2】:

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

    下面是和你的 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 过滤器(在我的实现中,这只是一个普通的旧方法,没有附加到任何类,但我想它可能是):

    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
    

    最后,在我们的模型中:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-12-22
      • 2021-01-23
      • 2018-03-07
      • 2013-10-21
      • 2013-05-15
      • 2023-03-05
      • 1970-01-01
      相关资源
      最近更新 更多