8

I'm practicing django Class-Based-View with a basic blog application. For some reason, however, the CreateView for my Post model is not saving the post inside the database.

models.py

class Post(models.Model):
    user = models.ForeignKey(User)
    post_title = models.CharField(max_length=200)
    post_content = models.CharField(max_length=500)
    post_date = models.DateTimeField('date posted')

forms.py

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        exclude = ('user', 'post_date')

views.py

class PostCreate(CreateView):
    template_name = 'app_blog/post_save_form.html'
    model = Post
    form_class = PostForm

    def form_valid(self, form):
        form.instance.user = self.request.user
        form.instance.post_date = datetime.now()
        return super(PostCreate, self).form_valid(form)

It displays content without generating any error, but when I check the admin page, the post created by the CreateView is not saved in the database.. Any idea..??

Thanks

karthikr
  • 97,368
  • 26
  • 197
  • 188
user2492270
  • 2,215
  • 6
  • 40
  • 56
  • try doing `form.save()` first then call the super !!! if this helps – Manuj Rastogi Jul 24 '13 at 06:54
  • I tried your code and it works for me. There is something else. After save it's redirect you to posts list or post form again? – sneawo Jul 24 '13 at 07:20
  • 2
    `form.save()` should be called by `super` anyways since he is using a ModelForm – Diego Navarro Jul 24 '13 at 08:15
  • What happens if you did not fill in the form correctly, such as leaving the post_content or post_title out? Does it display any error? As 'user' is a required field, I doubt if the form_valid() is ever called or not. – Raymond Tau Jul 24 '13 at 18:34

4 Answers4

8

One tip: don't use exclude when defining forms, use fields, is more secure and the recommended way to do it.

The redirect is defined by get_success_url method. If you have in your model the method get_absolute_url CreateView will redirect to that URL, otherwise you can always override get_success_url in your view.

Using get_absolute_url:

class Post(models.Model):
    user = models.ForeignKey(User)
    post_title = models.CharField(max_length=200)
    post_content = models.CharField(max_length=500)
    post_date = models.DateTimeField('date posted')

    @permalink
    def get_absolute_url(self):
        return ('myurlname', (), {'myparam': something_useful})

Using get_success_url:

class PostCreate(CreateView):
    template_name = 'app_blog/post_save_form.html'
    model = Post
    form_class = PostForm

    def form_valid(self, form):
        form.instance.user = self.request.user
        form.instance.post_date = datetime.now()
        form.save()
        return super(PostCreate, self).form_valid(form)

    def get_success_url(self):
        return reverse('myurlname', args=(somethinguseful,))

I think you will find this page very useful when working with CBVs: http://ccbv.co.uk/projects/Django/1.5/django.views.generic.edit/CreateView/

Diego Navarro
  • 9,316
  • 3
  • 26
  • 33
5

the problem is that you are excluding fields that are mandatory, so it won't pass through your form validation.

You should pass this fields hidden with some default value, let the use fill them, set them to null=True or populate them before you access form_valid

  • @Diego Navrro Why don't use the exclude? That may cause the mislead for beginner. I think we can use either of them, depends on your model's field and form's fields. If we want to use few field on form, use 'fields' in its meta class. Otherwise if we don't want to use some fields while use many on form, we can use exlude so that we don't have to type in much of the texts. – Dat TT May 24 '20 at 04:13
  • @DatTT 3 years later, but just to clarify for future readers: I believe the idea is you're avoiding accidentally exposing sensitive model fields that might be introduced later when you do `exclude` instead of `fields`, since you might forget to add those new fields to the exclusion list. – Luca Bezerra May 28 '23 at 20:09
1

I came across this question today after many years but those answer seems not correctly.

The main issue here is the form.instance is None for CreateView. So my approach is below as suggestion form django docs:

def form_valid(self, form):
    instance = form.save(commit=False)
    instance.user = self.request.user
    instance.post_date = datetime.now()
    instance.save()
    return redirect(self.get_success_url())
Dat TT
  • 2,850
  • 2
  • 16
  • 18
-1

I think this is a simple case of not calling form.save(). When the form is validated, all of the checks are done, but it doesn't actually save the object in the database. To do that, you explicitly need to tell it to, via the save() method.

So you want:

class PostCreate(CreateView):
    template_name = 'app_blog/post_save_form.html'
    model = Post
    form_class = PostForm

    def form_valid(self, form):
        form.instance.user = self.request.user
        form.instance.post_date = datetime.now()
        form.save()
        return super(PostCreate, self).form_valid(form)
Daniel Rosenthal
  • 1,386
  • 4
  • 15
  • 32