gpt4 book ai didi

python - 使用外键定义一个抽象模型到另一个抽象模型

转载 作者:行者123 更新时间:2023-11-30 22:34:03 24 4
gpt4 key购买 nike

我正在尝试构建两个名为 SurveyQuestionBaseSurveyResponseBase 的抽象类,它们将用作模板来快速定义新的具体模型,以便在我们的网站上实现特定调查。我遇到的问题是强制要求 SurveyResponseBase 模型具体化后,应将 ForeignKey 定义为 SurveyQuestionBase 的具体模型。

Django 不允许我们将 ForeignKeys 定义为抽象类,因此我无法执行以下操作:问题 = models.ForeignKey(SurveyQuestionBase)出于类似的原因,我也不能将其设置为 Noneapp_label.ModelName

一个巧妙的修复方法是创建一个新的具体模型 SurveyQuestionConcrete 并使 ForeignKey 指向此:question = models.ForeignKey(concrete_model),结合验证来确保该模型被替换。

是否有更简洁的方法来实现同样的目标? 我需要做的就是确保当有人从 SurveyResponseBase 定义具体模型时,他们将 ForeignKey 包含到从 SurveyQuestionBase 定义的具体模型>

完整代码如下:

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

最佳答案

此问题的两个解决方案(均有效):

第一个解决方案涉及使用GenericForeignKey。第二个更有趣,涉及动态生成 SurveyResponseBase

Solution 1: Using 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

Solution 2: Dynamic base class generation

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)

我们决定继续采用解决方案 2,因为 GenericForeignKeys 方法需要额外的 ContentType 选择。

关于python - 使用外键定义一个抽象模型到另一个抽象模型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44965172/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com