Activate account HTML email in Django - django

I have a Django App that lets users to register to the system. After the user register via a form receive a email with an activation link. The app works very well, but I want to send the user an HTML mail, not only plain text. I use an template to provide fields to be filled. The problem I face is how to provide the uuid and token in an HTML template in an activate link.
I've reading about the send_mail() function, but I don't understand how to use for my purpose.
I'm using Django 3.2.8.
Show my code:
myApp/urls.py
path('registro/<str:usuario>', views.registro, name='registro'),
path('activate/<uidb64>/<token>/', views.activate, name='activate'),
path('esperaconfirmacionregistro/', views.esperaConfirmacionRegistro, name='esperaconfirmacionregistro'),
path('confirmacionregistro/<str:status>', views.confirmacionRegistro, name='confirmacionregistro'),
acc_active_email.html
{% autoescape off %}
Saludos, {{user.first_name}} {{user.last_name}}.
Por favor da click en el enlace para confirmar tu registro,
http://{{ domain }}{% url 'activate' uidb64=uid token=token %}
{% endautoescape %}
views.py
def registro(request,usuario):
if request.method == 'POST':
form = FormularioAutoRegistro(request.POST, initial={'usuario':usuario})
if form.is_valid():
user = form.save()
current_site_info = get_current_site(request)
mail_subject = 'Activar cuenta de usuario'
message = render_to_string('myApp/acc_active_email.html', {
'user': user,
'domain': current_site_info.domain,
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'token': account_activation_token.make_token(user),
})
to_email = form.cleaned_data.get('email')
email = EmailMessage(mail_subject, message, to=[to_email])
email.send()
form = FormularioAutoRegistro(initial={'usuario':''})
return redirect('esperaconfirmacionregistro')
else:
form = FormularioAutoRegistro(initial={'usuario':usuario})
return render(request, 'myApp/registro.html', {'form': form})
def activate(request, uidb64, token):
user = get_user_model()
try:
uid = force_text(urlsafe_base64_decode(uidb64))
user = customuser.objects.get(pk=uid)
except(TypeError, ValueError, OverflowError, user.DoesNotExist):
user = None
if user is not None and account_activation_token.check_token(user, token):
user.is_active = True
user.save()
return redirect('confirmacionregistro', status='confirm')
else:
return redirect('confirmacionregistro', status='fail')
def confirmacionRegistro(request, status):
titulo = ''
mensaje = ''
if status=='confirm':
titulo = 'Cuenta confirmada'
mensaje = 'Gracias por confirmar tu email.'
elif status=='fail':
titulo = 'Link inválido'
mensaje = 'El link de activación es inválido.'
else:
raise Http404
return render (request,'myApp/confirmacionregistro.html', {'titulo':titulo, 'mensaje':mensaje})
def esperaConfirmacionRegistro(request):
mensaje = "Autorización pendiente"
return render(request, 'myApp/esperaconfirmacionregistro.html', {'mensaje': mensaje})

Try using EmailMultiAlternatives instead of EmailMessage. It is pretty similar:
from django.core.mail import EmailMultiAlternatives
mail = EmailMultiAlternatives("title", "content", "email_from#test.pl", ["email_to#test.pl"])
Then you can attach html "alternative":
from django.urls import reverse
url = reverse('activate', kwargs={'uidb64': your_uidb64, 'token': your_token})
html_content = f"Link: <a href='{url}'>{url}</a>"
mail.attach_alternative(html_content, "text/html")
And send it:
mail.send()

Finally I resove my problem. NixonSparrow you are right, I had the answer:
def registro(request,usuario):
if request.method == 'POST':
form = FormularioAutoRegistro(request.POST, initial={'usuario':usuario})
if form.is_valid():
user = form.save()
current_site_info = get_current_site(request)
mail_subject = 'Activar cuenta de usuario'
to_email = form.cleaned_data.get('email')
nombre = user.first_name
apellidos = user.last_name
domain = current_site_info.domain
uid = urlsafe_base64_encode(force_bytes(user.pk))
token = account_activation_token.make_token(user)
url = f"http://{domain}/activate/{uid}/{token}"
content = f"Hola {nombre} {apellidos} \n" \
f"Por favor da click en el enlace o copialo y pégalo en el navegador para confirmar tu registro en el sistema: {url} \n" \
f"Gracias."
email = EmailMultiAlternatives(mail_subject, content, "pepe.eloy#gmail.com", [to_email])
html_content = f"Hola <strong>{nombre} {apellidos}</strong>. <br> <br>" \
f"Por favor da click en el enlace para confirmar tu registro en el sistema: <a href='{url}'>{url}</a> <br> <br>" \
f"Gracias."
email.attach_alternative(html_content, "text/html")
email.send()
form = FormularioAutoRegistro(initial={'usuario':''})
return redirect('esperaconfirmacionregistro')
else:
form = FormularioAutoRegistro(initial={'usuario':usuario})
return render(request, 'myApp/registro.html', {'form': form})

Related

I have created a contact us app and booking app for my website but I still having an issue

What I want is that the sender be the email provided in email field and de receiver must be the website owner
Here is my view contact/views.py
# SPEEDING UP DJANGO EMAIL
class EmailThread(threading.Thread):
def __init__(self, email):
self.email = email
threading.Thread.__init__(self)
def run(self):
self.email.send(fail_silently=False)
# CONTACT US VIEW
def contact_us(request):
# Contact Us & Booking Table
if request.method == 'GET':
return render(request, 'contact.html', {"values": request.POST})
if request.method == 'POST':
contact_name = request.POST['name']
contact_email = request.POST['email']
contact_subject = request.POST['subject']
contact_message = request.POST['message']
# ========== saving entries into database ======================
# Let's save our datas in database
contact = Contact() # here he call our Contact Class
# and then we can use its propriertes
contact.contact_name = contact_name
contact.contact_email = contact_email
contact.contact_subject = contact_subject
contact.contact_message = contact_message
if len(contact_message)<20:
messages.error(request, "Contenu du message trop court pour être envoyé. Veillez réessayer svp!")
return render(request, 'contact.html')
contact.save() # All our entries will be now saved in our database
# ========== end saving entries into database ======================
# ============ Let's send user infos and request through email
message = f"NAME: {contact_name}\n\n\n\nMESSAGE:\n{contact_message}"
to_mail = settings.EMAIL_HOST_USER
email = EmailMessage(
contact_subject,
message,
contact_email,
to_mail
)
EmailThread(email).start() # Here we called our speeding email class :)
messages.success(request,
"Message sent successfully!\n \
We will call you or send you an email or sms to inform you about your request\n \
Thanks for waiting! \
")
return render(request, 'contact.html', {"values": request.POST})

How to fill a Django form with an API response?

I need to create a form of persons, where the camp Name must receive an API response. I created the formulary e rendered the api response in template, but I can´t put it in my formulary, in order to save in my Models camp Name. So, I just want to save my API respone inside my forms and in my database.
Views
def cadastro(request):
url = 'https://gerador-nomes.herokuapp.com/nome/aleatorio'
api = requests.get(url)
nome_api = ' '.join(api.json())
form = PessoaForm()
form.nome = api
if request.method == 'POST':
form = PessoaForm(request.POST)
if form.is_valid():
form.cleaned_data('nome')
form.save()
return redirect('/')
context = {'form': form, 'api': nome_api}
return render(request, 'base/pessoa_form.html', context)
pessoa_form.html
<body>
<form action="" method="post">
{% csrf_token %}
{{form}}
<input type="submit" name="Cadastrar">
</form>
</body>
</html>
Forms
from django.forms import ModelForm
from . models import Pessoa
class PessoaForm(ModelForm):
class Meta:
model = Pessoa
fields = '__all__'
Models
from django.db import models
class Pessoa(models.Model):
name= models.CharField(max_length=255, null=True)
lastname= models.CharField(max_length=255, null=True)
age= models.IntegerField(null=True)
birthday_date= models.DateField()
email = models.CharField(max_length=255, null=True)
nickname= models.CharField(max_length=255, null=True, blank=True)
note = models.CharField(max_length=500, null=True, blank=True)
def __str__(self):
return self.nome
class Meta:
ordering = ['nome', 'sobrenome']
I have tried some things of my head but nothing actually worked, like try to access the variable Name in my forms inside my template and inside my views.
[English]
Well, by judging by the variable names you've chosen, I guess you're brazilian, so I'll post the answer in portuguese as well.
I don't know if this is the best practice, but you could just append the api response as a new field in the request.POST, for instance:
[Portuguese Translation]
Pelos nomes que tu usou, imagino que sejas BR então vou botar a resposta em Português também além do inglês.
Não sei se é o método que segue as melhores práticas, mas você poderia simplesmente adicionar a resposta da API como um campo novo no teu request.POST, por exemplo:
def cadastro(request):
url = 'https://gerador-nomes.herokuapp.com/nome/aleatorio'
api = requests.get(url)
nome_api = ' '.join(api.json())
form = PessoaForm()
if request.method == 'POST':
updated_request = request.POST.copy()
updated_request.update({'name': nome_api})
form = PessoaForm(updated_request)
if form.is_valid():
form.save()
return redirect('/')
context = {'form': form, 'api': nome_api}
return render(request, 'base/pessoa_form.html', context)

Django edit profile not saving

I have pre-filled forms for my user (the django user + my profile one with more informations) that just doesn't want to be saved. No error message, no redirection, I press the button "edit", and then... same page, with the modifications already written. But the database isn't changed.
So, here is the code :
forms.py :
class UpdateUser(forms.ModelForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = ('email', 'username')
def clean_email(self):
email = self.cleaned_data.get('email')
username = self.cleaned_data.get('username')
if email and User.objects.filter(email=email).exclude(username=username).count():
raise forms.ValidationError('Cette adresse email est déjà utilisée, veuillez en indiquer une autre')
return email
class UpdateProfil(forms.ModelForm):
class Meta:
model = Profil
fields = ("society", "thingA", "thingB", "thingC", "thingD", "thingE")
views.py :
#login_required
def edit_profile(request):
user = User.objects.get(username = request.user)
profile = Profil.objects.get(user = user)
if request.method == 'POST':
form1 = UpdateUser(request.POST, instance=user)
form2 = UpdateProfil(request.POST, instance=profile)
if form1.is_valid() and form2.is_valid():
form1.save()
form2.save()
return HttpResponseRedirect('Register/view_profile.html')
else:
form1 = UpdateUser(initial={"email": user.email,
"username": user.username})
form2 = UpdateProfil(initial={"society": profile.society,
"thingA": profile.thingA,
"thingB": profile.thingB,
"thingC": profile.thingC,
"thingD": profile.thingD,
"thingE": profile.thingE})
return render(request, 'Register/edit_profile.html', locals())
and the template:
<form action="" method="post">
{% csrf_token %}
<p>Email : </p>{{ form1.email }}
<p>Nom d'utilisateur : </p>{{ form1.username }}
<p>Société (entreprise, association, établissement...") :</p>
{{ form2.society }}
<p>Si vous êtes membres d'un ou plusieurs centres pilotes, veuillez les cocher ci-dessous :</p>
<p>thingA : {{form2.thingA }}</p>
<p>thingB : {{form2.thingB }}</p>
<p>thingC : {{form2.thingC }}</p>
<p>thingD : {{form2.thingD }}</p>
<p>thingE : {{form2.thingE }}</p>
<button type="submit">Editer</button>
</form>
models.py
class Profil(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
deadline = models.DateField(blank=True, null=True,
default=None,
verbose_name="Date limite de validité")
thingA = models.BooleanField(default=False)
thingB = models.BooleanField(default=False)
thingC = models.BooleanField(default=False)
thingD = models.BooleanField(default=False)
thingE = models.BooleanField(default=False)
society = models.CharField(max_length = 255,
blank=True, null=True,
verbose_name="Société",
help_text="Le nom de votre entreprise, association, établissement...")
def __str__(self):
return "Profil de {0}".format(self.user.username)
After trying different thing and reading everything I could on the topic without finding a solution working, I try to think outside the box : I thought that maybe it was because I didn't put the password inside the form, but it should'nt play a role here, right ?
So, maybe a permissions problem ? I try giving permisions, for both the model. Still not working...
Thanks for your time, and sorry for my English mistakes...
So, I tinkered with my code a bit, and it's now working, I don't know why, and if someone know, I'm all ears!
I'll put it here, in case it could be helpful for others.
so, new view's code :
#login_required
def edit_profile(request):
user = User.objects.get(username = request.user)
profile = Profil.objects.get(user = user)
if request.method == 'POST':
form1 = UpdateUser(request.POST, instance=user)
form2 = UpdateProfil(request.POST, instance=profile)
if form1.is_valid() and form2.is_valid():
form1.save()
form2.save()
return redirect("viewProfile") # Here is the only change I finished with
else:
form1 = UpdateUser(initial={"email": user.email,
"username": user.username})
form2 = UpdateProfil(initial={"society": profile.society,
"thingA": profile.thingA,
"thingB": profile.thingB,
"thingC": profile.thingC,
"thingD": profile.thingD,
"thingE": profile.thingE})
return render(request, 'Register/edit_profile.html', locals())
So, I guess the problem was with the httpResponseRedirect, don't know why, since there was no error, and nothing was saved, but well... I won't look a gift horse in the mouth! It's working now. 2020 suddenly seems a lot better! Until tomorrow that is =P
Thanks to those who tried to help me!

Passing an initial value into a form field from previous detail view

I a bit confused how to do this, I have a "contact user" button on a user detail page. I am trying to set the initial form value for the email field as the users email from the previous detail page view, so that it pre-populates.
Would I pass the user.email as a kwarg into the button url?
views.py
def Contact(request):
form_class = ContactForm
# new logic!
if request.method == 'POST':
form = form_class(data=request.POST)
if form.is_valid():
contact_name = request.POST.get(
'contact_name'
, '')
contact_email = request.POST.get(
'contact_email'
, '')
form_content = request.POST.get('content', '')
# Email the profile with the
# contact information
template = get_template('contact_template.txt')
context = {
'contact_name': contact_name,
'contact_email': contact_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()
return redirect('contact_form')
return render(request, 'portal/contact_form.html', {
'form': form_class,
})
forms.py
from django import forms
class ContactForm(forms.Form):
contact_name = forms.CharField(required=True)
contact_email = forms.EmailField(required=True, initial='{ kwarg }')
content = forms.CharField(
required=True,
widget=forms.Textarea
)
user_detail.html
<div class="col-lg-4 text-center p-5" style="padding-right:20px; border-right: 1px solid #e5e5e5;">
<a href="{% url 'portal:contact_form' kwarg.user.email %}">
<h1><i class="fa fa-2x fa-envelope-o text-success" aria-hidden="true"></i></h1>
<p class="text-muted mt-3 ">Contact Customer</p>
</a>
</div>
There are multiple ways to do that. Like for example:
Method 1: Send it in URL Querystring
from you previous details view, send the email in url querystring. For example:
def DetailForm(request, *args, **kwargs):
...
if form.is_valid():
# do some things
user_email = form.cleaned_data.get('user_email') # assuming its the form field you are using for catching user email
redirect_url = "{}?user_email={}".format(self.get_success_url(), user_email)
return HttpResponseRedirect(redirect_url)
And catch it in Next form view:
def Contact(request):
form_class = ContactForm(initial={'contact_email': request.GET.get('user_email')})
...
Method 2: Store in session.
Its almost similar to last approach, but it stores the data in session.
def DetailForm(request, *args, **kwargs):
...
if form.is_valid():
# do some things
request.session['user_email'] = form.cleaned_data.get('user_email') # Storing data in session
# every other code
And get it in Next form view:
def Contact(request):
form_class = ContactForm(initial={'contact_email': request.session.get('user_email')})
...
Update
If you are coming from a detail page, means you have the access to the object. So in the detail page, update the contact button like this:
contact // or user.email if you are passing user object as user in context
in Contact View:
def Contact(request, email):
if request.method == "GET":
form = ContactForm(initial={'contact_email': email)})

The view newsletter.views.control_newsletter_edit didn't return an HttpResponse object. It returned None instead

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})