Django создает поле, связанное с другим значением поля (объекта) - PullRequest
1 голос
/ 31 октября 2019

Несколько лет назад я работал с Odoo Framework. и у Odoo есть очень приятная функция, подобная этой:

partner_id = field.Many2one(Partner)
partner_name = fields.Char(string='Partner name', related='partner_id.name')

, в основном, когда бы вы ни назначали другой partner_id из таблицы Partner, имя партнера будет назначаться автоматически. Теперь я начал работать с django (абсолютный новичок), и я не могу найти подобную функциональность. Мой вопрос, что может быть возможным решением этой проблемы. Может быть, уже существуют внешние библиотеки, которые имеют такую ​​функциональность? Ожидаемый результат:

product = models.ForeignKey(Product)
product_color = models.CharField(string='Partner name', related='product.color')

с учетом того, что у объекта продукта будет поле цвета, и оно будет присвоено product_color при каждом изменении значения поля продукта Значение цвета объекта продукта. И как насчет хранения его в базе данных? Было бы неплохо, если бы была возможность выбрать между хранением в базе данных или получением на лету.

Приветствия!

Ответы [ 2 ]

1 голос
/ 31 октября 2019

Создать метод получения довольно легко, потому что вы можете просто иметь функции в объекте Python, которые ведут себя как свойство:

class SampleModel(models.Model):
    product = models.ForeignKey(Product)

    @property
    def product_color(self):
         return self.product.color

Это извлекает свойство на лету, что вызовет вызовбазы данных.

0 голосов
/ 31 октября 2019

Дублирующие данные, как правило, (более серьезные) антипаттерн . Синхронизация данных, даже в двух таблицах в одной базе данных, часто оказывается сложнее, чем можно было ожидать. Даже если вы, например, используете структуру сигналов 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.

...