I have set up a fully operational contact form and now I would like to render the users inputs into a html template, so when I receive the email it is easier to read.
How do I do this? Do I link the template below some where, or link the form with in a template? Thank you in advance
def contactPage(request):
if request.method == 'POST':
form = contactForm(request.POST)
if form.is_valid():
subject = "INQUIRY"
body = {
'full_name': form.cleaned_data['full_name'],
'email': form.cleaned_data['email_address'],
'message':form.cleaned_data['message'],
}
message = "\n".join(body.values())
try:
send_mail(subject, message, '', ['email])
except BadHeaderError:
return HttpResponse('Invalid header found.')
return redirect ('thankyou')
form = contactForm()
return render(request, "contact.html", {'form':form})
One approach is to use the html_message parameter in send_mail(). html_message can refer to an html template that will be used to present the message content. Here's what I have working:
forms.py
from django import forms
class ContactForm(forms.Form):
full_name = forms.CharField(max_length=100)
message = forms.CharField(widget=forms.Textarea)
from_email = forms.EmailField()
views.py
from django.core.mail import send_mail
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.template.loader import render_to_string
from django.urls import reverse
from app1.forms import ContactForm
def html_email_view(request):
template = 'email_form.html'
if request.POST:
form = ContactForm(request.POST)
if form.is_valid():
message_plain = render_to_string(
'email.txt',
{
'full_name': form.cleaned_data['full_name'],
'message': form.cleaned_data['message'],
},
)
message_html = render_to_string(
'email.html',
{
'full_name': form.cleaned_data['full_name'],
'message_lines': list(filter(bool, form.cleaned_data['message'].splitlines())),
},
)
send_mail(
subject='INQUIRY',
message=message_plain,
from_email=form.cleaned_data['from_email'],
recipient_list=['fake#email.com'],
html_message=message_html,
)
return HttpResponseRedirect(reverse('app1:html_email_view'))
else:
form = ContactForm()
context = {'form': form}
return render(request, template, context)
email.html
<h1>You've received an email through site.com!</h1>
<div><b>Full name:</b> {{ full_name }}</div>
<hr>
<div><b>Message:</b></div>
{% for line in message_lines %}
<p>{{ line }}</p>
{% endfor %}
email.txt
Message from: {{ full_name }}
--------------------------------
Message:
{{ message }}
--------------------------------
Sending a message through this form, I get one email in my inbox. Here's a screen shot:
A related SO question is: Creating email templates with Django
Note: My answer implements andilabs's answer from that post.
Docs reference: https://docs.djangoproject.com/en/4.1/topics/email/#send-mail
Related
I cannot render the contact.html. Why? I am facing to the following error and description:
Page not found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/contact/
admin/
[name='index']
contact [name='contact']
The current path, contact/, didn't match any of these.
Here is my code:
views.py
def contact(request):
if request.method == 'POST' :
form = ContactForm(request.POST)
if form.is_valid():
form.save()
return render ('thanks.html', {'form':form})
else:
form = ContactForm()
return render(request, 'contact.html', {'form':form})
models.py
from django.db import models
class Contact (models.Model):
name = models.CharField (max_length=100)
email = models.EmailField (max_length=100)
subject = models.CharField (max_length=100)
message = models.TextField (max_length=1000)
forms.py
from django.forms import ModelForm
from .models import ContactModel
class ContactForm (ModelForm):
class Meta:
model = ContactModel
fields = ['name', 'email', 'subject', 'message']
urls.py
urlpatterns = [
path('', views.index, name='index'),
path('contact', views.contact, name='contact'),
]
contact.html
<body>
<div class="container">
<br />
<form action="{% url 'contact' %}" method="post">
{% csrf_token %}
...
<div id="success">
<button type="submit" class="btn btn-outline-dark"
id="sendMessageButton">
Send Message
</button>
</form>
</div>
Are you sure that the form is valid? Repeating the Contact-page indicates that you end up within the else:. That would explain why nothing is saved, as the if: is skipped, and it would explain why the incorrect render() without a request passed is not giving you any issues.
Try printing out the POST-data with print(request.POST) at the top of the view to see if all variables you expect to see are there and that everything is spelled correctly (easy mistake). Compare what is POST:ed with what your form is looking for.
When you get that working, I would consider changing the render to a redirect.
It is also a good idea to add a second else: if the form is not valid. This would look like:
if form.is_valid():
name = form.cleaned_data['name']
email = form.cleaned_data['email']
subject = form.cleaned_data['subject']
message = form.cleaned_data['message']
form.save()
return redirect('your-thankyou-url')
else: #if form is not valid
render(request, 'contact.html', {'form': form})
That would render any errors and make it clear for whomever is entering information that something submitted is faulty.
Best of luck!
This is my urls.py :
from django.urls import path
from newsletter.views import control_newsletter, control_newsletter_list, control_newsletter_detail, control_newsletter_edit
urlpatterns = [
path('newsletter/', control_newsletter, name='control_newsletter'),
path('newsletter_list/', control_newsletter_list, name='control_newsletter_list'),
path('newsletter_detail/<int:pk>/', control_newsletter_detail, name='control_newsletter_detail'),
path('newsletter_edit/<int:pk>/', control_newsletter_edit, name='control_newsletter_edit'),]
and this is my view.py :
def control_newsletter_edit(request, pk):
newsletter = get_object_or_404(Newsletter, pk=pk)
if request.method == 'POST':
form = NewsletterCreationForm(request.POST, instance=Newsletter)
if form.is_valid():
newsletter = form.save()
if newsletter.status == 'Published':
subject = newsletter.subject
body = newsletter.body
from_email = global_settings.EMAIL_HOST_USER
for email in newsletter.email.all():
send_mail(subject=subject, from_email=from_email, recipient_list=[email], message=body,
fail_silently=True)
messages.success(request, 'Your Changes Write Successfully.',
'alert alert-success alert-dismissible')
else:
messages.warning(request, 'SomeThing Went Wrong..',
'alert alert-warning alert-dismissible')
return redirect('control_newsletter_detail', pk=newsletter.pk)
else:
form = NewsletterCreationForm(instance=newsletter)
context = {
'form': form,
}
return render(request, 'control_panel/control_newsletter.html', context)
when I try to access to newsletter_edit/1/
from here in my teplate code :
<div class="col-sm-8">
<div class="col-sm-2">
<a href="{% url 'control_newsletter_edit' pk=newsletter.pk %}">
<button class="btn-success">Edit</button>
</a>
</div>
I faced to this error :
ValueError at /panel/newsletter_edit/1/
The view newsletter.views.control_newsletter_edit didn't return an HttpResponse object. It returned None instead.
I checked my urls.py and my views.py over and over but I cant find out what is my problem.
is any body know why I face to this error?
In addition, I'm sorry for writing mistakes in my question.
and if you need the full of my views.py , pleas tell.
your problem is here if request.method == 'POST': as you can see you're only returning a template when the request method is post, make sure you return something outside the if conditional
You didn't mention what should happen if a request other than HTTP POST comes. It can be done by using a simple else block as below,
def control_newsletter_edit(request, pk):
newsletter = get_object_or_404(Newsletter, pk=pk)
if request.method == 'POST':
form = NewsletterCreationForm(request.POST, instance=Newsletter)
if form.is_valid():
newsletter = form.save()
if newsletter.status == 'Published':
subject = newsletter.subject
body = newsletter.body
from_email = global_settings.EMAIL_HOST_USER
for email in newsletter.email.all():
send_mail(subject=subject, from_email=from_email, recipient_list=[email], message=body,
fail_silently=True)
messages.success(request, 'Your Changes Write Successfully.',
'alert alert-success alert-dismissible')
else:
messages.warning(request, 'SomeThing Went Wrong..',
'alert alert-warning alert-dismissible')
return redirect('control_newsletter_detail', pk=newsletter.pk)
else:
form = NewsletterCreationForm(instance=newsletter)
context = {
'form': form,
}
return render(request, 'control_panel/control_newsletter.html', context)
else:
form = NewsletterCreationForm() # if method not HTTP POST
return render(request, 'control_panel/control_newsletter.html', {"form":form})
Django 2.0
Python 3.6
I am having trouble with a Django form that is not saving the file that is selected through the form; whenever you select a file to upload, I receive the message "This Field is Required.".
I placed a blank=True and a null=True in the Model FileField to get rid of the same, but whenever I attempt to load the html, I get this error: "The 'copydoc' attirbute has no file associated with it."
I would like for a user to be able to log in, create an entry and upload a file along with said entry. Why doesn't the DB accept the file from the form?
Thank you.
views.py:
from django.shortcuts import render, redirect
from .models import notarizer, CustomUser, notarizerCreateForm
# from .forms import notarizerCreateForm
# Create your views here.
def home(request):
t = 'home.html'
return render(request, t)
def page1(request):
t = 'log1/page1.html'
if request.user.is_authenticated:
logger = notarizer.objects.filter(userziptie=request.user).order_by('-date')
return render(request, t, {'logger': logger})
else:
return redirect(home)
def create_entry(request):
createPath = 'log1/create_entry.html'
if request.method == 'POST':
if request.method == 'FILES':
form = notarizerCreateForm(request.POST, request.FILES)
if form.is_valid():
instance =notarizerCreateForm(
file_field=request.FILES['file']
)
instance.save()
else:
print(form.errors)
else:
form = notarizerCreateForm(request.POST)
if form.is_valid():
form.save()
else:
print(form.errors)
else:
form = notarizerCreateForm()
return render(request, createPath, {'form': form})
create_entry.html:
{% extends "base.html" %}
{% block placeholder1 %}
<div class="form-holder">
<form name="form" enctype="multipart/form-data" method="POST"
action="/create_entry/" >
{% csrf_token %}
{{ form.as_table }}
<input type="submit"/>
</form>
</div>
{% endblock %}
models.py:
from django.db import models
from users.models import CustomUser
from django.forms import ModelForm
# Create your models here.
class notarizer(models.Model):
date = models.DateField(auto_now_add=True)
docName = models.CharField(max_length=25, null=False)
describe = models.TextField(max_length=280)
signee = models.CharField(max_length=25, null=False)
signeeDets = models.TextField(max_length=280)
copydoc = models.FileField(upload_to='users/', blank=True, null=True)
userziptie = models.ForeignKey('users.CustomUser',
on_delete=models.DO_NOTHING, null=True)
def __str__(self):
return "{0}\n{1}\n{2}\n{3}\n{4}\n{5}\n{6}".format(
self.pk,
self.date,
self.docName,
self.describe,
self.signee,
self.signeeDets,
self.userziptie
)
class notarizerCreateForm(ModelForm):
class Meta:
model = notarizer
fields = ['docName','describe','signee','signeeDets', 'copydoc']
There are some things that make the view workflow very weird:
you check request.method, first you check if it is a 'POST' which is a good idea, but then you check if it is 'FILES', there is no HTTP method named FILES, there are only GET, POST, PATCH, PUT, OPTIONS, etc.;
you call form.is_valid() which is again what should happen, but then you create a new Form, and only pass it a single parameter; and
in case of a POST you should not return a rendered page, but redirect to a GET page (for example showing the result). The workflow is typically Post-redirect-get, since if the user refreshes their browser, we do not want to make the same post again.
The workflow should look like:
def create_entry(request):
createPath = 'log1/create_entry.html'
if request.method == 'POST': # good, a post (but no FILES check!)
form = notarizerCreateForm(request.POST, request.FILES)
if form.is_valid():
instance = form.save()
else:
# you probably want to show the errors in that case to the user
print(form.errors)
# redirect to a page, for example the `page1 view
return redirect(page1)
else:
form = notarizerCreateForm()
return render(request, createPath, {'form': form})
I'm trying to render a Django form on every page of my Wagtail site in a jQuery slideout box. I've created a template tag that is currently rendering the box, but the form is not showing up. I'm thinking that I'm incorrectly passing the form variable. My template tag file looks like this:
from django import template
from django.shortcuts import render, redirect
from django.template.loader import get_template
from django.core.mail import EmailMessage
from django.template import Context
from .forms import DemoForm
register = template.Library()
#register.inclusion_tag('demo_form.html')
def book_a_demo(request):
form = DemoForm
if request.method == 'POST':
demo_form = form(data=request.POST)
if demo_form.is_valid():
contact_name = request.POST.get('name', '')
company_name = request.POST.get('company', '')
contact_email = request.POST.get('email', '')
contact_phone = request.POST.get('phone', '')
# Email the profile with the
# contact information
template = get_template('contact_template.txt')
context = Context({
'contact_name': contact_name,
'company_name': company_name,
'contact_email': contact_email,
'contact_phone': contact_phone,
})
content = template.render(context)
email = EmailMessage(
"Request to Book a Demo",
content,
"domain" +'',
['example#email.com'],
headers = {'Reply-To': contact_email }
)
email.send()
return render(request, 'demo_form_landing.html')
return render(request, 'demo_form.html', {'form': form})
demo_form.html looks like this:
{% load static %}
<div id="feedback">
<div id="feedback-tab">Book a Demo</div>
<div id="feedback-form" style='display:block;' class="col-xs-4 col-md-4 panel panel-default">
<form role="form" action="" method="post" class="form panel-body">
{% csrf_token %}
{{ form.as_p }}
<button class="btn btn-primary pull-right" type="submit">Send</button>
</form>
</div>
</div>
My form looks like this:
from django import forms
class DemoForm(forms.Form):
name = forms.CharField(initial='Your Name', max_length=100)
company = forms.CharField(initial='Company Name', max_length=100)
email = forms.EmailField(initial='Email')
phone = forms.CharField(initial='Phone Number', max_length=100)
The template tag I'm using to try to render it in the main base.html is {% book_a_demo request %}
Any idea what I'm doing wrong? I don't get any errors; it just doesn't appear. I've been stuck on this for hours and it's driving me crazy.
When using inclusion_tag, you shouldn't call render yourself. You should return the context dictionary (for example: return {'form': form}) - Django will then take care of rendering the template you named in the #register.inclusion_tag('demo_form.html') declaration.
I would advise against trying to handle the if request.method == 'POST': logic within the tag, as that will be triggered any time any page on your site is rendered in response to a POST request - regardless of whether the POST request had anything to do with your DemoForm. Instead, you should set up a Django view to handle those form submissions, and put the URL to that view in the form's action attribute.
What is returned when the request is not a POST or is_valid() is false? I typically use a pattern like this (note the changes at the end):
from .forms import DemoForm
register = template.Library()
#register.inclusion_tag('demo_form.html')
def book_a_demo(request):
form = DemoForm
if request.method == 'POST':
demo_form = form(data=request.POST)
if demo_form.is_valid():
contact_name = request.POST.get('name', '')
company_name = request.POST.get('company', '')
contact_email = request.POST.get('email', '')
contact_phone = request.POST.get('phone', '')
# Email the profile with the
# contact information
template = get_template('contact_template.txt')
context = Context({
'contact_name': contact_name,
'company_name': company_name,
'contact_email': contact_email,
'contact_phone': contact_phone,
})
content = template.render(context)
email = EmailMessage(
"Request to Book a Demo",
content,
"domain" +'',
['example#email.com'],
headers = {'Reply-To': contact_email }
)
email.send()
return render(request, 'demo_form_landing.html')
else:
demo_form = form()
return render(request, 'demo_form.html', {'form': demo_form})
I am trying to have a user input a task from the frontend and have that data instantiate a new model and add this new field in the database associated with their account. I have tried the following;
Profile HTML
<form id="taskitem_form" method="post" action="/">
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
{{ field.errors }}
{{ field.help_text }}
{{ field }}
{% endfor %}
<input type="submit" name="submit" value="Add Task" class ="btn btn-primary" />
</form>
Model
class TaskItem(models.Model):
taskn = models.CharField(max_length = 400)
usern = models.ForeignKey(User)
def __str__(self):
return self.taskn
Views
def add_task(request):
# Get the context from the request.
#context = RequestContext(request)
# A HTTP POST?
if request.method == 'POST':
form = TaskItemForm(request.POST)
# Have we been provided with a valid form?
if form.is_valid():
task = form.save(commit=False)
task.usern = request.user
task.save()
# we should redirect after data modifying
return redirect('/user/%s' %(request.user))
else:
# If the request was not a POST, display the form to enter details.
return render(request, 'profile.html', {'form': form})
# Bad form (or form details), no form supplied...
# Render the form with error messages (if any).
return render(request, 'profile.html', {'form': form})
Forms
from django import forms
from bkmks.models import TaskItem
class TaskItemForm(forms.ModelForm):
taskn = forms.CharField(max_length = 300, help_text = "Please enter your task")
# An inline class to provide additional information on the form.
class Meta:
fields = ('taskn', 'usern' )
#This is the association between the model and the model form
model = TaskItem
Lot's of Changes needed to your code.
I'm posting a working version so that you can try.
Put profile.html file as bkmks/templates/bkmks/profile.html
Get it working. Customize later.
profile.html
<form id="taskitem_form" method="post" action="">
{% csrf_token %}
{{form}}
<input type="submit" name="submit" value="Add Task" class ="btn btn-primary" />
</form>
model as it is.
views.py
from django.contrib.auth.decorators import login_required
from django.shortcuts import render_to_response, RequestContext, redirect
from .forms import TaskItemForm
#login_required
def add_task(request):
# Get the context from the request.
context = RequestContext(request)
# A HTTP POST?
if request.method == 'POST':
form = TaskItemForm(request.POST)
# Have we been provided with a valid form?
if form.is_valid():
# Save the new category to the database.
task = form.save(commit=False)
task.usern = request.user
task.save()
# Redirect to home (/)
return redirect('/')
else:
# The supplied form contained errors - just print them to the terminal.
print form.errors
else:
# If the request was not a POST, display the form to enter details.
form = TaskItemForm()
# Bad form (or form details), no form supplied...
# Render the form with error messages (if any).
return render_to_response('bkmks/profile.html', {'form': form}, context)
forms.py
class TaskItemForm(forms.ModelForm):
# task is changed to taskn
taskn = forms.CharField(max_length = 300, help_text = "Please enter your task")
# An inline class to provide additional information on the form.
class Meta:
fields = ('taskn',)
#This is the association between the model and the model form
model = TaskItem
If you get any error or data is not getting saved post here.
Going through Django tutorial will be an wise decision.
The below should do what you need. You really want to inherit 100% of everything from your model when you can. This insures all model validation trickles down to the form. I utilized verbose_name and help_text on the model to achieve this.
Models
from django.conf import settings
class TaskItem(models.Model):
taskn = models.CharField(
max_length=400,
verbose_name="task",
help_text="Please enter your task.",
)
usern = models.ForeignKey(
to=settings.AUTH_USER_MODEL,
related_name="tasks",
)
def __str__(self):
return self.taskn
For the forms, I have added a forms.HiddenInput widget to the user, assuming you want the user submitting the task to become the user.
Forms
from django import forms
from bkmks.models import TaskItem
class TaskItemForm(forms.ModelForm):
widgets = {
'user': forms.HiddenInput,
}
class Meta:
model = TaskItem
fields = ('taskn', 'usern')
I have used a CreateView to reduce code complexity, and overrode the form_valid to add the user instance to the form.
Views
from django.views.generic import CreateView
from bkmks.models import TaskItem
from bkmks.forms import TaskItemForm
class TaskCreateView(CreateView):
model = TaskItem
form_class = TaskItemForm
template_name = "path/to/template.html"
def form_valid(self, form):
form.instance.user = self.request.user
return super(TaskCreateView, self).form_valid(form)
Finally, in the template, we simply want to use {{ form }}. I see you are looking into bootstrap. I'll suggest django-crispy-forms for this, but that is beyond the scope of your question.
Template
<form id="taskitem_form" method="post" action="/">
{% csrf_token %}
{{ form }}
<input type="submit" name="submit" value="Add Task" class ="btn btn-primary" />
</form>
https://docs.djangoproject.com/en/1.8/topics/http/shortcuts/#render-to-response
render_to_response expects a template as the first argument, not a url.
I think in your second call to render_to_response should include the template name / path , while the first one should use a return HttpResponseRedirect("/") instead, though its not clear exactly what your problem is.
Add this line to imports in views.py
from django.contrib.auth.decorators import login_required
Decorate add_task view
#login_required
def add_task(request):
Then, edit part of your code
if form.is_valid():
task = form.save(commit=False)
task.usern = request.user
task.save()
# we should redirect after data modifying
return redirect('/')
else:
# etc.
Some notes. You may replace render_to_response to render.
Remove this line
context = RequestContext(request)
Replace
# Wrong usage, actually.
# Should be something like
# render_to_response(template_name, context, context_instance)
render_to_respone('/', {'form': form}, context)
with
# if template_name is "profile.html"
render(request, 'profile.html', {'form': form})
Why define a field called task in the form if you've already got a field in the model called taskn, wouldn't it be better to just use that? And like the guys have said, you need to specify a template to render (that's why you're not seeing anything).
It'd also be a good idea to pass the current user to the form's user field.
#login_required
def add_task(request):
# Get the context from the request.
context = {}
# A HTTP POST?
if request.method == 'POST':
form = TaskItemForm(request.POST)
# Have we been provided with a valid form?
if form.is_valid():
# Save the new category to the database.
form.save()
# Now call the index() view.
# The user will be shown the homepage.
return render_to_response(
'profile.html',
{'form': form},
RequestContext(request, context)
)
else:
# The supplied form contained errors - just print them to the terminal.
print form.errors
else:
# If the request was not a POST, display the form to enter details.
form = TaskItemForm(initial={'usern': request.user})
# Bad form (or form details), no form supplied...
# Render the form with error messages (if any).
return render_to_response(
'profile.html',
{'form': form},
RequestContext(
request, context
)
)
Form;
from django import forms
from bkmks.models import TaskItem
class TaskItemForm(forms.ModelForm):
taskn = forms.CharField(max_length = 300, help_text = "Please enter your task")
# An inline class to provide additional information on the form.
class Meta:
fields = ('taskn', 'usern' )
#This is the association between the model and the model form
model = TaskItem