3

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

模型很简单:

class Order(models.Model):
    user = models.ForeignKey(User)
    created = models.DateTimeField()
    paid = models.DateTimeField(null=True, blank=True)
    items = models.ManyToManyField(Item)

我正在使用CreateView创建 Order 实例:

class OrderView(CreateView):
    model = Order
    form_class = OrderForm

我想在这些实例中强制执行某些字段的值。例如,我希望将实例user字段设置为当前登录用户。我不希望用户可以更改此字段的值,因此我根本不希望它出现在表单中。因此,我使用自定义ModelForm从表单中删除这些字段:

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',)

现在我希望新创建的 Order 实例将该user字段设置为当前登录用户。我找不到任何有关执行此操作的最佳方法的文档。

(A)我可以在保存之前重写表单的save()方法来修改对象,但是感觉这段代码不属于表单,对user字段一无所知。我也无权访问request此处,我需要确定当前用户。但它可能看起来像这样:

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_valid方法来保存对象,就像这个 question的基于类的版本一样。但是我不能直接调用超类方法,因为它保存了对象没有办法禁用提交,所以我必须手动跳过form_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)我不能提供initial,因为没有表单域可以放入,所以会被忽略。

还有其他想法吗?如果(B)是最好的选择,有什么办法可以绕过手动指定form_valid我想调用哪个超类的方法吗?

4

3 回答 3

2

Django 用户Charettes为我 回答了这个问题

您可以通过覆盖 form_valid 来实现:

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)

This is definitely the simplest and cleanest answer I've found so far. It doesn't require modifying the form in any way, although it does directly access its instance member which is a bit ugly. However, at least it's officially documented, so it's unlikely to break.

于 2013-01-03T11:45:30.840 回答
0

对此可能有多种方法。我会这样做:

在您的表单中创建一个接受请求的构造函数:

def __init__(self, *args, **kwargs):
        request = kwargs.pop('request', None)
        super(OrderForm, self).__init__(*args, **kwargs)
        self.request = request

在为 POST 处理创建表单时,将其实例化如下:

form = OrderForm(data=request.POST, request=request)

现在,在您的 save() 方法中,您可以通过引用访问请求中的用户,self.request.user并可以在您的模型上相应地设置它。

于 2012-12-05T17:49:24.533 回答
0

我用 CBV 处理这种情况的方法是将模型的未保存实例传递给表单。这就是我的做法:

class OrderView(CreateView):
    def get_form_kwargs(self):
        self.object = Order(user=self.request.user)
        return super(OrderView, self).get_form_kwargs()

两者CreateViewUpdateView都将添加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)

这是一个更通用的版本,可以重用:https ://gist.github.com/4439975

于 2013-01-03T01:14:39.720 回答