我正在尝试使用Django模型从许多非规范化表中提取信息。这些表是预先存在的,是遗留MySQL数据库的一部分。
假设每个表都描述了一个人的特征,每个人都有一个名字(这实际上是一个人,但不对应一些统一的“人”表)。例如:
class JobInfo(models.Model):
name = models.CharField(primary_key=True, db_column='name')
startdate = models.DateField(db_column='startdate')
...
class Hobbies(models.Model):
name = models.CharField(primary_key=True, db_column='name')
exercise = models.CharField(db_column='exercise')
...
class Clothing(model.Model):
name = models.CharField(primary_key=True, db_column='name')
shoes = models.CharField(db_column='shoes')
...
# Twenty more classes exist, all of the same format
在原始SQL中,当我想访问所有表中的信息时,我会执行一系列丑陋的OUTER JOIN
,并使用WHERE
子句对其进行优化。
SELECT JobInfo.startdate, JobInfo.employer, JobInfo.salary,
Hobbies.exercise, Hobbies.fun,
Clothing.shoes, Clothing.shirt, Clothing,pants
...
FROM JobInfo
LEFT OUTER JOIN Hobbies ON Hobbies.name = JobInfo.name
LEFT OUTER JOIN Clothing ON Clothing.name = JobInfo.name
...
WHERE
Clothing.shoes REXEGP "Nike" AND
Hobbies.exercise REGEXP "out"
...;
我正在尝试将其转换为基于Django的方法,在那里我可以轻松获得从所有表中提取信息的QuerySet
。
我已经研究过使用OneToOneField
(example),使一个表有一个字段可以将其绑定到其他每个表。但是,这意味着一个表需要“中央”表,其他所有表都反向引用。这看起来像二十多个字段的混乱,并没有真正意义上的原因(“工作信息”是核心属性?衣服?)。
我觉得我的方式错了。我应该如何在相关表上构建QuerySet
,其中每个表在所有表中都有一个主键字段?
答案 0 :(得分:2)
如果您的数据库访问允许这样做,我可能会通过定义Person
模型来执行此操作,然后将name
数据库列声明为该模型的外键,并设置to_field
作为人物模型上的名称。然后,您可以在查询中使用通常的__
语法。
假设Django不抱怨ForeignKey
字段primary_key=True
,无论如何。
class Person(models.Model):
name = models.CharField(primary_key=True, max_length=...)
class JobInfo(models.Model):
person = models.ForeignKey(Person, primary_key=True, db_column='name', to_field='name')
startdate = models.DateField(db_column='startdate')
...
只要将to_field
声明为您的主键,我认为实际上并不需要name
,但我认为这有利于清晰。或者,如果您没有将name
声明为人身PK。
我没有测试过这个。
要使用视图,您有两种选择。我认为两者都可以在包含所有已知用户名的实际表中做得最好,也可以使用Django通常期望的数字PK。我们假设表存在 - 称之为person
。
一个选项是创建一个大型视图来包含有关用户的所有信息,类似于您在上面使用的大型联接 - 类似于:
create or replace view person_info as
select person.id, person.name,
jobinfo.startdate, jobinfo.employer, jobinfo.salary,
hobbies.exercise, hobbies.fun,
clothing.shoes, ...
from person
left outer join hobbies on hobbies.name = person.name
left outer join jobinfo on jobinfo.name = person.name
left outer join clothing on clothing.name = person.name
;
这可能需要一些调试,但这个想法应该是明确的。
然后在db_table = person_info
中使用managed = False
和Meta class
声明您的模型。
第二个选项是为包含与名称匹配的person_id
值的每个子表声明一个视图,然后只使用Django FK。
create or replace view jobinfo_by_person as
select person.id as person_id, jobinfo.*
from person inner join jobinfo on jobinfo.name = person.name;
create or replace view hobbies_by_person as
select person.id as person_id, hobbies.*
from person inner join hobbies on hobbies.name = person.name;
等。同样,我不完全确定。*语法是否有效 - 如果没有,您必须列出您感兴趣的所有字段。并检查子表中的列名是什么。
然后将模型指向by_person
版本并使用标准FK设置。
这有点不优雅,我没有声称有良好的性能,但它确实让你避免进一步反规范化你的数据库。