python - 如何查询描述非规范化表的多个 Django 模型

标签 python mysql django django-models

我正在尝试使用 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=TrueForeignKey 字段。

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_infomanaged = 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/

相关文章:

python - cherrypy 演示中缺少 tutorial.conf 文件

python - 使用Python分析YouTube评论-参数已禁用评论

mysql - 通过具有良好性能的唯一列选择多行

Python pymysql 无法接受密码字符串中的特殊字符,如何绕过这个?

javascript - onClick() 从传递给 Django 模板的函数中获取新值

python - 存储 ENUM 值的 PostgreSQL ARRAY

python - 如何制作自定义 python 站点包?

mysql - Heroku MySQL 重音问题

Django、GDAL 和 CircleCI 2

python - 什么时候使用 Django get_absolute_url() 方法?