You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中使用cleaned_data更新/保存表单字段失效问题

问题:计算的Total字段无法保存到数据库

我希望将表单中的TOTAL字段设置为计算得出的PRE-TOTAL值,但出现异常:已通过cleaned_data更新了form_data中的total值,却无法将该值保存至数据库,该字段仍显示默认值。


相关代码

MODELS.PY

from django.db import models
from django.utils import timezone

class av_pomades(models.Model):
    cod_prod = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=100)
    price = models.FloatField(default=0)
    pic = models.ImageField(default='default.jpg', blank=True)

    def __str__(self):
        return self.name


pomades_to_chose = (
    ('Suavecito - water based','Suavecito - water based'),
    ('Reuzel Blue - water based','Reuzel Blue - water based'),
    ('Layrite - water based','Layrite - water based'),
    ('Suavecito - oil based','Suavecito - oil based'),
    ('Reuzel Pink - Oil Based','Reuzel Pink - Oil Based'),
    ('Layrite - oil based','Layrite - oil based')
)

class order_form(models.Model):
    pomade = models.CharField(max_length=100, choices=pomades_to_chose)
    amount = models.PositiveIntegerField()
    ord_date = models.DateField(default=timezone.now)
    total = models.FloatField(default=0)

    def __str__(self):
        return self.pomade

FORMS.PY

from dataclasses import fields
from tabnanny import verbose
from django import forms
from . import models

class order_forms(forms.ModelForm):
    class Meta:
        # model that the fields will come from
        model = models.order_form
        # chosing the fields to be displayed
        fields = ['pomade', 'amount', 'total']  

VIEWS.PY

from django.shortcuts import render, redirect
from .models import av_pomades, order_form
from django.http import HttpResponse
from django.contrib.auth.decorators import login_required
from . import forms

@login_required(login_url='accounts:login')
def purchase(request):
    if request.method == 'POST':
        form = forms.order_forms(request.POST)
        if form.is_valid():
            # creating a cleanned data instace
            form_data = form.cleaned_data
            ch_pomade = str(form_data['pomade'])

            # retrieving the right price from the selected product 
            pomades_prices = av_pomades.objects.all().values('name','price')        
            for pomades_data in pomades_prices:
                if ch_pomade in pomades_data['name']:
                    ch_pomade_price = pomades_data['price']

            # setting the total price
            famount = int(form_data['amount'])
            pre_total = ch_pomade_price * famount
            
            # updating the price in the for to be recorded in DB
            
            # HERE IS THE PROBLEMATIC PART I GUESS
            form_data['total'] = pre_total
            print(form_data)           
            

            form.save()
            return redirect('products:order_success')
    else:
        form = forms.order_forms()
    return render(request,'basquet.html', {'order_form':form})

问题原因与解决方法

原因

form.cleaned_data是表单验证后生成的数据副本,修改它并不会影响表单绑定的模型实例。form.save()最终保存的是表单内部的instance对象,不是你修改后的form_data。

解决方法1:修改表单实例后保存(快速修复)

在视图中,不要修改cleaned_data,而是通过commit=False获取未保存的实例,修改字段后再保存:

# 替换原视图中修改form_data和save的代码
instance = form.save(commit=False)
# 计算total
famount = int(form_data['amount'])
pre_total = ch_pomade_price * famount
instance.total = pre_total
instance.save()

解决方法2:将计算逻辑移到表单的clean方法(更规范)

把total的计算逻辑放到表单的clean方法中,符合Django表单验证的流程,同时自动同步到实例:
修改FORMS.PY:

from django import forms
from . import models

class order_forms(forms.ModelForm):
    class Meta:
        model = models.order_form
        fields = ['pomade', 'amount', 'total']  

    def clean(self):
        cleaned_data = super().clean()
        pomade_name = cleaned_data.get('pomade')
        amount = cleaned_data.get('amount')
        
        if pomade_name and amount:
            # 优化查询:直接通过name获取产品,避免遍历所有数据
            try:
                pomade = models.av_pomades.objects.get(name=pomade_name)
                total = pomade.price * amount
                cleaned_data['total'] = total
                # 将计算值同步到表单实例,确保保存时生效
                self.instance.total = total
            except models.av_pomades.DoesNotExist:
                self.add_error('pomade', '所选产品不存在于数据库中')
        
        return cleaned_data

此时VIEWS.PY可以简化,不用再计算total,直接保存即可:

@login_required(login_url='accounts:login')
def purchase(request):
    if request.method == 'POST':
        form = forms.order_forms(request.POST)
        if form.is_valid():
            form.save()
            return redirect('products:order_success')
    else:
        form = forms.order_forms()
    return render(request,'basquet.html', {'order_form':form})

额外优化:查询效率提升

原视图中遍历所有av_pomades数据来匹配价格,效率极低。上面的解决方法2中用get直接查询,能大幅提升性能,同时还能处理产品不存在的异常情况。


内容的提问来源于stack exchange,提问作者IARC

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 12:52:20