Django CBV использует значения из Modelform для вычисления других полей модели

Я пытаюсь сделать систему управления складом с помощью Django 3.2 на основе следующих моделей:

class itemtype(models.Model):
    item_id = models.IntegerField(primary_key=True)
    item_name = models.CharField(max_length=100)
    group_name = models.CharField(max_length=100)
    category_name = models.CharField(max_length=100)
    mass = models.FloatField()
    volume = models.FloatField()
    used_in_storage = models.BooleanField(default=False, null=True)

    class Meta:
        indexes = [
            models.Index(fields=['item_id'])
        ]

    def __str__(self):
        return '{}, {}'.format(self.item_id, self.item_name)

class material_storage(models.Model):
    storage_id = models.AutoField(primary_key=True)
    material = models.ForeignKey(itemtype, on_delete=models.PROTECT)
    amount_total = models.IntegerField(null=True)
    price_avg = models.FloatField(null=True)
    order_amount = models.IntegerField(null=True)
    order_price = models.IntegerField(null=True)
    timestamp = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return '{}, {} avg.: {} ISK'.format(self.material, self.amount, self.price)

"itemtype" определяет в основном возможные объекты, которые могут храниться, а "material_storage" показывает, что есть на складе. Я попытался объединить общее количество каждого товара, а также среднюю цену, уплаченную за него, и количество и цену для одного заказа в одной строке базы данных. Идея заключается в том, чтобы получить последнюю запись для выбранного товара/материала, когда происходит новый заказ, добавить сумму этого заказа и пересчитать среднюю цену. Теоретически это можно разделить на две таблицы, но в данный момент я не вижу причин для этого. Однако я не могу разобраться с кодом функции для выполнения вычислений. Я новичок в Django и поэтому немного ошеломлен сложностью. Я пытался использовать представления на основе классов и модельные формы, для простых вещей, которые работали хорошо, но теперь я немного потерялся.

Создание формы только для добавления новых строк в эту таблицу хранения было в порядке.

class NewAssetForm(forms.ModelForm):

    material = MaterialChoiceField(models.itemtype.objects.filter(used_in_storage= True))

    def __init__(self, *args, **kwargs):
        super(NewAssetForm, self).__init__(*args, **kwargs)
        self.fields['amount'].widget.attrs['min'] = 1
        self.fields['price'].widget.attrs['min'] = 1

    class Meta:
        model = models.material_storage
        fields = ( 
            'material',
            'amount',
            'price'
        )

        widgets = {
            'material': forms.Select(),
        }

То же самое для View, чтобы обработать его.

class NewItemView(FormView):

    template_name = 'assetmanager/newasset.html'
    form_class = forms.NewAssetForm
    success_url = '/storage/current'

    def form_valid(self, form):
        return super().form_valid(form)

Но теперь я застрял. Я думал, что это должна быть довольно стандартная задача, но я не смог найти решение для нее до сих пор. Идея была в том, чтобы поместить это в функцию form_valid, взять материал из формы, найти последнюю соответствующую запись, добавить новую сумму, а также рассчитать новую среднюю цену и сохранить все вместе в модель. Пока я нашел только несколько примеров, сопоставимых с моей проблемой, и я не смог перевести их на мою установку, так что, возможно, кто-то может дать мне подсказку для более успешного поиска или предоставить мне пример, как подойти к этой теме.

thx in advance.

Для изменения значений полей формы можно переопределить метод "clean" и предоставить значения полям формы. Доступ к данным можно получить с помощью "self.clean_data", это словарь.

class NewAssetForm(ModelForm):
    def clean(self):
        super().clean()
        # place code that retrieves existing data and calculate new values.
        self.cleaned_data['price'] = 'New Value'

очищенные_данные будут переданы в "form_valid", там вы можете вызвать функцию сохранения. "form.save()" создаст новую строку, убедитесь, что вы передаете действительные значения представлениям. Поскольку вы принимаете в форму несколько полей, убедитесь, что у вас есть значения по умолчанию для полей, которые не включены в объект формы.

Спасибо за ответ Я нашел решение, используя метод form_valid() в FormView. Большая часть кода используется для создания записей на основе существующих записей или для проверки, есть ли уже записи для того же материала.

class NewItemView(FormView):

    template_name = 'assetmanager/newasset.html'
    form_class = forms.NewAssetForm
    success_url = '/storage/current'

    def form_valid(self, form):
        try:
            # check if there is already a record for this material.
            material_storage.objects.filter(material_id = form.cleaned_data['material'])[:1]

            # getting total amount and average price values from latest entry with said material.
            total_amount = material_storage.objects.values('amount_total').filter(material_id=form.cleaned_data['material']).order_by('-timestamp')[:1][0]['amount_total']
            avg_price = material_storage.objects.values('price_avg').filter(material_id=form.cleaned_data['material']).order_by('-timestamp')[:1][0]['price_avg']

            amount = form.cleaned_data['amount']
            price = form.cleaned_data['price']

            # calculating new total amount and average price based on old values and new entry. 
            form.instance.amount_total = total_amount + amount
            form.instance.price_avg = ((avg_price * total_amount) + (price * amount)) / (total_amount + amount)
            form.save()
        except material_storage.DoesNotExist:

            # if there is no entry for the chosen material yet, amount = total amount, price = average price.
            form.instance.amount_total = form.cleaned_data['amount']
            form.instance.price_avg = form.cleaned_data['price']
            form.save()       
             
        return super().form_valid(form)

На данный момент это решает мою проблему, однако я не знаю, имеет ли смысл выбранное место (form_valid()) - ваш ответ предполагает, что это имело бы больше смысла в другом месте. Кроме того, проверка того, существует ли уже запись для материала, и выбор значений из такой записи, не очень элегантны и эффективны. Но, как уже было сказано, я начинающий - буду рад любым предложениям по улучшению.

Я также пока не уверен, что это справится со всеми возможными особыми случаями, которые могут появиться...

Вернуться на верх