0

I'm trying to test some form logic in my views.py and need to simply pass a form to that view in order to do that but I can't figure out why it isn't receiving a valid form when passed using a Client().post from my tests. When using the form from the browser normally it works fine.

I'm using Django test Client submitting a form with a POST request and How should I write tests for Forms in Django? as guidance with no luck after looking at many different resources.

The errors from form.errors shows

name - This field is required

address - This field is required

However my form.data shows

<QueryDict: {"'name': 'foo bar', 'address': '2344 foo st'}": ['']}>

The working live form has the form.data as:

<QueryDict: {'csrfmiddlewaretoken': ['htR...Rel', 'htR...Rel'], 'name': ['test'], 'address': ['test'],'submit': ['Submit_Form']}>

I've played around with getting a "csrfmiddlewaretoken" but I don't feel like that the problem. I've also tried setting the .initial and played around with content type. Is the form.data not what is looked at? form.is_valid() is True before the post to the view.py. Thanks in advance for the help!

My tests code is:

@pytest.mark.django_db def test_add_bar_with_logged_in_user_form(setUpUser, django_user_model):

from ..models.forms import BarForm
from django.test import Client

bar_name = "foo bar2"
address = "2344 foo st"

user = setUpUser 

client = Client()
client.force_login(user=user)

client.login(username=user.username, password=user.password, email=user.email)

form = BarForm()

form_data = {
    "name": bar_name,
    "address": address,
}

form = BarForm(data=form_data)

assert form.is_valid() is True

response = client.post(urls.reverse("add_bar"), form.data, content_type="application/x-www-form-urlencoded", follow=True)

assert response.status_code == 200

The relevant portion of views.py is:

def add_bar(request):

# If this is a POST then the user has submitted the form to add a new bar
if request.method == "POST":

    active_user = auth.get_user(request)

    # Only logged in users should be able to submit
    if not active_user.is_authenticated:
        messages.error(request, "Must be logged in to do that")
        return HttpResponseForbidden("Authentication required")

    form = BarForm(request.POST)

    logger.info(f"*=-===-= form.data : {form.data}")

    if form.is_valid():
        logger.info(f"*=-===-= form.data : {form.data}")
        cleaned_data = form.cleaned_data

        created_by_id = active_user.id

        bar_new = Bar.objects.create(**cleaned_data, submitted_by=active_user)

        messages.success(
            request,
            f" - Thanks! Bar submited.",
        )
        return redirect("index")
    
    elif form.errors:

        errors = form.errors
        messages.error(request, "Form is invalid")

        logger.info(f"** test_add_bar_with_logged_in_user -> form.errors: {errors}")
        for error in form.errors:
            logger.info(f"** test_add_bar_with_logged_in_user -> error: {error}")
        logger.info(f"*=-===-= form.data : {form.data}")
        return redirect("index")
    elif form.non_field_errors():
        for error in form.non_field_errors():
            logger.info(f"** test_add_bar_with_logged_in_user -> error: {error}")
        return False
LtDan33
  • 301
  • 3
  • 11

1 Answers1

0

I pass data via login form but without the content_type="application/x-www-form-urlencoded and it works fine. However; when I added the content_type it failed. This is how I pass the data:

new_login = LoginForm(
    data={'username': 'someone', 'password': 'blah'},
)
self.assertEqual(new_login.is_valid(), True)
response = self.client.post(
    path='/accounts/login/',
    data=new_login.data,
    follow=True,
)

I also have a check inside the view to print if form.is_valid() and it is. The default content_type is MULTIPART_CONTENT as you can see in this older source code, which handles form data.

I did run into a problem when trying to pass multiple forms to a view. In that case, I found that instead of trying to send forms at all, I merely passed a dictionary of the keyword values to data in the post and the forms inside view validated just fine.

I hope this helps!

stujar
  • 5
  • 4