Django表单上传多文件时提示DeliveryCreateView无object属性如何解决
问题根本原因
你遇到的AttributeError: 'DeliveryCreateView' object has no attribute 'object'报错,以及多文件上传逻辑的问题,核心有5个错误:
- 重写
post方法后,调用form_valid之前没有为视图的object属性赋值:CreateView的form_valid方法默认会保存表单生成实例并赋值给self.object,你自定义的post逻辑跳过了这个步骤,导致后续get_success_url读取self.object.pk时找不到属性。
- 重写
- 字段名拼写错误:获取上传文件时写的是
request.FILES.getlist("decument"),实际字段名是document,拼写错误会导致拿不到上传的文件列表。
- 字段名拼写错误:获取上传文件时写的是
- 模型调用错误:
self.Delivery.objects.create写法错误,视图实例本身没有Delivery属性,应该直接调用模型类Delivery.objects.create,或者用视图绑定的self.model.objects.create。
- 模型调用错误:
- 模型设计不符合多文件上传需求:当前的
Delivery模型只有一个FileField,一个实例只能关联一个文件,你循环创建多个Delivery实例的逻辑不符合业务需求,相当于每传一个文件就生成一个重复的配送单,其他必填字段(用户、地址、时间)都会缺失。
- 模型设计不符合多文件上传需求:当前的
- 类继承冗余:
CreateView本身已经继承了FormView,不需要重复继承。
- 类继承冗余:
解决方案
1. 先修正模型,增加多文件关联表
# 原Delivery模型保留,删除原来的document字段,新增独立的文件表 class Delivery(models.Model): user = models.ForeignKey( Client, on_delete=models.CASCADE, verbose_name=_("Client"), related_name="delivery_user" ) pickup_address = models.ForeignKey(Address, on_delete=models.CASCADE, related_name="pickup_address") destination_address = models.ForeignKey(Address, on_delete=models.CASCADE, related_name="destination_address") operation_date = models.DateField( _("desired pickup date"), auto_now=False, auto_now_add=False, blank=False, null=False ) operation_time = models.TimeField( _("desired pickup time"), auto_now=False, auto_now_add=False, blank=False, null=False ) class Meta: ordering = ("-created_at",) verbose_name = _("Delivery") verbose_name_plural = _("Deliveries") def get_absolute_url(self): return reverse("delivery:unconf-delivery-details", args=[self.id]) # 新增配送文件表,一个配送单对应多个文件 class DeliveryDocument(models.Model): delivery = models.ForeignKey(Delivery, on_delete=models.CASCADE, related_name="documents") document = models.FileField( help_text=_("Delivery Documents"), verbose_name=_("Delivery Certificates"), upload_to="documents/deliveries_documents/", )
注意:related_name不要加翻译函数_(),它是代码中调用的属性,不需要国际化
2. 修正表单
class UserDeliveryForm(forms.ModelForm): # 新增多文件上传字段,不绑定到Delivery模型 documents = forms.FileField(widget=forms.ClearableFileInput(attrs={"multiple": True}), required=False) class Meta: model = Delivery fields = [ "user", "pickup_address", "destination_address", "operation_date", "operation_time", ] widgets = { "operation_date": forms.DateInput( format=("%d %B %Y"), attrs={"class": "form-control mb-2 delivery-form", "placeholder": "JJ-MM-AAAA"}, ), "operation_time": forms.TimeInput( attrs={"type": "time", "class": "form-control mb-2 delivery-form"}, ), "user": forms.Select(), "pickup_address": forms.Select(), "destination_address": forms.Select(), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields["user"].widget.attrs.update({"class": "form-control mb-2 delivery-form", "placeholder": "Client "}) self.fields["pickup_address"].widget.attrs.update( {"class": "form-control mb-2 dropdown delivery-form", "placeholder": "Addresse de ramassage "} ) self.fields["destination_address"].widget.attrs.update( {"class": "form-control mb-2 delivery-form", "placeholder": "Addresse de destination"} )
3. 修正视图逻辑
# 去掉冗余的FormView继承 class DeliveryCreateView(LoginRequiredMixin, UserPassesTestMixin, CreateView): model = Delivery form_class = UserDeliveryForm template_name = "deliveries/customer/edit_deliveries.html" def get_success_url(self): return reverse("delivery:operation-form", kwargs={"pk": self.object.pk}) def test_func(self): return self.request.user.is_active def form_valid(self, form): # 先保存配送单主实例,赋值给self.object self.object = form.save() # 获取上传的所有文件 files = self.request.FILES.getlist("documents") for file in files: # 为每个文件创建关联的配送文件记录 DeliveryDocument.objects.create(delivery=self.object, document=file) return super().form_valid(form)
不需要重写整个post方法,直接重写form_valid即可,原有逻辑更加简洁,也不会丢失CreateView的默认处理逻辑
内容的提问来源于stack exchange,提问作者E.Mohammed
相关产品推荐
相关产品推荐

