7

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

4 回答 4

12

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

视图.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

模型.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所有更改的结果如下所示。

视图.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)
于 2019-10-02T03:51:46.707 回答
8

这正是它的完成方式,除了您的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. 如果您需要自定义返回 URL,您应该覆盖get_success_url,而不是从form_valid.

于 2013-09-27T13:32:02.323 回答
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视图以将用户传入,或使用UserFormKwargsMixin类 fromdjango-braces以确保user在初始化表单时被视图传递。

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

表格.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

视图.py

class NewPostView(UserFormKwargsMixin, generic.edit.CreateView):
    model = BlogPost
    form_class = NewPostForm
于 2013-09-27T13:35:35.493 回答
0

您还可以像default = timezone.now()在模型中一样提供默认值,因此无论何时创建对象,它都会自动采用当前时间。

于 2020-12-17T16:20:02.883 回答