[英]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
为@property
的Room
。 我可以将它们全部更改为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.