Дублирующие данные, как правило, (более серьезные) антипаттерн . Синхронизация данных, даже в двух таблицах в одной базе данных, часто оказывается сложнее, чем можно было ожидать. Даже если вы, например, используете структуру сигналов Django, некоторые вызовы Django ORM могут обойти это (например, .update(..)
[Django-doc] ). Но даже если вы как-то рассмотрите эти случаи, тогда другая программа, которая обращается к базе данных, может обновить одно из двух полей.
Большинство баз данных имеют триггеры , которые могут помочь. Но опять же, количество случаев, которые нужно покрыть, часто превышает ожидаемое. Например, если Product
, на который мы ссылаемся, удален, то или внешний ключ теперь указывает на другое Product
, тогда нам нужно будет обновить это поле.
Поэтому часто лучше,чтобы получить название соответствующего продукта, когда нам это нужно. Мы можем сделать это путем (а) определения свойства;или (b) сделать аннотацию, например, в менеджере.
Определение свойства
Мы можем определить свойство, которое будет загружать связанный продукт, и извлекать связанное имя, например:
class Order(models.Model):
product = models.ForeignKey(Product, on_delete=models.PROTECT)
<b>@property</b>
def product_name(self):
return self.product.name
Затем мы можем получить название продукта с помощью some_order.product_name
. Это может быть не очень эффективно, если нам нужно часто его извлекать, поскольку отношения по умолчанию загружаются в Django лениво и, следовательно, могут привести к проблеме N + 1 .
Аннотировать набор запросов
Мы можем сделать аннотацию, которая будет извлекать имя продукта в том же запросе, когда мы получим Order
, например:
from django.db.models import F
class <b>OrderManager</b>(models.Manager):
def get_queryset(self):
return super().get_queryset().annotate(
<b>product_name=F('product__name')</b>
)
class Order(models.Model):
product = models.ForeignKey(Product, on_delete=models.PROTECT)
objects = OrderManager()
Тогда, если мы получимЗаказ. Например, с Order.objects.get(pk=1)
этот объект Order
будет иметь атрибут product_name
с именем product
.