Django CreateView:如何在保存时执行操作

45
我正在使用自定义的CreateView(CourseCreate)和UpdateView(CourseUpdate)保存和更新一个Course。我希望在保存Course时执行一个操作。我将创建一个新的多对多关系,将新课程的讲师与用户之间建立联系(如果还不存在)。因此,我想将Course保存为course,然后使用course.faculty来创建该新关系。最好的实现位置在哪里?
我试图在视图中的form_valid中完成这个操作,但是在CourseCreate中尝试访问form.instance.faculty会出现错误,因为course还没有被创建(在CourseCreate中)。错误信息如下所示:“Course:…”需要在使用此多对多关系之前为字段“course”设置值。在CourseUpdate中也无法工作。Assists关系未被创建。我应该在表单中进行尝试吗?但我不确定如何将用户信息传递给表单。谢谢。
models.py
class Faculty(models.Model):
    last_name = models.CharField(max_length=20)

class Course(models.Model):
    class_title = models.CharField(max_length=120)
    faculty = models.ManyToManyField(Faculty)

class UserProfile(models.Model):
    user = models.OneToOneField(User)
    faculty = models.ManyToManyField(Faculty, through='Assists')

class Assists(models.Model):
    user = models.ForeignKey(UserProfile)
    faculty = models.ForeignKey(Faculty)

视图.py

class CourseCreate(CreateView):
    model = Course
    template_name = 'mcadb/course_form.html'
    form_class = CourseForm
    def form_valid(self, form):
        my_course = form.instance
        for f in my_course.faculty.all():
            a, created = Assists.objects.get_or_create(user=self.request.user.userprofile, faculty=f)
        return super(CourseCreate, self).form_valid(form)

class CourseUpdate(UpdateView):
    model = Course
    form_class = CourseForm
    def form_valid(self, form):
        my_course = form.instance
        for f in my_course.faculty.all():
            a, created = Assists.objects.get_or_create(user=self.request.user.userprofile, faculty=f)
        return super(CourseUpdate, self).form_valid(form)

尝试将 my_course = form.instance 替换为 my_course = form.save(commit=False) - Vingtoft
@Vingtoft,谢谢。我尝试了您提供的方法,但是当我在创建新课程时单击保存按钮时,仍会看到相同的错误:“在使用此多对多关系之前,课程对象需要为“course”字段设置值。”我不太明白“课程”的提及来自何处……我确实有一个名为Course的类,但它没有名为“course”的字段,我的视图中也没有任何“course”的提及。 - Carrie
4个回答

96

form_valid()方法适用于CreateViewUpdateView,它会保存表单并重定向到成功的URL。由于您想在对象保存和重定向之间进行操作,因此无法使用return super()

第一种选择是不调用super(),并在视图中复制这两行代码。这样做的优点是非常清楚明了。

def form_valid(self, form):
    self.object = form.save()
    # do something with self.object
    # remember the import: from django.http import HttpResponseRedirect
    return HttpResponseRedirect(self.get_success_url())

第二种选择是继续调用super(),但在更新关系之后再返回响应。这样做的优点是不会重复super()中的代码,但缺点是除非您熟悉super()的功能,否则很难理解正在发生什么。
def form_valid(self, form):
    response = super(CourseCreate, self).form_valid(form)
    # do something with self.object
    return response

4
哇,谢谢,这个方法可行。(我选择了第一个选项以便透明)。我花了很多时间查阅文档,但仍然觉得很难自己解决这样的问题。非常感谢@Alasdair! - Carrie
2
个人而言,我也更喜欢第一种选择。很高兴它有所帮助 :-) - Alasdair
@Alasdair:你觉得重写form_valid()并在其中更新form.instance怎么样?这个instance是使用POST数据作为form.is_valid的结果生成的,在form_valid()之前调用。当在form_valid()中调用form.save()时,这个instance稍后会被用来设置object - Rahul Verma
@batMan 如果我没记错的话,对于 CreateViewform.instanceNone。你可以在 get_form_kwargs 中将 instance 设置为 Course(),但是这对于这个特定的问题并没有太大帮助,因为在设置多对多字段的值之前必须先保存实例。 - Alasdair

3

我建议使用Django的Signal,这是在模型发生某些情况时触发的操作,例如保存更新。这样您的代码就会保持整洁(表单处理中没有业务逻辑),并且您可以确保它只在保存之后触发。

#views.py
from django.dispatch import receiver
...

@receiver(post_save, sender=Course)
def post_save_course_dosomething(sender,instance, **kwargs):
    the_faculty = instance.faculty
    #...etc  

1
这种情况下不起作用,因为您无法使用post_save信号访问M2M字段,例如faculty,请参见此答案https://dev59.com/wmAg5IYBdhLWcg3wQ5El#23796604。 - fasouto

2

如果您需要在调用保存函数时修改Course对象,请使用False,在更改后保存对象即可。

def form_valid(self, form):
    self.object = form.save(False)
    # make change at the object
    self.object.save()

    return HttpResponseRedirect(self.get_success_url())

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接