1

我正在尝试使用 Django 模型从许多非规范化表中提取信息。这些表是预先存在的,是遗留 MySQL 数据库的一部分。

架构描述

假设每个表都描述了一个人的特征,并且每个人都有一个名字(这本质上标识了这个人,但不对应于一些统一的“Person”表)。例如:

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示例),使一个表有一个字段用于将其与其他表绑定。但是,这意味着一个表需要“中央”表,所有其他表都反向引用。这似乎与 20 多个字段混在一起,并且没有真正的示意图意义(“工作信息”是核心属性吗?衣服?)。

我觉得我正在以错误的方式解决这个问题。我应该如何QuerySet在相关表上构建一个,其中每个表都有一个在所有表中通用的主键字段?

4

1 回答 1

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 设置。

这有点不雅,我没有声称性能良好,但它确实可以让您避免进一步对数据库进行非规范化。

于 2013-07-11T22:39:08.230 回答