Django3.0如何给ModelFormSet自动填充当前登录用户为作者外键?
解决Django FormSet自动填充当前用户为外键且不显示字段的问题
嘿,这个问题我之前在项目里也碰到过,刚好Django 3.0里有几种优雅的解决方案,完全不用自定义模板,给你梳理一下:
错误原因先理清楚
你遇到的'list' object has no attribute 'author'报错,是因为formset的save(commit=False)返回的是一个实例列表(毕竟FormSet是处理多个表单的),而不是单个模型对象,直接给列表赋值author肯定会出错~
方案一:直接遍历实例列表赋值(简单直接)
这是最直观的解决方式,拿到save返回的实例列表后,逐个给每个实例设置author:
from django.forms import modelformset_factory from .models import Creation import datetime def submit(request): # 只包含需要显示的字段,author完全不加入fields FormSet = modelformset_factory(Creation, fields=('title', 'url')) if request.method == 'POST': formset = FormSet(request.POST, request.FILES) if formset.is_valid(): # 拿到未保存的实例列表 instances = formset.save(commit=False) # 遍历每个实例,设置当前登录用户为author for instance in instances: instance.author = request.user instance.save() # 如果你的模型有多对多字段,别忘了调用这个方法保存关联关系 formset.save_m2m() else: # 加上queryset=Creation.objects.none(),避免默认显示现有数据 formset = FormSet(queryset=Creation.objects.none()) return render(request, 'app/submit.html', {'formset': formset})
这种方法适合快速解决问题,代码改动小,完全不会在模板里显示author的选择框。
方案二:封装逻辑到自定义ModelForm(更优雅,符合Django设计理念)
如果想把设置author的逻辑和视图解耦,可以自定义一个ModelForm,把用户赋值的逻辑放在Form里,视图会更简洁:
首先定义你的Form类:
from django import forms from .models import Creation from django.conf import settings class CreationForm(forms.ModelForm): class Meta: model = Creation fields = ('title', 'url') # 同样不包含author字段 def __init__(self, *args, **kwargs): # 从初始化参数中取出当前用户,后续保存时用 self.current_user = kwargs.pop('user') super().__init__(*args, **kwargs) def save(self, commit=True): # 先调用父类的save方法拿到实例 instance = super().save(commit=False) # 设置author为当前用户 instance.author = self.current_user if commit: instance.save() return instance
然后在视图里使用这个自定义Form创建FormSet:
def submit(request): # 指定form_class为我们自定义的CreationForm FormSet = modelformset_factory(Creation, form=CreationForm) if request.method == 'POST': # 通过form_kwargs给每个Form传递当前用户 formset = FormSet(request.POST, request.FILES, form_kwargs={'user': request.user}) if formset.is_valid(): # 直接调用save就行,Form里已经处理了author赋值 formset.save() else: # 同样初始化空的FormSet,传递当前用户 formset = FormSet(queryset=Creation.objects.none(), form_kwargs={'user': request.user}) return render(request, 'app/submit.html', {'formset': formset})
这种方式把业务逻辑封装在Form层,视图只负责处理请求和渲染,更符合Django的MVC架构思想,也方便后续扩展。
两种方案都不需要修改模板,模板里直接渲染{{ formset }}就只会显示title和url字段,完全看不到author相关的控件~
内容的提问来源于stack exchange,提问作者user1511417
相关产品推荐
相关产品推荐

