Django: send email on form submition - django

I need to send an email after user submits a form on my page (this is still in development, but it'll be in production soon).
I've read this other answer but after my form is submitted I'm not reciving any email (I've used my personal email as the sender and the receiver, for testing).
What I need to modify?
PLUS: How to send images on emails? Any tutorial on this? I need to send professional emails after form submition.
settings.py
EMAIL_HOST = 'smtp.gmail.com' # since you are using a gmail account
EMAIL_PORT = 587 # Gmail SMTP port for TLS
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'oma.oma#gmail.com' #Not my actual email
EMAIL_HOST_PASSWORD = 'MyPassword' #Not my actual password
views.py:
# here we are going to use CreateView to save the Third step ModelForm
class StepThreeView(CreateView):
form_class = StepThreeForm
template_name = 'main_app/step-three.html'
success_url = '/'
def form_valid(self, form):
form.instance.tamanios = self.request.session.get('tamanios') # get tamanios from session
form.instance.cantidades = self.request.session.get('cantidades') # get cantidades from session
del self.request.session['cantidades'] # delete cantidades value from session
del self.request.session['tamanios'] # delete tamanios value from session
self.request.session.modified = True
return super(StepThreeView, self).form_valid(form)
form.py:
from django.core.mail import send_mail
class StepThreeForm(forms.ModelForm):
instrucciones = forms.CharField(widget=forms.Textarea)
class Meta:
model = TamaniosCantidades
fields = ('imagenes', 'instrucciones')
def __init__(self, *args, **kwargs):
super(StepThreeForm, self).__init__(*args, **kwargs)
self.fields['instrucciones'].required = False
def send_email(self):
send_mail('Django Test', 'My message', 'oma.oma#gmail.com',
['oma.oma#gmail.com'], fail_silently=False)

You can override the save method like this in your Last Form:
class StepThreeForm(forms.ModelForm):
def save(self, commit=True):
instance = super(StepThreeForm, self).save(commit=commit)
self.send_email()
return instance
def send_email(self):
image = self.cleaned_data.get('imagenes', None)
msg = EmailMessage(
'Hello',
'Body goes here',
'from#example.com',
['to1#example.com', 'to2#example.com'],
headers={'Message-ID': 'foo'},
)
msg.content_subtype = "html"
if image:
mime_image = MIMEImage(image.read())
mime_image.add_header('Content-ID', '<image>')
msg.attach(mime_image)
msg.send()
You can check this SO Answer for more details on how to send image from Django

Related

not able to send email while user creation

I'm trying to put send a random 6 digit otp through email .Below code work for django 4.0 but not working in 3.2 ,
I'm not getting any error but when i createsuperuser in django 4.0 setup email send work but not the case in django 3.2
views.py
#receiver(post_save, sender=CustomUser)
def send_email_otp(sender, instance, created, **kwargs):
if created:
try:
subject = "Your email needs to be verified to use this site"
message = f'Hi, Dear {instance.name} use this following OTP to Get verified your email : OTP({instance.otpForEmail})/'
email_from = settings.EMAIL_HOST_USER
recipient_list = [instance.email]
send_mail(subject, message, email_from, recipient_list)
print(f"Email Sent to {instance.email}")
except Exception as e:
print(e)
print("Something Wrong at send_email_otp")
signals.py
from django.db.models.signals import pre_save
from .models import CustomUser
def updateUser(sender, instance, **kwargs):
user = instance
if user.email != '':
user.name = user.email
pre_save.connect(updateUser, sender=CustomUser)
I'm not sure what can be the problem,please help me to identify and fix .
Edit:
my email backend
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'my#gmail.com' #Allow Secure connections is enabled
EMAIL_HOST_PASSWORD = 'password'
EMAIL_PORT = 587
EMAIL_USE_TLS = True

when i submit email form it send back my own email Django python

class ProjectListAndFormView(SuccessMessageMixin, ListView, FormView):
model = Project # data from database
template_name = 'mainpage/main.html'
context_object_name = 'list_projects' # name of the var in html template
queryset = Project.objects.all().order_by("-pub_date")# list of all projects
object_list = None
form_class = ContactForm
success_url = '/' # After submiting the form keep staying on the same url
success_message = 'Your Form has been successfully submitted!'
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
cd = form.cleaned_data
con = get_connection('django.core.mail.backends.console.EmailBackend')
send_mail(
cd['name'],
cd['message'],
cd.get('email', 'noreply#example.com'),
['22agrandon#gmail.com'],
fail_silently=False
)
return super(ProjectListAndFormView, self).form_valid(form)
views.py
im having trouble with a form page on my website. Whenever i enter a random email on the email form part on the website it sends a succesful mail but from my own emayil even if i put a random email. How do i fix this so when someone enters their email, it sucesfully sends me an email from their email?

How to send Email without using settings.py ? (smtp parameters in database)

I would like to be able to send emails with django but without using the email parameters in settings.py.
(EMAIL_HOST, EMAIL_USE_TLS, EMAIL_HOST_PASSWORD, etc ...)
These parameters are stored in db because they can be different depending on the user.
How to use these parameters in base to send emails and not those in settings.py ?
class EmailThread(threading.Thread):
"""
Class email (Thread)
"""
def __init__(self, subject, html_content, recipient_list):
self.subject = subject
self.recipient_list = recipient_list
self.html_content = html_content
threading.Thread.__init__(self)
def run (self):
msg = EmailMessage(self.subject,
self.html_content,
settings.EMAIL_HOST_USER,
self.recipient_list)
msg.content_subtype = "html"
msg.send()
def send_html_mail(subject, html_content, recipient_list):
"""
send an email asynchronous
"""
EmailThread(subject, html_content, recipient_list).start()
I can get the parameters using: email_params = EmailParameter.objects.get(user=request.user)
class EmailParameter(models.Model):
email_use_tls = models.BooleanField(_("email use tls"), default=True)
email_use_ssl = models.BooleanField(_("email use tls"), default=False)
email_host = models.URLField(_("email host"), max_length=200)
email_host_user = models.CharField(_("email host user"), max_length=200)
email_host_password = models.CharField(_("email host password"), max_length=200)
email_port = models.PositiveIntegerField(_("email port"))
default_from_email = models.EmailField(_("default from email"), max_length=200)
signature = models.TextField(_("signature"))
user = models.ForeignKey(
User,
verbose_name = _("user"),
related_name = "user_email_parameter",
on_delete=models.CASCADE
)
Just move all necessary parameters inside class instance and use it instead of settings.XXX:
class EmailThread(threading.Thread):
def __init__(self, subject, html_content, recipient_list, email_host_user):
self.subject = subject
self.recipient_list = recipient_list
self.html_content = html_content
# set parameter to instance
self.email_host_user = email_host_user
threading.Thread.__init__(self)
def run (self):
msg = EmailMessage(self.subject,
self.html_content,
# use instance parameter
self.email_host_user,
self.recipient_list)
msg.content_subtype = "html"
msg.send()
# add email_host_user to be able set it when call mail send process
def send_html_mail(subject, html_content, recipient_list, email_host_user):
EmailThread(subject, html_content, recipient_list, email_host_user).start()
Now you able to send mail with custom parameters:
e_h_u = EmailParameter.objects.get(user=request.user).email_host_user
send_html_mail(subject, html_content, recipient_list, e_h_u)

How to write test case for sending email

Here is the simple view for creating user and staff model.After creating user and staff, it sends the html email to the user's email to fill up the details and the view works fine.
Now I want to write test case for this view and tried like this below but i got stuck on how can i write test to check whether the email will be sent or not after saving staff model, to the users.
models.py
class Staff(models.Model):
user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE, related_name='staff')
name = models.CharField(max_length=255, blank=True, null=True)
organization = models.ForeignKey(Organization, on_delete=models.SET_NULL,related_name='staff')
position = models.ForeignKey(Position, on_delete=models.SET_NULL,related_name='staff')
.......
views.py
def register_staff(request):
form = RegisterStaffForm()
if request.method == 'POST':
form = RegisterStaffForm(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
organization = form.cleaned_data['organization']
position = form.cleaned_data['position']
......
user = form.save(commit=False)
user.is_staff = True
user.is_active = True
user.save()
# creating staff model with user data
Staff.objects.create(user=user, name=name, organization=organization, position=position,....)
# sending html_email to the user
config = EmailConfiguration.objects.order_by('-date').first()
backend = EmailBackend(host=config.email_host, port=config.email_port, username=config.email_host_user,
password=config.email_host_password, use_tls=config.email_use_tls)
subject, from_email, to = "Staff Details", config.email_host_user, user.email
text_content = "Staff Details "
site = get_current_site(request)
html_content = render_to_string('send_email.html',
{'user': user, 'site_domain': site,})
msg = EmailMultiAlternatives(subject, text_content, from_email, [to],connection=backend)
msg.attach_alternative(html_content, "text/html")
msg.send()
tests.py
class StaffTestCase(TestCase):
def setUp(self):
self.position = Position.objects.create(title='Developer')
self.org = Organization.objects.create(name='name')
self.user = get_user_model().objects.create_user(username='username01',password='Admin#321',email='abc#xyz.com',is_staff=True)
self.staff = Staff.objects.create(user=self.user,position=self.position,organization=self.org,name='Julia')
self.client = Client()
def test_view_staffs(self):
self.client.login(username='username01', password='Admin#321')
response = self.client.get(reverse('app:view_staff_users'))
self.assertEqual(response.status_code, 200)
def add_staff(self):
self.client.login(username='username01', password='Admin#321')
url = reverse('app:register_staff')
response = self.client.post(url, {'user': self.user,'organization':'name1','position':'Designer','name':'Mark'})
self.assertEqual(response.status_code, 302)
def check_email_will_sent_or_not(self):
??
Django provides tools to test the sending of emails, but from my understanding, these only work with the default email backend configured in your settings file.
That leaves you with four options:
figure out if you can monkeypatch your view to use locmem backend in testing
mock EmailBackend in your register_staff view and check if its send_messages function is called.
spin up a dummy SMTP server and check if it receives the messages
use valid credentials to actually send the emails to addresses you control and check if the email is received
The options 3 & 4 give you the most confidence that your email sending really works, but they might turn out to be slow and brittle and actually test the email sending code of Django itself. I would go with option 2.

Django form not saving or emailing as it should

I've built a simple form that should send an email on form submit, as well as save to a table in the database. I've done this by following different tutorials, as I am still learning Django. At the moment, it should be configured to output the email to the console rather than actually send an email, as per the tutorial I followed.
I don't get any errors and the console shows a HTTP 200 success code when the form is posted, so it seems to be working, but since nothing is saved and no email is printed to the console, I'm not sure what is going on.
My models.py:
from __future__ import unicode_literals
from django.db import models
from multiselectfield import MultiSelectField
class Booking(models.Model):
timeslot = (
('EM', 'Early Morning'),
('LM', 'Late Morning'),
('EA', 'Early Afternoon'),
('LA', 'Late Afternoon'),
)
services = (
('gutters', 'Gutter Cleaning'),
('windows', 'Window Cleaning'),
('decks', 'Deck Staining'),
('painting', 'Painting'),
)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.CharField(max_length=30)
phone = models.CharField(max_length=30)
booking_time = models.CharField(max_length=1, choices=timeslot)
selected_services = MultiSelectField(choices=services)
The relevant part of my views.py:
def booking_new(request):
form_class = BookingForm
# new logic!
if request.method == 'POST':
form = form_class(data=request.POST)
if form.is_valid():
first_name = request.POST.get(
'first_name'
, '')
last_name = request.POST.get(
'last_name'
, '')
email = request.POST.get(
'email'
, '')
phone = request.POST.get(
'phone'
, '')
booking_time = request.POST.get(
'booking_time'
, '')
selected_services = request.POST.get(
'selected_services'
, '')
form_content = request.POST.get('content', '')
template = get_template('email_template.txt')
context = {
'first_name': first_name,
'last_name': last_name,
'email': email,
'form_content': form_content,
}
content = template.render(context)
email = EmailMessage(
"New contact form submission",
content,
"Your website" +'',
['youremail#gmail.com'],
headers = {'Reply-To': contact_email }
)
email.send()
form.save()
return redirect('booking_new')
return render(request, 'home/booking_edit.html', {
'form': form_class,
})
My forms.py:
from django import forms
from .models import Booking
class BookingForm(forms.ModelForm):
class Meta:
model = Booking
fields = ('first_name', 'last_name', 'email', 'phone', 'booking_time', 'selected_services')
And the end of my settings.py:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
DEFAULT_FROM_EMAIL = 'testing#example.com'
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''
EMAIL_USE_TLS = False
EMAIL_PORT = 1025
I'm not getting any errors about missing imports or URLs or templates etc, so I think I have included all the relevant info. As far as I can see everything is configured correctly.
Why when my form is submitted is no email sent, and no record saved to the database?
the console shows a HTTP 200 success code
This suggests that the form is not valid. If the form was valid, you would redirect after sending the email, so you would see status code 302.
In the view, you should pass the form to the template instead of the form class. That way you should see any errors rendered in the form template (assuming you use {{ form }}).
if request.method == 'POST':
form = form_class(data=request.POST)
...
else:
form = form_class() # Blank form for GET request
return render(request, 'home/booking_edit.html', {
'form': form,
})
You may also find it useful to add print statements (e.g. print(form.is_valid()), print(form.errors). The output in the console will help you debug what is happening when the view runs.