Database 使用ModelForm时强制某些字段的值
我有一个Django应用程序,用户在其中提交付款订单。显然,安全是重要的。我希望尽量减少必须编写的代码量,避免引入任何安全漏洞,并简化维护 模型很简单:Database 使用ModelForm时强制某些字段的值,database,django,security,modelform,Database,Django,Security,Modelform,我有一个Django应用程序,用户在其中提交付款订单。显然,安全是重要的。我希望尽量减少必须编写的代码量,避免引入任何安全漏洞,并简化维护 模型很简单: class Order(models.Model): user = models.ForeignKey(User) created = models.DateTimeField() paid = models.DateTimeField(null=True, blank=True) items = models.
class Order(models.Model):
user = models.ForeignKey(User)
created = models.DateTimeField()
paid = models.DateTimeField(null=True, blank=True)
items = models.ManyToManyField(Item)
我正在使用创建订单的实例:
class OrderView(CreateView):
model = Order
form_class = OrderForm
我想在这些实例中强制某些字段的值。例如,我希望实例user
字段设置为当前登录用户。我不希望用户可以更改此字段的值,因此我根本不希望它出现在表单中。因此,我使用自定义从表单中删除这些字段:
class OrderForm(forms.ModelForm):
class Meta:
model = Order
# For security, we control exactly which fields are placed
# in the form, rather than excluding some:
fields = ('items',)
现在,我希望新创建的订单实例将user
字段设置为当前登录用户。我找不到任何关于什么是最好的方法的文档
(A)我可以覆盖表单的save()
方法来在保存之前修改对象,但感觉此代码不属于表单,表单不知道有关用户
字段的任何信息。我也无法访问此处的请求
,我需要确定当前用户。但可能是这样的:
class OrderForm(forms.ModelForm):
def save(self, commit=True):
instance = super(OrderForm, self).save(commit=False)
instance.user = get_request_magic().user
if commit:
instance.save()
return instance
(B)我可以使用commit=False保存对象,就像基于类的版本一样。但是我不能直接调用超类方法,因为它保存对象时无法禁用提交,所以我必须手动跳过生成form\u valid
,这很糟糕。除了抱怨之外,这确实是我迄今为止找到的最好的方法:
class OrderView(CreateView):
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()
return super(ModelFormMixin, self).form_valid(form)
(C)我可以为CreateView
编写一个替换程序,添加一个钩子,允许在保存对象之前对其进行更改。但这感觉像是更多的样板和重复
(D)我不能,因为没有表单字段可将其放入,因此它将被忽略
还有其他想法吗?如果(B)是最好的选择,那么有没有办法绕过手动指定我要调用哪个超类“
form\u有效的方法”
的老套方法?可能有多种方法。我会这样做:
在表单中创建一个接受请求的构造函数:
def __init__(self, *args, **kwargs):
request = kwargs.pop('request', None)
super(OrderForm, self).__init__(*args, **kwargs)
self.request = request
创建表单进行后期处理时,请按如下方式实例化它:
form = OrderForm(data=request.POST, request=request)
现在,在save()方法中,您可以通过引用
self.request.user
访问请求中的用户,并可以在您的模型上进行相应的设置。我使用CBV处理这种情况的方法是将未保存的模型实例传递给表单。我就是这样做的:
class OrderView(CreateView):
def get_form_kwargs(self):
self.object = Order(user=self.request.user)
return super(OrderView, self).get_form_kwargs()
CreateView
和UpdateView
都会将instance
添加到表单kwargs,将其设置为self.object
的值
除了前面提到的方法之外,唯一的其他方法是从CreateView
所使用的相同元素构造视图类,然后更改get和post方法以填充self.object
。当我在项目中需要大量创建视图时,我就这样做了:
class OrderView(SingleObjectTemplateResponseMixin, ModelFormMixin, ProcessFormView):
template_name_suffix = '_form'
def get(self, request, *args, **kwargs):
self.object = Order(user=request.user)
return super(OrderView, self).get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.object = Order(user=request.user)
return super(OrderView, self).post(request, *args, **kwargs)
这里有一个更通用的版本可以重用:Django user Charettes for me:
您可以通过覆盖表单_valid来实现这一点:
这让我想到:
这绝对是迄今为止我找到的最简单、最清晰的答案。它不需要以任何方式修改表单,尽管它直接访问它的
实例
成员,这有点难看。但是,至少它是官方记录的,所以不太可能损坏。不幸的是,最后一部分不起作用。在ModelForm
save()
方法中,我们现在可以访问request
,但不能访问新实例!这是由调用construct\u instance
函数(非方法)的save\u instance
函数(非方法)创建的,因此我也必须重写这两个函数,它再次变得丑陋。感谢您的回答,不幸的是,它需要插入CreateView
的未记录对象
属性,这可能会在未来发生变化,并打破这一准则。
class OrderCreateViewMixin(CreateView):
def form_valid(self, form):
form.instance.user = request.user
return super(OrderCreateViewMixin, self).form_valid(form)
class AuthorCreate(CreateView):
form_class = AuthorForm
model = Author
def form_valid(self, form):
form.instance.created_by = self.request.user
return super(AuthorCreate, self).form_valid(form)