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
相关产品推荐
相关产品推荐

