32π
I figured out a solution , but for the second requirement user has to input the password at the time of account creation . The main goal was to verify the user supplied email.
Models
class Yourmodel(models.Model):
first_name = models.CharField(max_length=200)
second_name = models.CharField(max_length=200)
email = models.EmailField(max_length=100)
Tokens
from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.utils import six
class TokenGenerator(PasswordResetTokenGenerator):
def _make_hash_value(self, user, timestamp):
return (
six.text_type(user.pk) + six.text_type(timestamp) +
six.text_type(user.is_active)
)
account_activation_token = TokenGenerator()
Views
from django.contrib.auth import get_user_model
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.contrib.sites.shortcuts import get_current_site
from .tokens import account_activation_token
from django.core.mail import send_mail
from django.utils.encoding import force_bytes
from django.template.loader import render_to_string
def signup(request):
User = get_user_model()
if request.method == 'POST':
form = SignupForm(request.POST)
if form.is_valid():
email = form.cleaned_data.get('email')
if Yourmodel.objects.filter(email__iexact=email).count() == 1:
user = form.save(commit=False)
user.is_active = False
user.save()
current_site = get_current_site(request)
mail_subject = 'Activate your account.'
message = render_to_string('email_template.html', {
'user': user,
'domain': current_site.domain,
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'token': account_activation_token.make_token(user),
})
to_email = form.cleaned_data.get('email')
send_mail(mail_subject, message, 'youremail', [to_email])
return HttpResponse('Please confirm your email address to complete the registration')
else:
form = SignupForm()
return render(request, 'regform.html', {'form': form})
def activate(request, uidb64, token):
User = get_user_model()
try:
uid = force_text(urlsafe_base64_decode(uidb64))
user = User.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 HttpResponse('Thank you for your email confirmation. Now you can login your account.')
else:
return HttpResponse('Activation link is invalid!')
Forms
from django.contrib.auth.forms import UserCreationForm
class SignupForm(UserCreationForm):
class Meta:
model = User
fields = ('username', 'email', 'password1', 'password2')
Email Template
{% autoescape off %}
Hi ,
Please click on the link to confirm your registration,
http://{{ domain }}{% url 'activate' uidb64=uid token=token %}
{% endautoescape %}
regform.html
{% csrf_token %}
{% for field in form %}
<label >{{ field.label_tag }}</label>
{{ field }}
{% endfor %}
If you donβt want to compare with email address in your model you can
skip, this will send the email to the email address which was supplied
at the time registration without further validation.
email = form.cleaned_data.get('email')
if Yourmodel.objects.filter(email__iexact=email).count() == 1:
4π
For the first answer, you need to add on urls.py
path('emailVerification/<uidb64>/<token>', views.activate, name='emailActivate')
And emailVerification.html must be like this:
Hi ,
Please click on the link to confirm your registration,
http://{{ domain }}/emailVerification/{{ uid }}/{{ token }}
- Filter on datetime closest to the given datetime
- What is Serializers to_internal_value method used for in Django
- Django: "order" a queryset based on a boolean field
- Web visitor statistics for Django?
2π
I have an answer on your first problem:
If you user password reset based on the PasswordResetView + PasswordResetConfirmView you could do following:
PasswordResetView is in charge for sending emails to the users. It uses it own form for typing in user emails -PasswordResetForm. You could make your own form and inherit it from PasswordResetForm.
For example:
class PRForm(PasswordResetForm):
def clean_email(self):
email = self.cleaned_data['email']
if not User.objects.filter(email__iexact=email, is_active=True).exists():
msg = "There is no user with this email."
self.add_error('email', msg)
return email
# User β your user model or any custom model if you have one instead of the default one
this code will not allow controller to send email to a email address that you dont have in your DB.
Then specify this form in your VIEW:
class PassResView(RatelimitMixin, PasswordResetView):
success_url =
from_email =
subject_template_name =
email_template_name =
success_message =
template_name =
form_class = PRForm # here is a custom form
ratelimit_key = 'ip'
ratelimit_rate = '10/5m'
ratelimit_block = True
ratelimit_method = ('GET', 'POST')
RatelimitMixin will not allow someone to brute-force your DB by running your BD out. Your could use it or not -its up to you.
1π
I did this with the help of itsdangerous
and signals
token_generator.py
import hashlib
from typing import NoReturn, Union
from django.conf import settings
from itsdangerous import URLSafeTimedSerializer
from itsdangerous.exc import BadTimeSignature, SignatureExpired
serializer = URLSafeTimedSerializer(settings.SECRET_KEY, salt="active-email")
serializer.default_signer.default_digest_method = hashlib.sha256
MAX_AGE : Final = 60 * 60 * 3 # The token is valid for just 3 hours
class ExpiredToken(Exception):
pass
class BadToken(Exception):
pass
def generate_token(user_id: int) -> bytes:
return serializer.dumps(user_id)
def validate_token(token: Union[str , bytes], max_age: int = MAX_AGE) -> Union[int, NoReturn]:
try:
data = serializer.loads(token, max_age=max_age)
except SignatureExpired:
raise ExpiredToken("Token has expired. request for another token.")
except BadTimeSignature:
raise BadToken("Token is invalid.")
return data
I just take userβs pk
and sign it with it itsdangerous
signals.py
from django.conf import settings
from django.core.mail import send_mail
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.template.loader import render_to_string
from .token_generator import generate_token
@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def send_verify_email(instance, created, **kwargs):
if not created or instance.is_active:
return
domain = settings.ALLOWED_HOSTS[0] or 'http://localhost:8000'
token = generate_token(instance.id)
message = render_to_string(
"users/verify_email.html",
context={"token": token, "base_url": domain},
)
send_mail(
"Verify Email Subject",
message=message,
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=[instance.email],
)
settings.py
DEFAULT_FROM_EMAIL = 'no-replay@mysite.com'
verify_email.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>title</title>
</head>
<body>
<a href="{{base_url}}{% url 'verify-email' %}?token={{ token }}">verify</a>
</body>
</html>
views.py
from django.contrib.auth import get_user_model
from .token_generator import BadToken , ExpiredToken , validate_token
rom django.views import View
class EmailVerify(View):
def get(self, request : HttpRequest) -> HttpResponse:
UserModel = get_user_model()
token = request.GET.get('token')
if not token:
return HttpResponseBadRequest('Error')
try:
user_id = validate_token(token)
except BadToken:
return HttpResponseBadRequest('Error')
except ExpiredToken:
return HttpResponseBadRequest('Error')
user = UserModel.objects.get(pk=user_id)
if user.email_verified:
return HttpResponseBadRequest('Error')
user.is_active = True
user.save()
return HttpResponse('Your account has been activated')
urls.py
from django.urls import path
from . import views as userview
urlpatterns = [
path("verify-email/", userview.EmailVerify.as_view(), name="verify-email"),
]