I'm trying to develop a simple Django app of a contact form and a thanks page. I'm not using Django 'admin' at all; no database, either. Django 3.2.12. I'm working on localhost using python manage.py runserver
I can't get the actual form to display at http://127.0.0.1:8000/contact/contact; all I see is the submit button from /contact/contactform/templates/contact.html:
Static files load OK: http://127.0.0.1:8000/static/css/bootstrap.css
The thanks.html page loads OK: http://127.0.0.1:8000/contact/thanks
This is the directory structure:
/contact/contact/settings.py
import os
from pathlib import Path
from dotenv import load_dotenv
load_dotenv()
DEBUG=True
BASE_DIR = Path(__file__).resolve().parent.parent
ALLOWED_HOSTS = ['127.0.0.1'] + os.getenv('REMOTE_HOST').split(',')
SECRET_KEY = os.getenv('SECRET_KEY')
EMAIL_USE_TLS = os.getenv('EMAIL_USE_TLS')
EMAIL_HOST = os.getenv('EMAIL_HOST')
EMAIL_PORT = os.getenv('EMAIL_PORT')
EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD')
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'contactform.apps.ContactformConfig',
]
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',
]
ROOT_URLCONF = 'contact.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'contact.wsgi.application'
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'static/'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
/contact/contact/urls.py
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
]
from django.urls import include
urlpatterns += [
path('contact/', include('contactform.urls')),
]
from django.conf import settings
from django.conf.urls.static import static
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
/contact/contactform/urls.py
from django.urls import path
from . import views
app_name = 'contactform'
urlpatterns = [
path('thanks/', views.thanks, name='thanks'),
path('contact/', views.contact, name='contact'),
]
/contact/contactform/views.py
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from django.http import HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from contactform.forms import ContactForm
from contact.settings import EMAIL_HOST_USER, EMAIL_PORT, EMAIL_HOST_PASSWORD, EMAIL_HOST
def thanks(request):
return render(request, 'thanks.html', {})
def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
form_data = form.cleaned_data
msg = MIMEMultipart()
msg['From'] = EMAIL_HOST_USER
msg['To'] = EMAIL_HOST_USER
msg['Subject'] = f'Personal site: {form_data["subject"]}'
message = f'Name: {form_data["name"]}\n' \
f'Email address: {form_data["email_address"]}\n\n' \
f'{form_data["message"]}'
msg.attach(MIMEText(message))
with smtplib.SMTP(EMAIL_HOST, EMAIL_PORT) as server:
server.ehlo()
server.starttls()
server.login(EMAIL_HOST_USER, EMAIL_HOST_PASSWORD)
server.sendmail(EMAIL_HOST_USER, EMAIL_HOST_USER, msg.as_string())
return HttpResponseRedirect('/thanks')
else:
form = ContactForm()
return render(request, 'contact.html')
/contact/contactform/models.py
from django.urls import reverse
/contact/contactform/apps.py
from django.apps import AppConfig
class ContactformConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'contactform'
/contact/contactform/forms.py
from django import forms
class ContactForm(forms.Form):
name = forms.CharField(required=True, widget=forms.TextInput(
attrs={'class': 'form-control', 'maxlength': '100'}
))
email_address = forms.EmailField(required=True, widget=forms.EmailInput(
attrs={'class': 'form-control', 'maxlength': '100'}
))
subject = forms.CharField(required=True, widget=forms.TextInput(
attrs={'class': 'form-control', 'maxlength': '100'}
))
message = forms.CharField(required=True, widget=forms.Textarea(
attrs={'class': 'form-control', 'maxlength': '1000', 'rows': 8}
))
/contact/contactform/templates/contact.html
<h2>Form</h2>
<form action="/contact/" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Send</button>
</form>
Update 2/20/22
This views.py now works and shows the contact form; the remaining issuse is when the form is completed, the redirect to the thanks page throws a 404.
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from django.shortcuts import redirect
from django.shortcuts import render, get_object_or_404
from contactform.forms import ContactForm
from contact.settings import EMAIL_HOST_USER, EMAIL_PORT, EMAIL_HOST_PASSWORD, EMAIL_HOST
def thanks(request):
return render(request, 'thanks.html', {})
def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
form_data = form.cleaned_data
msg = MIMEMultipart()
msg['From'] = EMAIL_HOST_USER
msg['To'] = EMAIL_HOST_USER
msg['Subject'] = f'Site Email'
message = f'Name: {form_data["name"]}\n' \
f'Email address: {form_data["email_address"]}\n\n' \
f'{form_data["message"]}'
msg.attach(MIMEText(message))
with smtplib.SMTP(EMAIL_HOST, EMAIL_PORT) as server:
server.ehlo()
server.starttls()
server.login(EMAIL_HOST_USER, EMAIL_HOST_PASSWORD)
server.sendmail(EMAIL_HOST_USER, EMAIL_HOST_USER, msg.as_string())
return redirect('contactform:thanks')
else:
form = ContactForm()
return render(request, 'contact.html', { "form": form })
Error screen:
4 Answers 4
The form does not display as you are not passing it into your template. You can do this instead in the contact view:
return render(request, 'contact.html', {
'form': form
})
EDIT:
If you are getting 'return' outside function error, you can do this in your contact view.
def contact(request):
form = ContactForm() # Before if condition
You can remove the else condition.
EDIT 2:
This should be your contact view.
def contact(request):
form = ContactForm()
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
form_data = form.cleaned_data
msg = MIMEMultipart()
msg['From'] = EMAIL_HOST_USER
msg['To'] = EMAIL_HOST_USER
msg['Subject'] = f'Personal site {form_data["subject"]}'
message = f'Name: {form_data["name"]}\n' \
f'Email address: {form_data["email_address"]}\n\n' \
f'{form_data["message"]}'
msg.attach(MIMEText(message))
with smtplib.SMTP(EMAIL_HOST, EMAIL_PORT) as server:
server.ehlo()
server.starttls()
server.login(EMAIL_HOST_USER, EMAIL_HOST_PASSWORD)
server.sendmail(EMAIL_HOST_USER, EMAIL_HOST_USER, msg.as_string())
return HttpResponseRedirect('/thanks')
else:
return HttpResponseRedirect(reverse('contactform:contact'))
return render(request, 'contact.html', {
'form': form
})
30 Comments
return render(request, 'contact.html')? I get SyntaxError: 'return' outside functionform = ContactForm() inside the else condition, you can use this statement before the if condition in your contact view.return render(request, 'contact.html') with return render(request, 'contact.html', { "form": form }). That's it. This answer is correct (which is why I don't give this as my own), although the essential point can be lost in a bunch of extraneous detail - which I assume came from your subsequent complaints, which I guess are due to changing the indentation in some strange way. You just need to add that dictionary as a third argument to your return statement - nothing else.return HttpResponseRedirect(reverse('contactform:thanks')), or building on the comment by @AMG, return redirect('contactform:thanks').Your form action needs to point to
<form action="/contact/contact/"....
or better
<form action="{% url 'contactform:contact' %}" ...)
Comments
After, you updated the project on Update 2/20/22
You forgot to register the default path for contactform app in url_patterns list.
your /contact/contact/urls.py
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
]
from django.urls import include
urlpatterns += [
path('contact/', include('contactform.urls')),
]
from django.conf import settings
from django.conf.urls.static import static
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
It should be :
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('contactform.urls'))
]
from django.urls import include
urlpatterns += [
path('contact/', include('contactform.urls')),
]
from django.conf import settings
from django.conf.urls.static import static
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
Try this and tell if it works?
you have to add path('', include('contactform.urls')) in urls.py of main project.
As, if we remove all things by testing it very simply and updating views.py through following code:
/contact/contactform/views.py
from contactform.forms import ContactForm
from django.http import HttpResponseRedirect
from django.shortcuts import render
def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
form_data = form.cleaned_data
return HttpResponseRedirect('/thanks/')
else:
form = ContactForm()
return render(request, 'contact.html', {'form': form})
def thanks(req):
return render(req, 'thanks.html')
The above code worked very efficiently.So, i drew the conclusion that it will work.
Try it.
And one thing, i would like to focus on is, always use the following pattern while making templates file.
Appname/templates/Appname/anyfile.html
Like in your case:
contactform/templates/contactform/contact.html
You have missed the third step after templates, well its not necessary, but if you do it is a good practice.
You have forgot / too while redirecting in your views.py.
It should be return HttpResponseRedirect('/thanks/')
rather than return HttpResponseRedirect ('/thanks')
Also, remove the action attribute of form so that it can redirect through HttpResponseRedirect () method.
4 Comments
urlpatterns = blocks in /contact/contact/urls.py ?contactform by default, i.e, any path which you type in url if it is blank then its next urls will be decided through that particular app, like in your case it is contactform. But, what is the issue you are facing right now?Your Django form doesn't display because you are not passing the form to your html template. you have to pass it.
return render(request, 'contact.html','form':form)
try this and it should be work:
def contact(request):
form = ContactForm()
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
form_data = form.cleaned_data
msg = MIMEMultipart()
msg['From'] = EMAIL_HOST_USER
msg['To'] = EMAIL_HOST_USER
msg['Subject'] = f'Personal site {form_data["subject"]}'
message = f'Name: {form_data["name"]}\n' \
f'Email address: {form_data["email_address"]}\n\n' \
f'{form_data["message"]}'
msg.attach(MIMEText(message))
with smtplib.SMTP(EMAIL_HOST, EMAIL_PORT) as server:
server.ehlo()
server.starttls()
server.login(EMAIL_HOST_USER, EMAIL_HOST_PASSWORD)
server.sendmail(EMAIL_HOST_USER, EMAIL_HOST_USER, msg.as_string())
return HttpResponseRedirect('/thanks')
else:
return HttpResponseRedirect('contactform:contact')
return render(request, 'contact.html','form':form)
forms.py
from django import forms
from .models import *
class ContactForm(forms.ModelForm):
class Meta:
model = your_model_name
fields = '__all__'
11 Comments
return HttpResponseRedirect(reverse('contactform:contact')) but it should be return HttpResponseRedirect('contactform:contact')Explore related questions
See similar questions with these tags.
return render(request, 'contact.html')You aren't passingformin the template context.path('', include ("contactform.urls"))? or giving after this?