Search code examples
pythondjangodjango-formsdjango-testing

Can I somehow call logic from form_valid() in Django tests?


I am trying to test form for Post model creation in my simple forum application. The problem I am having is that after I try to save the form in tests I get an error NOT NULL constraint failed: forum_web_post.user_id because I am assigning the user in the form_valid() method in the view. The user is not passed via the form since the user that creates the post is the signed in user that sent the request.

models.py

class Post(models.Model):
    category = models.ForeignKey(Category, on_delete=models.PROTECT)
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    title = models.CharField(max_length=100)
    text = models.TextField()
    created_at = models.DateTimeField(auto_now=True)

user is imported form django.contrib.auth.models and Category model looks like this.

class Category(models.Model):
    name = models.CharField(max_length=100)
    created_at = models.DateTimeField(auto_now=True)

in views.py after the user submits the form he is the redirected to his profile page

views.py

class PostCreate(generic.CreateView):
    model = Post
    form_class = PostForm
    template_name = 'forum_web/post_edit.html'

    def form_valid(self, form):
        post = form.save(commit=False)
        post.user = models.User.objects.get(id=self.request.user.id)
        post.save()
        return HttpResponseRedirect(reverse_lazy('forum:user_detail', kwargs={'pk': self.request.user.id}))

forms.py

class PostForm(ModelForm):
    class Meta:
        model = Post
        fields = ['category', 'title', 'text']

tests.py

    def test_post_form_create_should_pass(self):
        # this dict is in setUp() as well as the test_category but for simplicity I will include it in this method
        post_data = {
        'category': self.test_category.pk,
        'title': 'test_post',
        'text': 'test_post_text'
        }
        post_count = Post.objects.count()
        form = PostForm(data=self.post_data)
        self.assertTrue(form.is_valid())
        form.save()
        self.assertEqual(post_count + 1, Post.objects.count())

any help would be appreciated!


Solution

  • You are just testing the form. So it doesn't make sense to call a view function in your test. You should manually assign a user to the form's instance using form.instance.user = ... before saving the form in your test, since that's the way the form should be used.

    In addition, you should test your view by actually logging in a user and posting the request to the PostCreate view. Here you're testing that the view correctly saves the form (and that your form_valid() method works correctly).

    Note: post.user = self.request.user would be better than re-fetching the user from the database using the id. It's redundant.