Database 使用ModelForm时强制某些字段的值

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.

我有一个Django应用程序,用户在其中提交付款订单。显然,安全是重要的。我希望尽量减少必须编写的代码量,避免引入任何安全漏洞,并简化维护

模型很简单:

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)