How to merge a function into another - python

Sorry for asking a basic thing. new to Python and Django ,
I want to resend email if the OTP from PUT request is incorrect.
I have a function which send email with otp automatically on Register.
But if user PUT incorrect OTP I want to resend that email with new otp, So I want to merge sent_email_otp into verifyEmail function.
So how could I achieve that?
#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 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")
#api_view(['PUT'])
#permission_classes([IsAuthenticated])
def verifyEmail(request, pk):
user = CustomUser.objects.get(id=pk)
data = request.data
otp_to_verify = data['otpForEmail']
if otp_to_verify == user.otpForEmail:
user.isEmailVerified = True
user.save()
message = {'detail': 'Your email is now verified'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
else:
message = {
'detail': 'OTP is not valid and expired, Use New OTP which we have sent you on the email'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
Edit:
If I simply call the send_email_otp() inside else statement of verifyEmail then this error comes :
TypeError: send_email_otp() missing 3 required positional arguments: 'sender', 'instance', and 'created'

You cant call the signal directly unless you provide it the expected input.
You will need to create another function for sending the otp. and call it in the post_save signal and in the view
def send_otp(name, email, otpForEmail):
subject = "Your email needs to be verified to use site"
message = f'Hi, Dear {name} use this following OTP to Get verified your email : OTP({otpForEmail})'
email_from = settings.EMAIL_HOST_USER
recipient_list = [email]
send_mail(subject, message, email_from, recipient_list)
print(f"Email Sent to {email}")
#receiver(post_save, sender=CustomUser)
def send_email_otp_on_post_save(sender, instance, created, **kwargs):
if created:
try:
send_otp(instance.name, instance.email, instance.otpForMail)
except Exception as e:
print(e)
print("Something Wrong at send_email_otp")
#api_view(['PUT'])
#permission_classes([IsAuthenticated])
def verifyEmail(request, pk):
user = CustomUser.objects.get(id=pk)
data = request.data
otp_to_verify = data['otpForEmail']
if otp_to_verify == user.otpForEmail:
user.isEmailVerified = True
user.save()
message = {'detail': 'Your email is now verified'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
else:
message = {
'detail': 'OTP is not valid and expired, Use New OTP which we have sent you on the email'}
send_otp(user.name, user.email, user.otpForEmail)
return Response(message, status=status.HTTP_400_BAD_REQUEST)

It would be hard for you to configure the same function to send_mail again to send OTP, as you said this sends the email when the user gets registered. So why not modify the verifyEmail itself.
First, you don't need a user instance here as the user is already authenticated and you already have the User Id.
So in the else statement of verifyEmail, you can send_email without calling send_email_otp() function.
Update the verifyEmail to.
#api_view(['PUT'])
#permission_classes([IsAuthenticated])
def verifyEmail(request, pk):
user = CustomUser.objects.get(id=pk)
data = request.data
otp_to_verify = data['otpForEmail']
if otp_to_verify == user.otpForEmail:
user.isEmailVerified = True
user.save()
message = {'detail': 'Your email is now verified'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
else:
subject = "Your email needs to be verified to use site "
message = f'Hi, Dear {user.name} use this following OTP to Get verified your email : OTP({user.otpForEmail})'
email_from = settings.EMAIL_HOST_USER
recipient_list = [user.email]
send_mail(subject, message, email_from, recipient_list)
print(f"Email Sent to {user.email}")
message = {
'detail': 'OTP is not valid and expired, Use New OTP which we have sent you on the email'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)

Related

Django password reset with email using REST APIs

I'm developing a website using DjangoRest and Flutter and I want to add password reset using email.
I know that django.contrib.auth has views that help with password reset (PasswordResetView, PasswordResetDoneView, etc). But as long as I see, they return HTML files as a response when you call them in Postman.
Is there any way to use the same easy-to-use views but instead of getting HTML files, get an HTTP response so it can be called by the Flutter app?
This can be handled in basically Two Steps:
1. One is to send anything like OTP or Reset Link view email
2. The second is to verify whether the OTP/link either is valid or not with a new password.
This can be achieved via simple function-based API views:
I can demonstrate the simplest form using OTP for basic understanding, as u said you are using flutter at frontend and that will be easier to manage otp instead of link
Step 1: Add a top Field into User Model.
Let's say we have field otp in the user model. Later we use it for verification purposes.
class CustomerUser(models.Model):
#...
otp = models.CharField(
max_length=6, null=True, blank=True)
# Method to Put a Random OTP in the CustomerUser table.
def save(self, *args, **kwargs):
number_list = [x for x in range(10)] # Use of list comprehension
code_items_for_otp = []
for i in range(6):
num = random.choice(number_list)
code_items_for_otp.append(num)
code_string = "".join(str(item)
for item in code_items_for_otp) # list comprehension again
# A six digit random number from the list will be saved in top field
self.otp = code_string
super().save(*args, **kwargs)
Step:2: Function to send Email with OTP on User Request
#api_view(['POST'])
def reset_request(request):
data = request.data
email = data['email']
user = CustomUser.objects.get(email=email)
if CustomUser.objects.filter(email=email).exists():
# send email with otp
send_mail(
'Subject here',
f'Here is the message with {user.otp}.',
'from#example.com',
[user.email],
fail_silently=False,
)
message = {
'detail': 'Success Message'}
return Response(message, status=status.HTTP_200_OK)
else:
message = {
'detail': 'Some Error Message'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
Last Step: Verify OTP And reset Password
#api_view(['PUT'])
def reset_password(request):
"""reset_password with email, OTP and new password"""
data = request.data
user = CustomUser.objects.get(email=data['email'])
if user.is_active:
# Check if otp is valid
if data['otp'] == user.opt:
if new_password != '':
# Change Password
user.set_password(data['password'])
user.save() # Here user otp will also be changed on save automatically
return Response('any response or you can add useful information with response as well. ')
else:
message = {
'detail': 'Password cant be empty'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
else:
message = {
'detail': 'OTP did not matched'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
else:
message = {
'detail': 'Something went wrong'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
So you can replicate it with your custom approach as well and can refactor it easily.
I have used Simple API views in these examples you can check the detailed information in DRF DOCS Requests and Response Section as well
So You don't have to use HTML at all, just can work with Response, HttpResponse whatever you prefer.

How to send a reset password email in Django on User creation?

I want to be able to let an admin create user accounts and then, instead of setting up a password for the user, the user would automatically receive a reset password email.
The view for the user creation, which also includes a Member model, is the following:
def newmember(request):
if request.method == 'POST':
nu_form = NewUser(request.POST)
nm_form = NewMember(request.POST)
if nu_form.is_valid() and nm_form.is_valid():
nusave = nu_form.save()
nmsave = nm_form.save(commit = False)
nmsave.user = nusave
nmsave.save()
return redirect(members)
else:
print(nu_form.errors)
print(nm_form.errors)
else:
nu_form = NewUser()
nm_form = NewMember()
context = {
'nu_form': nu_form,
'nm_form': nm_form}
return render(request, 'web/newmember.html', context)
How can I make so that upon creation of a new user, Django automatically sends an email to that new user requestion a password reset?
In order to send an email on user creation you need to define a method which shoot an email like below :-
Create a text file name such as 'email_content.txt'
Please reset password for your profile {{username}}
Click Here
Update the newmember method and add below code into it :-
template = get_template('email_content.txt')
context = {"usename": nmsave.user.username}
content = template.render(context)
email = EmailMessage(
"Congratulation, please reset your account password", content, 'App Name' <sender_email>
)
email.content_subtype = "html"
email.send()
add above code in try catch block
In your models.py:
def save(self, *args, **kwargs):
send_mail('subject', 'message', 'your email', 'user email')
return super().save(*args, **kwargs)

Send email to address getting from HTML input via Django

I'm searching for solution to this problem for many hours but can't find anything related. I want to get user's email from input and send mail from admin to that email address. Here are my codes:
views.py:
def index(request):
context = {
'questions': Question.objects.all(),
'applicants': Applicant.objects.filter(status=1),
'empty_cards': range(4 - Applicant.objects.filter(status=1).count())
}
if request.method == "POST":
if request.POST.get('message_text'):
Message.objects.create(
sender_name = request.POST.get('sender_name'),
sender_email = request.POST.get('sender_email'),
message_text = request.POST.get('message_text'))
if request.method == 'POST':
subject = 'Welcome !'
message = 'We will back to you.'
from_email = settings.EMAIL_HOST_USER
recipient_list = 'don't know how to get email'
send_mail(subject, message, from_email, recipient_list)
return render(request, 'index.html', context)
from your code I assume that you already have access to the user's mail
inside the request.
so you can try this:
sender_email = sender_request.POST.get('sender_email')
recipient_list = [sender_email]

Django restframe work send email from posted data

I'm using Django rest framework, I have a contact us table.. in views, I use class based view i want to send email when user post data .. this's my function
def post(self, request):
return self.create(request)
return send_email(request)
def send_email(request):
if request.method == 'POST':
subject = request.POST.get('subject', '')
message = request.POST.get('message', '')
from_email = request.POST.get('email', '')
send_mail(subject, message, from_email, ['haguwanax#l0real.net'], fail_silently=False)
return Response (status=status.HTTP_200_OK)
it saves the data but there's nothing being sent.
this's my settings.py
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
EMAIL_PORT = 587
EMAIL_HOST_USER = '*****#gmail.com'
EMAIL_HOST_PASSWORD = '*******'
EMAIL_USE_TLS = True
You can not use two return at a time in a same block of code. Your second return statement will not execute.
I don't think your create method need return something. It's just storing some values.
And your send email might return true or false if email send is successful or not
Please use only one return statement in your post function.
You can rewrite the code following:
def post(self, request):
self.create(request)
check_mail=send_email(request)
if check_mail:
return Response (status=status.HTTP_200_OK)
def send_email(request):
if request.method == 'POST':
subject = request.POST.get('subject', '')
message = request.POST.get('message', '')
from_email = request.POST.get('email', '')
send_mail(subject, message, from_email, ['haguwanax#l0real.net'],
fail_silently=False)
return True
The send_email function is not being called because you're calling it after return self.create(request). Your code for send_email is hence unreachable.
Please use only one return statement in your post function.
You can call email method without the return statement and then call return self.create(request)
send_email(request)
return self.create(request)

invitation not accepted user are also registered

The feature in my app is a user should first request for invitation and if his invitation is accepted by admin(from admin), a message should be sent that you can signup.
Flow of the feature
User submits the form with his/her email address and mail will be sent saying thank you. Now if admin accepts his/her request, i.e request_accepted will be True now and a mail will be sent saying now you can signup. Finally when user go to signup page and submit the form, it should check if the email that he has filled and submitted during signup has been accepted or not. If accepted, signup is complete with success otherwise display an error stating this email address has not been invited.
class Invitation(models.Model):
email = models.EmailField(unique=True, verbose_name=_("e-mail Address"))
request_approved = models.BooleanField(default=False, verbose_name=_('request accepted'))
#receiver(post_save, sender=Invitation)
def send_email_when_invite_is_accepted_by_admin(sender, instance, *args, **kwargs):
request_approved = instance.request_approved
if request_approved:
subject = "Request Approved"
message = "Hello {0}! Your request has been approved. You can now signup".format(instance.email)
from_email = None
to_email = [instance.email]
send_mail(subject, message, from_email, to_email, fail_silently=True)
def requestInvitation(request):
form = InviteForm(request.POST or None)
if form.is_valid():
join = form.save(commit=False)
email = form.cleaned_data.get('email')
already_join, created = Invitation.objects.get_or_create(email=email)
if created:
already_join.save()
subject = "Thank you for your request to sign up our community"
message = "Welcome! We will be in contact with you."
from_email = None
to_email = [email]
send_mail(subject, message, from_email, to_email, fail_silently=True)
messages.success(request, '{0} has been invited'.format(email))
return HttpResponseRedirect("/")
context = {"form": form}
return render(request, 'home.html', context)
ACCOUNT_SIGNUP_FORM_CLASS = 'pages.custom_sign_up_form.CustomSignupForm'
custom_signup_form.py
from invitation.models import Invitation
class CustomSignupForm(forms.Form):
def __init__(self, *args, **kwargs):
super(CustomSignupForm, self).__init__(*args, **kwargs)
def clean_email(self):
value = self.cleaned_data.get('email')
print ('value before adapter', value)
value = get_adapter().clean_email(value)
print ('value', value)
if value and app_settings.UNIQUE_EMAIL:
value = self.validate_unique_email(value)
try:
Invitation.objects.get(email=value, request_approved=True)
except Invitation.DoesNotExist:
raise forms.ValidationError(errors['Sorry! you are not yet invited'])
return value
def signup(self, request, user):
user.save()
The way i am doing allows to signup to non accepted user too.
UPDATE
if i put clean_email logic inside signup as
def signup(self, request, user):
print ('request', request, user)
value = self.cleaned_data.get('email')
print ('email is', value)
try:
Invitation.objects.get(email=value, request_approved=True)
except:
raise forms.ValidationError(errors['sorry! You are not yet invited'])
user.save()
i get 'name errors is not defined'
Based on the update part of your question:
raise forms.ValidationError(errors['sorry! You are not yet invited'])
There is going quite wrong here. You are saying you have a dict with sorry! You are... as a key, and expecting it to return a value.
This is what you are attempting to access:
errors = {
'sorry! you are not yet invited': None,
}
A key like that, with exclamation marks, spaces, etc is invalid.
Note that error is not defined in the method (and not anywhere I can see in the class). If you have initialized as a class variable, you should access it with self.
If you have not yet declared the error dict with a default error message, you should create it as a class variable.
class CustomerSignupForm(forms.Form):
errors = {
'not_invited': "Sorry! You are not yet invited.",
}
Now in your signup method, you can access the error message:
raise forms.ValidationError(
self.errors['not_invited'],
code='not_invited'
)
Based on the original question:
I'm not 100% sure right now what the problem is. But there are a few things you can take a look at and improve.
Point 1: Your variable names should explain clearly what it contains. I.e.
value = self.cleaned_data.get('email')
is a field containing an e-mail string or None. Keep it simple and clear, and keep email as variable name. If the value changes in your logic, you can rename it to a new name that clearly explains the contents of the variable. value is a too broad name, and is quite confusing.
value = get_adapter().clean_email(value)
What is happening here? You have a print after defining value (bad name again), what does it return? What do you expect it to do?
Point 2: Keep your validation in the clean methods
Your try and except with the ValidationError should be in your form cleaning method. If it does not properly fit in one of the fields that are being cleaned, you should override the clean method, run the super's clean method and add the custom method that validates what you want.
Point 3: The signup method.
I've not seen a place where you call the signup method. It is not inherited from forms.Form, so it is not yet called as far as I can see.
Note: I'm not going to create any new answers to this topic, I will be updating this 'answer'.

Categories

Resources