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个字段 - title
,post_body
,user
和pub_date
。用户应该可以填写title
和post_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方法有效,但我觉得它应该怎么做呢?是否有更好的地方将这些行动纳入其中?这样做的传统方式是什么?
答案 0 :(得分:4)
除了你的form_save
方法(不应该返回响应)之外,这正是它的完成方式:
您可以将auto_now_add
用于pub_date
字段,它会在首次创建模型时添加当前时间戳(为您省去麻烦)。
您还可以在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()
如果您需要自定义返回网址,则应覆盖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文档中找到对该逻辑的引用:
理想情况下,对于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()
,这样,无论何时创建对象,它都会自动花费当前时间。