繁体   English   中英

Django 模型字段实际上是对相关模型中字段的引用

[英]Django model field that's actually a reference to a field in a related model

如果这个问题很难理解,请提前道歉——我不知道如何措辞。 基本上,我试图在 Django 模型中创建一种“伪字段”,它的工作原理与任何其他 Django 字段完全一样,只是它实际上是对相关模型上的字段的引用。

举个例子,假设我经营一家养狗的旅馆。 在我的旅馆里,我有房间,每个房间都分配给一个顾客,里面有一只狗。

class Customer(Model):
    name = models.CharField(max_length=256, null=False)

class Dog(Model):
    name = models.CharField(max_length=256, null=False)
    customer = model.ForeignKey(Customer, null=True, on_delete=models.CASCADE) # The dog's owner

class Room(Model):
    customer = model.ForeignKey(Owner, null=True, on_delete=models.SET_NULL)
    dog = model.ForeignKey(Dog, null=True, on_delete=models.SET_NULL)

数据库中对应的表看起来像这样

CUSTOMER:

| ID | NAME       |
___________________
| 01 | John Smith |
| 02 | Jane Doe   |


DOG:

| ID | NAME  | CUSTOMER_ID |
____________________________
| 01 | Rover |     01      |
| 02 | Fido  |     01      |
| 03 | Spot  |     02      |


ROOM:

| ID | DOG_ID | CUSTOMER_ID |
_____________________________
| 01 |   01   |      01     |
| 02 |   03   |      02     |

所以,我的老板注意到我们在 DB 中存储了冗余数据:rooms 表并不需要有自己的客户 ID 列:客户始终是常驻狗的主人,每个房间只包含一只狗,并且每只狗都有一个主人,所以我们总是可以通过去狗桌并查找主人来将客户分配到房间。 我被要求以一种对其余代码库“完全透明”的方式从房间表中删除客户 ID 列。

首先,我可以转换customer@property在客房类,使用自定义的getter:

class Room(Model):
    dog = model.ForeignKey(Dog, null=True, on_delete=models.SET_NULL)

    @property
    def customer(self):
        return self.dog.customer

所以现在在代码中我们做类似c = room.customer ,它继续工作。 问题是,代码库是满场的Django查询像room__customer ,当我提出这一切停止工作customer@propertyRoom 我可以将它们全部更改为room__dog__customer ,这很好用,但是更改不会“完全透明”。

我做了一些研究,并尝试实现自定义管理器并注释房间的查询集:

class RoomManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().annotate(customer=F('dog__customer'))

当我这样做时,我可以在查询中使用room__customer ,但不能使用room__customer__name ,我想是因为F()返回主键值而不是模型实例( https://docs.djangoproject.com/en/2.1/ref/模型/表达式/#using-f-with-annotations )。

所以我的问题是,有没有一种我不知道的方法可以使这项工作? 有一种方法可以让 Room 表现得像它与 Customer 有直接的外键关系,而没有customer_id存储在 rooms 表中?

我猜你的经理关于“完全透明”的说法只是一种表达“无论你改变什么都不应该搞砸其他业务逻辑”的方式。 换句话说,应用程序应该像今天一样继续工作。 我非常怀疑他是否关心你编辑了多少文件(不过,他可能是一个微观经理,在这种情况下......我很抱歉)。

也就是说,我认为您将room__customer更改为room__dog__customer解决方案是最好的room__dog__customer 这一变化让其他 Django 开发人员很清楚发生了什么(你正在处理关系),这是一个相当简单的变化。 是的,您最终可能不得不接触许多文件,但是当您处理后端架构时,这就是生活。

但是,此更改需要考虑的一件事是潜在的性能影响。 您可能需要在查询中引入select_related调用,以确保正确连接表。 否则,执行room -> dog -> customer查找可能会变得昂贵(每次查找需要额外的数据库往返;这确实会在循环中累加)。

祝你好运!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM