2017-07-07 22 views
0

SurveyQuestionBaseSurveyResponseBaseという2つの抽象クラスを構築しようとしています。これらのクラスは、Webサイトで特定の調査を実装するための新しいコンクリートモデルを素早く定義するためのテンプレートです。私が抱えている問題は、SurveyResponseBaseモデルが具体的に作られたときにForeignKeyという具象モデルをSurveyQuestionBaseと定義しなければならないということです。 question = models.ForeignKey(SurveyQuestionBase) をどちらも私は同様の理由Noneまたはapp_label.ModelNameとしてそれを持つことができます。ForeignKeyを持つ抽象モデルを別の抽象モデルに定義する

Djangoは、私は、例えば、これを行うことはできませんので、私たちは抽象クラスにForeignKeysを定義することができません。

新しいコンクリートモデルを作成してForeignKeyにこれを指すようにしてください:question = models.ForeignKey(concrete_model)と、このモデルが確実に置き換えられることを確認してください。

同じことを達成するためのクリーンな方法はありますか? 私は誰かがSurveyResponseBaseから、具体的なモデルを定義したとき、彼らはここでSurveyQuestionBase

から定義された具体的なモデルへのForeignKeyは、完全なコードだ含まれていることを確認するだけです:

from __future__ import unicode_literals 


from django.contrib.contenttypes.fields import GenericForeignKey 
from django.contrib.contenttypes.models import ContentType 
from django.db import models 

# Implementation borrows from: https://github.com/jessykate/django-survey/ 


class SurveyQuestionBase(models.Model): 
    TEXT = 'text' 
    INTEGER = 'integer' 
    RADIO = 'radio' 
    SELECT = 'select' 
    MULTI_SELECT = 'multi-select' 

    ANSWER_TYPE_CHOICES = (
     (INTEGER, 'Integer',), 
     (TEXT, 'Text',), 
     (RADIO, 'Radio',), 
     (SELECT, 'Select',), 
     (MULTI_SELECT, 'Multi-Select',), 
    ) 

    question = models.TextField() 
    required = models.BooleanField() 
    question_type = models.CharField(choices=ANSWER_TYPE_CHOICES, max_length=20) 

    class Meta: 
     abstract = True 


class SurveyResponseBase(models.Model): 
    """ 
    concrete_question_model: 'app_label.Model' - Define the concrete model this question belongs to 
    """ 
    concrete_model = 'SurveyQuestionBase' 

    question = models.ForeignKey(concrete_model) 
    response = models.TextField() 

    class Meta: 
     abstract = True 

答えて

1

2つの溶液(両方の作業):

最初のソリューションはGenericForeignKeyを使用することを含みます。 2番目はより面白く、SurveyResponseBaseを動的に生成することです。

解決方法1:GenericForeignKey

class SurveyQuestionBase(models.Model): 
    TEXT = 'text' 
    INTEGER = 'integer' 
    RADIO = 'radio' 
    SELECT = 'select' 
    MULTI_SELECT = 'multi-select' 

    ANSWER_TYPE_CHOICES = (
     (INTEGER, 'Integer',), 
     (TEXT, 'Text',), 
     (RADIO, 'Radio',), 
     (SELECT, 'Select',), 
     (MULTI_SELECT, 'Multi-Select',), 
    ) 

    question = models.TextField() 
    required = models.BooleanField() 
    question_type = models.CharField(choices=ANSWER_TYPE_CHOICES, max_length=20) 

    class Meta: 
     abstract = True 

    @classmethod 
    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 


class SurveyResponseBase(models.Model): 
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, limit_choices_to=get_content_choices) 
    object_id = models.PositiveIntegerField() 
    content_object = GenericForeignKey('content_type', 'object_id') 
    response = models.TextField() 

    class Meta: 
     abstract = True 

def get_content_choices(): 
    query_filter = None 

    for cls in SurveyQuestionBase.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 

ソリューション2使用:動的ベースクラス生成

class SurveyQuestionBase(models.Model): 
    TEXT = 'text' 
    INTEGER = 'integer' 
    RADIO = 'radio' 
    RATING = 'rating' 
    SELECT = 'select' 
    MULTI_SELECT = 'multi-select' 

    QUESTION_TYPES = (
     (INTEGER, 'Integer'), 
     (TEXT, 'Text'), 
     (RADIO, 'Radio'), 
     (RATING, 'Rating'), 
     (SELECT, 'Select'), 
     (MULTI_SELECT, 'Multi-Select'), 
    ) 

    CHOICE_TYPES = (RADIO, RATING, SELECT, MULTI_SELECT) 

    question = models.TextField() 
    required = models.BooleanField() 
    question_type = models.CharField(choices=QUESTION_TYPES, max_length=20) 
    choices = models.TextField(blank=True, null=True) 

    choices.help_text = """ 
    If the question type is "Radio," "Select," or "Multi-Select", 
    provide a comma-separated list of options for this question 
    """ 

    class Meta: 
     abstract = True 


Meta = type('Meta', (object,), {'abstract': True}) 


def get_response_base_class(concrete_question_model): 
    """ 
    Builder method that returns the SurveyResponseBase base class 
    Args: 
     concrete_question_model: Concrete Model for SurveyQuestionBase 

    Returns: SurveyResponseBase Class 
    """ 
    try: 
     assert SurveyQuestionBase in concrete_question_model.__bases__ 
    except AssertionError: 
     raise ValidationError('{} is not a subclass of SurveyQuestionBase'.format(concrete_question_model)) 

    attrs = { 
     'question': models.ForeignKey(concrete_question_model, related_name='responses'), 
     'response': models.TextField(), 
     '__module__': 'survey_builder.models', 
     'Meta': Meta(), 
    } 
    return type('SurveyResponseBase', (models.Model,), attrs) 

をGenericForeignKeysが接近するため、我々は解決策2で先に行くことにしました追加のContentType選択が必要です。

関連する問題