Заранее извиняюсь, если за этим вопросом трудно следовать - я не уверен, как его сформулировать.По сути, я пытаюсь создать своего рода «псевдополе» в модели 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 |
Итак, мой босс заметил, что мы храним избыточные данные в БД: таблица комнат недействительно необходимо иметь собственную колонку идентификатора клиента: клиент всегда является владельцем собаки-резидента, и в каждой комнате есть только одна собака, и у каждой собаки есть один владелец, поэтому мы всегда можем назначить клиента в комнату, перейдя к собакестол и ищем хозяина.Меня попросили удалить столбец идентификатора клиента из таблицы номеров таким образом, чтобы он был «полностью прозрачным» для остальной части кода.
Для начала я могу преобразовать customer
в@property
в классе Room с пользовательским геттером:
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/models/expressions/#using-f-with-annotations).
Итак, мой вопрос: есть ли способ сделать эту работу, о которой я просто не знаю? Способ сделать так, чтобы Комната действовала так, как будто имеет прямые внешние ключиКлиент, не сохранив customer_id
в таблице номеров?