我正在尝试直接自己编写数据库模式的背景来学习Django。我想了解如何有效地使用数据库抽象工具进行规范化。
作为一个人为的例子,假设我有一个可以就3个主题提问的对话,每个问题都很复杂,足以保证自己的课程。
Class Conversation(models.Model):
partner = models.CharField()
Class Weather_q(models.Model):
#stuff
Class Health_q(models.Model):
#stuff
Class Family_q(models.Model):
#stuff
所以我想说我想要进行2次对话:
通常,我会为此编写一个规范化表:
INSERT INTO Conversation (partner) values ("Bob", "Alice"); --primary keys = 1 and 2
INSERT INTO NormalizationTable (fk_Conversation, fk_Weather_q, fk_Health_q, fk_Family_q) VALUES
(1,1,0,0), -- Bob weather#1
(1,2,0,0), -- Bob weather#2
(1,0,1,0), -- Bob health#1
(2,1,0,0), -- Alice weather#1
(2,0,0,1); -- Alice family#1
我是否需要显式创建此规范化表,还是不鼓励这样做?
Class NormalizationTable(models.Model):
fk_Conversation = models.ForeignKey(Conversation)
fk_Weather_q = models.ForeignKey(Weather)
fk_Health_q = models.ForeignKey(Health)
fk_Family_q = models.ForeignKey(Family)
然后我想执行对话。我写了一个这样的视图(跳过异常捕获和逻辑来迭代每个对话的多个问题):
from myapp.models import Conversation, Weather_q, Health_q, Family_q
def converse(request):
#get this conversation's pk
#assuming "mypartner" is provided by the URL dispatcher
conversation = Conversation.objects.filter(partner=mypartner)[0]
#get the relevant row of the NormalizationTable
questions = NormalizationTable.objects.filter(fk_Conversation=conversation)[0]
for question in questions:
if question.fk_Weather_q:
return render("weather.html", Weather_q.objects.filter(pk=fk_Weather_q)[0])
if question.fk_Health_q:
return render("health.html", Health_q.objects.filter(pk=fk_Health_q)[0])
if question.fk_Family_q:
return render("family.html", Family_q.objects.filter(pk=fk_Family_q)[0])
从整体上考虑,这是解决这种规范化问题的“Django”方法(与容器对象相关的N个对象)吗?我可以更好地使用Django内置的ORM或其他工具吗?
答案 0 :(得分:2)
暂且退出"规范化表格" (这个词对我来说并不熟悉),这就是我认为的一个" djangish"解决问题的方法。请注意,我附上了你的陈述"每个问题都很复杂,足以保证自己的等级#34;。对我来说,这意味着每种类型的问题都需要自己独特的领域和方法。否则,我会创建一个Question
模型,通过Category
连接到ForeignKey
模型。
class Partner(models.Model):
name = models.CharField()
class Question(models.Model):
# Fields and methods common to all kinds of questions
partner = models.ForeignKey(Partner)
label = models.CharField() # example field
class WeatherQuestion(Question):
# Fields and methods for weather questions only
class HealthQuestion(Question):
# Fields and methods for health questions only
class FamilyQuestion(Question):
# Fields and methods for family questions only
通过这种方式,您可以获得所有问题共有的所有字段和方法的基本Question
模型,以及用于描述不同类型问题的一系列子模型。基本模型与其子模型之间存在隐含关系,由Django维护。这使您能够创建具有不同问题的单个查询集,无论其类型如何。默认情况下,此查询集中的项目为Question
类型,但可以通过访问特殊属性(例如healthquestion
的{{1}}属性)将其转换为特定问题类型。这在"Multi-table model inheritance" section of Django documentation。
然后在视图中,您可以获得(不同类型)问题的列表,然后检测其特定类型:
HealtQuestion
检测问题类型的代码非常难看和复杂。您可以使用InheritanceManager包中的django-model-utils使其更简单,更通用。您需要安装包并将行添加到from myapp.models import Question
def converse(request, partner_id):
question = Question.objects.filter(partner=partner_id).first()
# Detect question type
question_type = "other"
question_obj = question
# in real life the list of types below would probably live in the settings
for current_type in ['weather', 'health', 'family']:
if hasattr(question, current_type + 'question'):
question_type = current_type
question_obj = getattr(question, current_type + 'question')
break
return render(
"questions/{}.html".format(question_type),
{'question': question_obj}
)
模型:
Question
然后视图看起来像这样:
objects = InheritanceManager()
两个视图只选择一个问题 - 第一个问题。这就是你的例子中的视图如何表现,所以我选择了它。您可以轻松转换这些示例以返回问题列表(不同类型)。
答案 1 :(得分:1)
我不熟悉规范化表这个术语,但我看到你正在尝试做什么。
在我看来,您所描述的并不是一种非常令人满意的数据库建模方法。最简单的方法是将所有问题都放在同一个表中,使用"类型"字段,可能还有一些其他可选字段,这些字段在不同类型之间在这种情况下,这在Django中变得非常简单。
但是,好吧,你说'#34;让我们说......每个问题都很复杂,足以保证自己的课程。" Django确实有一个解决方案,即generic relations。它看起来像这样:
class ConversationQuestion(models.Model):
conversation = models.ForeignKey(Conversation)
content_type = models.ForeignKey(ContentType)
question_id = models.PositiveIntegerField()
question = GenericForeignKey('content_type', 'question_id')
# you can use prefetch_related("question") for efficiency
cqs = ConversationQuestion.objects.filter(conversation=conversation)
for cq in cqs:
# do something with the question
# you can look at the content_type if, as above, you need to choose
# a separate template for each type.
print(cq.question)
因为它是Django的一部分,所以在管理员,表单等方面获得了一些(但不是全部)支持。
或者你可以做你上面所做的事情,但是,正如你所注意到的那样,它很丑陋并且似乎没有捕捉到使用ORM的优势。