我正在尝试使用 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访问
在原始 SQL 中,当我想跨所有表访问信息时,我会执行一系列丑陋的 OUTER JOIN
s,用 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
在相关表上,每个表都有一个所有表共有的主键字段?
最佳答案
如果您的数据库访问允许这样做,我可能会通过定义一个 Person
模型来做到这一点,然后将 name
数据库列声明为该模型的外键to_field
设置为人物模型的名字。然后您可以在查询中使用常用的 __
语法。
假设 Django 无论如何都不会提示带有 primary_key=True
的 ForeignKey
字段。
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')
...
我认为只要将 name
声明为您的主键,实际上就不需要 to_field
,但我认为这有利于清晰度。或者,如果您没有将 name
声明为 person 的 PK。
不过我还没有测试过。
要使用 View ,您有两个选择。我认为两者都最好使用包含所有已知用户名的实际表,也许也像 Django 通常期望的那样使用数字 PK。我们假设该表存在 - 称它为 person
。
一个选项是创建一个单一的大 View 来包含有关用户的所有信息,类似于您在上面使用的大连接 - 类似于:
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
;
这可能需要一些调试,但思路应该很清晰。
然后在 Meta 类
中使用 db_table = person_info
和 managed = False
声明您的模型。
第二个选项是为每个包含与名称匹配的 person_id
值的子表声明一个 View ,然后只使用 Django FKs。
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 设置。
这有点不雅,我对性能没有任何要求,但它确实可以避免进一步对数据库进行反规范化。
关于python - 如何查询描述非规范化表的多个 Django 模型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17604547/