Python 理解Django'中的规范化表;s ORM
我试图从自己直接编写数据库模式的背景中学习Django。我想了解我应该如何有效地使用数据库抽象工具进行规范化 作为一个人为的例子,假设我有一个对话,可以问3个主题的问题,每个问题都很复杂,足以保证自己的课程Python 理解Django'中的规范化表;s ORM,python,django,orm,normalization,Python,Django,Orm,Normalization,我试图从自己直接编写数据库模式的背景中学习Django。我想了解我应该如何有效地使用数据库抽象工具进行规范化 作为一个人为的例子,假设我有一个对话,可以问3个主题的问题,每个问题都很复杂,足以保证自己的课程 Class Conversation(models.Model): partner = models.CharField() Class Weather_q(models.Model): #stuff Class Health_q(models.Model): #stuff Cl
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
假设我想进行两次对话:
- 对话1:问两个不同的天气问题和一个关于他的健康的问题
- 与爱丽丝对话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)
然后我想执行对话。我写了这样一个视图(跳过异常捕获和逻辑,在每次对话中迭代多个问题):
从整体上看,这是解决此类规范化问题的“Django”方法吗(与容器对象关联的N个对象)?我能更好地利用Django的内置ORM或其他工具吗?我不熟悉术语规范化表,但我知道您在尝试做什么 在我看来,您所描述的并不是一种非常令人满意的数据库建模方法。最简单的方法是使所有问题都成为同一个表的一部分,带有一个“类型”字段,可能还有一些其他可选字段,这些字段在不同类型之间有所不同。在这种情况下,这在Django变得非常简单 但是,好吧,你说“让我们假设……每个问题都足够复杂,足以证明自己的类别。”Django确实有一个解决方案,这就是。它看起来像这样:
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)
from myapp.models import Question
def converse(request, partner_id):
question = Question.objects.filter(partner=partner_id).select_subclasses().first()
question_type = question._meta.object_name.lower()
return render(
"questions/{}.html".format(question_type),
{'question': question}
)
因为它是Django的一部分,所以您可以在管理、表单等方面获得一些(但不是全部)支持
或者你也可以像上面那样做,但是,正如你所注意到的,这很难看,似乎没有抓住使用ORM的优势。我不熟悉术语规范化表,但我知道你在尝试做什么 在我看来,您所描述的并不是一种非常令人满意的数据库建模方法。最简单的方法是使所有问题都成为同一个表的一部分,带有一个“类型”字段,可能还有一些其他可选字段,这些字段在不同类型之间有所不同。在这种情况下,这在Django变得非常简单 但是,好吧,你说“让我们假设……每个问题都足够复杂,足以证明自己的类别。”Django确实有一个解决方案,这就是。它看起来像这样:
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)
from myapp.models import Question
def converse(request, partner_id):
question = Question.objects.filter(partner=partner_id).select_subclasses().first()
question_type = question._meta.object_name.lower()
return render(
"questions/{}.html".format(question_type),
{'question': question}
)
因为它是Django的一部分,所以您可以在管理、表单等方面获得一些(但不是全部)支持
或者你可以像上面那样做,但是,正如你所注意到的,这很难看,而且似乎没有抓住使用ORM的优势。抛开“规范化表”(这个术语我不熟悉),这就是我认为解决问题的“djangish”方法。请注意,我同意你的说法“每个问题都很复杂,足以证明自己的类别”。对我来说,这意味着每种类型的问题都有其独特的领域和方法。否则,我将创建一个问题
模型,通过外键
连接到类别
模型
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
这样,您将拥有一个基本的问题
模型,用于所有问题通用的所有字段和方法,以及一组用于描述不同类型问题的子模型。基本模型和它的子模型之间有一种隐式关系,由Django维护。这使您能够创建一个包含不同问题的查询集,无论其类型如何。此查询集中的项目默认为Question
类型,但可以通过访问特殊属性(例如HealtQuestion
的HealtQuestion
属性)将其转换为特定的问题类型。这将在中详细描述
然后在视图中,您可以获得(不同类型)问题的列表,然后检测其特定类型:
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}
)
检测问题类型的代码非常难看和复杂。使用from包可以使它更简单、更通用。您需要安装软件包并将该行添加到问题
型号:
objects = InheritanceManager()
然后,视图将如下所示:
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)
from myapp.models import Question
def converse(request, partner_id):
question = Question.objects.filter(partner=partner_id).select_subclasses().first()
question_type = question._meta.object_name.lower()
return render(
"questions/{}.html".format(question_type),
{'question': question}
)
两个视图仅选择一个问题-第一个问题。这就是您示例中的视图的行为方式,因此我选择了它。您可以轻松地将这些示例转换为返回问题列表(不同类型)。抛开“规范化表”(我不熟悉这个术语),这就是我认为解决问题的“djangish”方法。请注意,我同意你的说法“每个问题都很复杂,足以证明自己的类别”。对我来说,这意味着每种类型的问题都有其独特的领域和方法。否则,我将创建一个问题
模型,通过外键
连接到类别
模型
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
这样,您将拥有一个基本的问题
模型,用于所有问题通用的所有字段和方法,以及一组用于描述不同类型问题的子模型。基本模型和它的子模型之间有一种隐式关系,由Django维护。这使您能够创建一个包含不同问题的查询集,无论其类型如何。此查询集中的项目默认为Question
类型,但可以通过访问特殊属性(例如HealtQuestion
的HealtQuestion
属性)将其转换为特定的问题类型。这将在中详细描述
然后在视图中,您可以获得(不同类型)问题的列表,然后检测其特定类型:
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}
)
检测问题类型的代码非常难看和复杂。