djangodjango-formshtmx

Attribute Error, 'WSGIRequest' object has no attribute 'get' Django-HTMX


I'm building a homepage that includes a contact-us form. This contact-us form element should be reusable across multiple pages, so I've used HTMX to try incorporate. However am receiving the following error, which I can't make sense of because I'm submitting POST data.

Request Method: POST
Request URL: http://localhost:8000/contactus

Django Version: 4.2.13
Python Version: 3.9.6
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'debug_toolbar',
 'accounts',
 'dashboard']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'debug_toolbar.middleware.DebugToolbarMiddleware']


Traceback (most recent call last):
  File "/Users/robrobrob/Desktop/Projects/Trolleys/venv/lib/python3.9/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
  File "/Users/robrobrob/Desktop/Projects/Trolleys/venv/lib/python3.9/site-packages/django/core/handlers/base.py", line 220, in _get_response
    response = response.render()
  File "/Users/robrobrob/Desktop/Projects/Trolleys/venv/lib/python3.9/site-packages/django/forms/utils.py", line 67, in render
    context = context or self.get_context()
  File "/Users/robrobrob/Desktop/Projects/Trolleys/venv/lib/python3.9/site-packages/django/forms/forms.py", line 322, in get_context
    top_errors = self.non_field_errors().copy()
  File "/Users/robrobrob/Desktop/Projects/Trolleys/venv/lib/python3.9/site-packages/django/forms/forms.py", line 358, in non_field_errors
    return self.errors.get(
  File "/Users/robrobrob/Desktop/Projects/Trolleys/venv/lib/python3.9/site-packages/django/forms/forms.py", line 196, in errors
    self.full_clean()
  File "/Users/robrobrob/Desktop/Projects/Trolleys/venv/lib/python3.9/site-packages/django/forms/forms.py", line 433, in full_clean
    self._clean_fields()
  File "/Users/robrobrob/Desktop/Projects/Trolleys/venv/lib/python3.9/site-packages/django/forms/forms.py", line 440, in _clean_fields
    value = bf.initial if field.disabled else bf.data
  File "/Users/robrobrob/Desktop/Projects/Trolleys/venv/lib/python3.9/site-packages/django/forms/boundfield.py", line 135, in data
    return self.form._widget_data_value(self.field.widget, self.html_name)
  File "/Users/robrobrob/Desktop/Projects/Trolleys/venv/lib/python3.9/site-packages/django/forms/forms.py", line 220, in _widget_data_value
    return widget.value_from_datadict(self.data, self.files, html_name)
  File "/Users/robrobrob/Desktop/Projects/Trolleys/venv/lib/python3.9/site-packages/django/forms/widgets.py", line 297, in value_from_datadict
    return data.get(name)

Exception Type: AttributeError at /contactus
Exception Value: 'WSGIRequest' object has no attribute 'get'

** Forms.py **

This file contains the form logic

class Contact_Us_Form(forms.Form):
    name = forms.CharField(
        label="Name",
        max_length=60,
        widget=forms.TextInput(attrs={'class': 'form-control'})
    )
    sender = forms.EmailField(
        widget=forms.TextInput(attrs={'class': 'form-control'}), 
        label="Email"
    )
    message_content = forms.CharField(
        widget=forms.Textarea(attrs={'class': 'form-control'}),
        label="Your message"
    )

** Views.py **

for the form element

def contact_us_response(request):
    if request.method == "POST":
        form = Contact_Us_Form(data=request.POST)
        if form.is_valid():
            return redirect("partials/success.html")
        else:
            return HttpResponse("Something's broke")
    elif request.method == "GET":
        form = Contact_Us_Form()
        return render(request, "partials/contact.html", {"form": form})
    else:
        form = Contact_Us_Form()
        return HttpResponse("Method not allowed", status=405)

** Views.py **

for the homepage

def home(request):
    form = Contact_Us_Form()
    return render(request, 'pages/home.html', {'form': form})

** Urls.py **

urlpatterns = [
 path("", include('dashboard.urls')), # the homepage is rendered via separate urls.py on the 'dashboard' app's urls.py
 path('contactus', Contact_Us_Form, name="contact"),
]

** home.html relevant section from homepage **

 <form id="contact-us-form" hx-post="{% url 'contact' %}" hx-target="#contact-us-form">
{{ form }}
<button type="submit" class="btn btn-secondary w-20" >Submit</button>
</form>

** contact.html **

<form>
    <div class="mb-3">
        <label for="name" class="form-label d-inline">Name <p class="text-secondary d-inline">*</p></label>
        {{ form.name }}
    </div>
    <div class="mb-3">
        <label for="email" class="form-label">Email <p class="text-secondary d-inline">*</p></label>
        {{ form.sender }}
    </div>
    <div class="mb-3">
        <label for="message" class="form-label">How can we help you? <p class="text-secondary d-inline">*</p></label>
        {{ form.message_content }}
        <p class="small text-secondary">We'll hold your details in accordance with our Privacy Policy. 
            This site is protected by reCAPTCHA and the <a href="https://policies.google.com/privacy">Google Privacy Policy</a> and <a href="https://policies.google.com/terms">Terms Of Service</a> apply.</p>
    </div>
    <div class="d-flex justify-content-end">
    <button type="submit" class="btn btn-secondary w-20">Submit</button>
    </div>
</form>

My troubleshooting process Don't understand why POST data is throwing a GET error. Also don't really understand how WSGI is coming into this picture. It looks like similar errors have been raised on SO before without resolution.


Solution

  • You should link the path to the view so contact_us_response, not the form (Contact_Us_Form`):

    urlpatterns = [
        path(
            '', include('dashboard.urls')
        ),  # the homepage is rendered via separate urls.py on the 'dashboard' app's urls.py
        path('contactus', contact_us_response, name='contact'),
    ]