如果很难回答这个问题,请提前道歉–我不确定该如何表达。基本上,我试图在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 |
因此,我的老板注意到我们在数据库中存储了冗余数据:rooms表实际上并不需要拥有自己的客户ID列:客户始终是当地狗的主人,每个房间仅包含一个狗,并且每只狗都有一个所有者,因此我们总是可以通过转到狗桌并查找所有者来将客户分配到房间。我被要求从“房间”表中删除“客户ID”列,其方式与其余代码库“完全透明”。
首先,我可以使用自定义吸气剂将Room类中的customer
转换为@property
:
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
之类的所有地方,它都继续起作用。问题是,代码库中充斥着room__customer
之类的Django字段查询,当我将customer
变成@property
的{{1}}时,它们全部停止工作。我可以将它们全部更改为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
返回的是主键值而不是模型实例({{3 }}。
所以我的问题是,有没有办法使我不了解的这项工作?一种使Room像它与客户具有直接外键关系,而无需将F()
存储在Rooms表中的方式?
答案 0 :(得分:1)
我猜您的经理关于“完全透明”的说法仅仅是一种说法,“无论您进行什么更改都不会破坏其他业务逻辑。”换句话说,该应用程序应该像今天一样继续工作。我非常怀疑他是否在乎您编辑多少个文件(不过,在这种情况下,他可能是一个微型经理。很抱歉)。
也就是说,我认为将room__customer
更改为room__dog__customer
的解决方案是最好的做法。更改使其他Django开发人员可以明显看出正在发生的事情(正在逐步发展这种关系),并且这是一个相当简单的更改。是的,您可能最终不得不触摸许多文件,但是当您弄乱了后端架构时,这就是生活。
但是,您需要考虑此更改的一件事是潜在的性能影响。您可能需要向您的查询引入select_related
调用,以确保正确连接表。否则,进行room -> dog -> customer
查找可能会变得很昂贵(每次查询都要进行一次额外的数据库往返行程 ;这确实是一个循环。)
祝你好运!