Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django CreateView: How to perform action upon save

Tags:

django

I'm using a custom CreateView (CourseCreate) and UpdateView (CourseUpdate) to save and update a Course. I want to take an action when the Course is saved. I will create a new many-to-many relationship between the instructor of the new course and the user (if it doesn't already exist).

So, I want to save the Course as course, and then use course.faculty to create that new relationship. Where is the best place to make this happen?

I'm trying to do this in form_valid in the views, but I'm getting errors when trying to access form.instance.faculty bc the course isn't created yet (in CourseCreate). The error message is like:

"Course: ..." needs to have a value for field "course" before this many-to-many relationship can be used.

It's also not working in CourseUpdate. The Assists relationship is not created. Should I be trying this in the Form? But I'm not sure how to get the user info to the Form. Thank you.

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) 

views.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) 
like image 254
Carrie Avatar asked Oct 07 '15 17:10

Carrie


2 Answers

The form_valid() method for CreateView and UpdateView saves the form, then redirects to the success url. It's not possible to do return super(), because you want to do stuff in between the object being saved and the redirect.

The first option is to not call super(), and duplicate the two lines in your view. The advantage of this is that it's very clear what is going on.

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()) 

The second option is to continue to call super(), but don't return the response until after you have updated the relationship. The advantage of this is that you are not duplicating the code in super(), but the disadvantage is that it's not as clear what's going on, unless you are familiar with what super() does.

def form_valid(self, form):     response = super(CourseCreate, self).form_valid(form)     # do something with self.object     return response 
like image 137
Alasdair Avatar answered Sep 28 '22 04:09

Alasdair


I would suggest to use Django's Signal. That is an action that gets triggered when something happens to a Model, like save or update. This way your code stays clean (no business logic in the form-handling), and you are sure that it only gets triggered after save.

#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   
like image 35
Alex Avatar answered Sep 28 '22 05:09

Alex