How to test django (3.0) CreateView's 'form_valid' method and parameters with a test client?

Viewed 698

Some info

I'm overriding the form_valid method (for one reason or another).

What I'm trying to do

I want to test the form_valid instance, and specifically its arguments. To do so, I'm using Django's test client.

Some code

models.py:

class MyModel(models.Model):
    my_model_text = models.CharField(max_length=100)
    my_model_date = models.DateTimeField(
        'my model date',
        auto_now_add=True)

views.py:

class CreateMyModelView(LoginRequiredMixin, generic.edit.CreateView):

    model = MyModel
    template_name = 'myapp/create-my-model.html'
    form_class = CreateMyModelForm

    def post(self, request=None, *args, **kwargs):

        # do something here
        form = self.get_form()
        if form.is_valid():
            return self.form_valid(form, request)
        else:
            return self.form_invalid(form)

    def form_valid(self, form, request):

        # do something else here
        my_model_text = form.cleaned_data['my_model_text']
        MyModel.objects.create(my_model_text=my_model_text)

        return redirect(reverse('myapp:mypage'))

forms.py:

class CreateMyModelForm(forms.ModelForm):

    class Meta:
        model = MyModel
        fields = ['my_model_text']

tests.py:

class CreateMyModelViewTests(TestCase):

    @classmethod
    def setUpTestData(cls):

        cls.my_auth_user = User.objects.create(
            username='my_auth_user')
        cls.my_auth_user.set_password('my_auth_password')
        cls.my_auth_user.save()

    def test_form_valid(self):

        client = self.client
        client.login(
            username='my_auth_user',
            password='my_auth_password')
        # post response ? #

Question

How can I test the arguments that form_valid gets with the test-client (not an instance of the form object, but the form_valid method itself)?

1 Answers

Since CBVs store the current request as self.request, you don't need to change the signature of form_valid.

I think your view can/should be simplified to something like this (and as you can see, there's just commented-out stuff in form_valid() too, so you can elide it entirely unless you need to modify the instance using e.g. the request user).

class CreateMyModelView(LoginRequiredMixin, CreateView):
    model = MyModel
    template_name = 'myapp/create-my-model.html'
    form_class = CreateMyModelForm
    success_url = reverse_lazy('myapp:mypage')

    def form_valid(self, form):
        # You can modify the object-to-be-saved before saving, e.g.
        # form.instance.creator = self.request.user

        # The CreateView implementation just calls `form.save()` and redirects away,
        # so let's reuse that.
        return super().form_valid(form)
Related