Как создать поле в модели django, используя значения других полей той же модели?

#python #django #django-models #django-2.1

#python #django #django-модели #django-2.1

Вопрос:

Я хочу создать имя поля:total, в котором указана общая стоимость всех продуктов * quqantity. Я хочу, как я могу это сделать total = price*quantity в модели django. Как вы можете видеть, может быть более одного продукта.
Я присоединился к модели OderItem с порядком через tabularinline.

введите описание изображения здесь

 class OrderItemInline(admin.TabularInline):
    model = OrderItem
    raw_id_fields = ['product']

class OrderAdmin(admin.ModelAdmin):
    list_display = ['id','name', 'mobile_no','address','status', 'created']
    list_editable = ['status']
    list_per_page = 15
    list_filter = ['status']
    search_fields = ('id',)
    inlines = [OrderItemInline]


admin.site.register(Order, OrderAdmin)  
 class Order(models.Model):
    name = models.CharField(max_length=60)
    email = models.EmailField(max_length=60,default=None, blank=True)
    mobile_no = models.CharField(max_length=13, default=None) 
    address = models.CharField(max_length=150)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    status_choices = (
        ('In Queue', 'In Queue'),
        ('Processing', 'Processing'),
        ('Ready', 'Ready'),
        ('Delivered', 'Delivered'),
        ('Paid', 'Paid'),
        ('Cancelled', 'Cancelled'),
    ) 
    status = models.CharField(max_length=15,       choices=status_choices, default=status_choices[0][0])


    class Meta:
        ordering = ('created', )

    def __str__(self):
        return 'Order {}'.format(self.id)

    def get_total_cost(self):
        return sum(item.get_cost() for item in self.items.all())



class OrderItem(models.Model):
    order = models.ForeignKey(Order, related_name='items', 
    on_delete=models.CASCADE)
    product = models.ForeignKey(MenuVariant, related_name='order_items', 
    on_delete=models.CASCADE, default=None)
    size = models.CharField(max_length=20, default=None)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    quantity = models.PositiveIntegerField(default=1)
    # total = models.DecimalField(max_digits=10, decimal_places=2,default=0)



    def __str__(self):
        return '{}'.format(self.id)

    def get_cost(self):
        return self.price * self.quantity  

Комментарии:

1. Что вы имеете в виду, что хотите «создать общее поле»? Вы хотите отобразить общую цену в admin? Или вы хотите иметь доступ к итогу в своем коде?

2. хочет сделать оба

3. Но у вас уже есть это в вашем коде: order.get_total_cost() .

4. в нем отображается только общая цена одного продукта, например, цена * количество

5. да? это сумма стоимости для всех товаров, а не только для одного продукта. Ваш код правильный.

Ответ №1:

Вы можете получить общую цену за Order непосредственно с запросом к базе данных, поэтому нет необходимости сохранять ее в базе данных:

 from django.db.models import Sum, F, FloatField
orders = Order.objects.annotate(total=Sum(F('items__quantity')*F('items__price'), output_field=FloatField()))
  

Это добавляет total поле к каждому результату запроса. Итак, тогда:

 price_of_first_order = orders.first().total
  

И если вы хотите отобразить итоговое значение для order в admin, сделайте подкласс ModelAdmin и добавьте get_total_cost к readonly_fields , как описано здесь:

Поле, доступное только для чтения, может не только отображать данные из поля модели, оно также может отображать выходные данные метода модели или метода самого класса ModelAdmin. Это очень похоже на то, как ведет себя ModelAdmin.list_display.

Комментарии:

1. я добавляю этот код в модель заказа или модель элемента заказа?

2. поскольку вы сказали, что функция get_total_cost выполняет то, что я прошу, тогда как я могу сделать total= get_total_cost в модели

3. эй, ты можешь мне помочь?

4. Смотрите ответ ниже от @amazonic, если вы действительно хотите его сохранить. Но, как я уже сказал, это плохая практика, потому что это избыточно и почти наверняка на каком-то этапе приведет к несоответствию данных в вашей базе данных. Вам нужно всегда сохранять один источник истины, сохранение этого в базе данных создает второй источник для той же истины.

5. я не понимаю, как использовать код, который вы предоставили, я копирую мимо кода в модели Order, но он не работает. я новичок в django и делаю это впервые.

Ответ №2:

Хотя я, как и диркгротен, не совсем понимаю смысл сохранения значения в поле модели, вы могли бы добавить поле total=models.IntegerField(null=True, blank=True) в свою Order модель и использовать post_save для обновления значения.

Итак, поскольку у вас уже есть get_total_cost() , мы можем сделать что-то вроде:

 def order_pre_save(sender, instance, **kwargs):
    instance.total_cost = instance.get_total_cost()

pre_save.connect(order_pre_save, sender=Order)
  

РЕДАКТИРОВАТЬ: как указывает диркгротен, вам понадобится post_save на OrderItem :

 def order_item_pre_save(sender, instance, **kwargs):
    instance.order.total_cost = instance.order.get_total_cost()
    instance.order.save()

post_save.connect(order_item_pre_save, sender=OrderItem)
  

И вам также нужно будет обновить значение при удалении:

 def order_item_pre_delete(sender, instance, **kwargs):
    instance.order.total_cost = instance.order.get_total_cost() - instance.get_cost()
    instance.order.save()

post_delete.connect(order_item_pre_delete, sender=OrderItem)
  

Комментарии:

1. это сработало бы, но вы хотели бы подключить сохранение OrderItem , а не Order для обновления поля Order . Так что это было бы instance.order.total_cost = instance.order.get_total_cost(); instance.order.save()

2. проблема в том, что вы также должны обновлять его при удалении OrderItem. Также эти сигналы не вызываются при определенных обстоятельствах, например, когда происходит массовое обновление или массовое создание.

3. когда я запускаю функцию order_item_pre_save, она выдает ошибку OrderItem не определен

4. Вы определили свою новую функцию в своем models.py ? Поместите свои записи / предварительные сохранения внизу models.py , где находится ваша модель.