2016-01-05 37 views
1

在我的應用程序的模型,我需要連接​​和Solutions的方式繼承CONTENTTYPES - 每Problem可以有多個Solutions和給定Solution可以映射回多​​。Django的GenericForeignKey限制,從一個特定的抽象模型

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,如果它很重要。)

預先感謝您的幫助!

回答

1

django 1.7不再支持在導入時重新導入模型。加載所有應用程序後,應該使用模型。所以,你應該要麼Staticly傳遞一個列表,你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

+0

問題是,我不能靜態地將一個列表傳遞給'solution'子類的'limit_choice_to' bc。謝謝,不過。 – TAH

1

所以我來到這裏尋找答案。根據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過濾器(S)(在我的實現,這僅僅是不依附於任何類別的普通老式的方法,但我想這可能是):

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()) 
    ... 
相關問題