在CreateView中设置值的更好方法?

时间:2013-09-27 13:19:30

标签: python django

models.py

class BlogPost(models.Model):
    title = models.CharField(max_length=200)
    post_body = models.TextField()
    user = models.ForeignKey(User)
    pub_date = models.DateTimeField('published')

    def get_absolute_url(self):
        return u'/entries/%d' % self.id

forms.py

class NewPostForm(forms.ModelForm):
    class Meta:
        model = BlogPost
        fields = ('title', 'post_body')

所以,我试图让用户在博客中发布新帖子。 BlogPost模型有4个字段 - titlepost_bodyuserpub_date。用户应该可以填写titlepost_body,而其他两个应该自动填充。我尝试的第一件事是:

class NewPostView(generic.edit.CreateView):
    model = BlogPost
    form_class = NewPostForm

    def get_initial(self):
        return {'pub_date': timezone.now(),
                'user': self.request.user}

但它唯一能做的就是为html表单提供默认值。经过进一步的研究,这是最终起作用的代码:

class NewPostView(generic.edit.CreateView):
    model = BlogPost
    form_class = NewPostForm

    def form_valid(self, form):
        obj = form.save(commit=False)
        obj.user = self.request.user
        obj.pub_date = timezone.now()
        obj.save()
        return HttpResponseRedirect(obj.get_absolute_url())

所以,虽然覆盖form_valid方法有效,但我觉得它应该怎么做呢?是否有更好的地方将这些行动纳入其中?这样做的传统方式是什么?

4 个答案:

答案 0 :(得分:4)

除了你的form_save方法(不应该返回响应)之外,这正是它的完成方式:

  1. 您可以将auto_now_add用于pub_date字段,它会在首次创建模型时添加当前时间戳(为您省去麻烦)。

  2. 您还可以在success_url参数中使用模型中的字段,如下所示:

    class CreateNewPost(generic.edit.CreateView):
        model = BlogPost
        form_class = NewPostForm
        success_url = '/entries/%(id)s'
    
        def form_valid(self, form):
            obj = form.save(commit=False)
            obj.user = self.request.user
            obj.pub_date = timezone.now()
            obj.save()
    
  3. 如果您需要自定义返回网址,则应覆盖get_success_url,而不是从form_valid返回回复。

答案 1 :(得分:3)

执行此操作的另一个方法是将保存行为放在表单上。

class NewPostForm(forms.ModelForm):
    class Meta:
        model = BlogPost
        fields = ('title', 'post_body')

    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('user')
        super(NewPostForm, self).__init__(*args, **kwargs)

    def save(self):
        obj = super(NewPostForm, self).save(commit=False)
        obj.user = self.user
        obj.pub_date = timezone.now()
        obj.save()
        return obj

这很好地将逻辑以更可重用的方式封装在视图之外。

您需要覆盖视图上的get_form_kwargs以传入用户,或使用django-braces中的UserFormKwargsMixin类,以确保user通过初始化表单时的视图。

请注意,django-braces还提供了UserKwargModelFormMixin您可以在表单上使用而不是覆盖__init__()。如果你采取括号方法,你最终会得到这样的东西:

<强> forms.py

class NewPostForm(UserKwargModelFormMixin, forms.ModelForm):
    class Meta:
        model = BlogPost
        fields = ('title', 'post_body')

    def save(self):
        obj = super(NewPostForm, self).save(commit=False)
        obj.user = self.user
        obj.pub_date = timezone.now()
        obj.save()
        return obj

<强> views.py

class NewPostView(UserFormKwargsMixin, generic.edit.CreateView):
    model = BlogPost
    form_class = NewPostForm

答案 2 :(得分:0)

您可以覆盖form_valid,然后在return语句中调出super().form_valid(form)。您不必担心保存表单,保存对象或返回HttpResponseRedirtect。所有这些都将由超级处理。您所需要做的就是设置要在幕后控制的form.instance的值。

views.py

class NewPostView(generic.edit.CreateView):
    model = BlogPost
    form_class = NewPostForm

    def form_valid(self, form):
        form.instance.user = self.request.user
        form.instance.pub_date = timezone.now()
        return super().form_valid(form)

这是在幕后添加值的一种干净方法,与添加值相比,您不需要做更多的逻辑。您可以在django文档中找到对该逻辑的引用:

https://docs.djangoproject.com/en/2.1/topics/class-based-views/generic-editing/#models-and-request-user


理想情况下,对于pub_date,您希望将其设置为使用auto_now_add自动记录创建时间的字段。

models.py

class BlogPost(models.Model):
    title = models.CharField(max_length=200)
    post_body = models.TextField()
    user = models.ForeignKey(User)
    pub_date = models.DateTimeField('published', auto_add_now=True)

    def get_absolute_url(self):
        return u'/entries/%d' % self.id

您可以在下面的链接上找到DateTimeField上的文档及其对auto_add_now参数的引用。它还有一个auto_add,如果您需要一个last_edited_date字段,则每次修改条目时都会更新。

https://docs.djangoproject.com/en/2.2/ref/models/fields/#django.db.models.DateTimeField


此外,您的用户有可能在不登录的情况下访问该表单。您可能需要处理未设置self.request.user的情况,也可以使用LoginRequiredMixin仅允许已登录的用户访问此表单。使用mixin让我过得更轻松。

具有上述所有更改的结果view.py如下所示。

views.py

from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import CreateView

from .forms import NewPostForm
from .models import BlogPost

class NewPostView(LoginRequiredMixin, CreateView):
    model = BlogPost
    form_class = NewPostForm

    def form_valid(self, form):
        form.instance.user = self.request.user
        return super().form_valid(form)

答案 3 :(得分:0)

您还可以在模型中将默认值设置为default = timezone.now(),这样,无论何时创建对象,它都会自动花费当前时间。