Я для активации аккаунта использовал механизм восстановления пароля. Получилось, примерно, так.
views.py
...
user.is_active = False
user.save()
context = {'user': user}
current_site = get_current_site(request)
site_name = current_site.name
domain = current_site.domain
use_https = request.is_secure()
token_generator = user_is_active_token_generator
extra_context = {
'domain': domain,
'site_name': site_name,
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'user': user,
'token': token_generator.make_token(user),
'protocol': 'https' if use_https else 'http',
}
context.update(extra_context)
mail_message_template = 'путь до registration_email_user_not_is_active.html'
mail_message = render_to_string(mail_message_template, context)
send_mail('activation JustSolve.kz', mail_message, settings.EMAIL_HOST_USER, ['knursultana@gmail.com'])
tokens.py (он почти такой же как и django.contrib.auth.tokens)
from django.conf import settings
from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.utils import six
from django.utils.crypto import constant_time_compare, salted_hmac
from django.utils.http import base36_to_int, int_to_base36
class UserIsActiveTokenGenerator(PasswordResetTokenGenerator):
def check_token(self, user, token):
"""
Check that a password reset token is correct for a given user.
"""
# Parse the token
try:
ts_b36, hash = token.split("-")
except ValueError:
return False
try:
ts = base36_to_int(ts_b36)
except ValueError:
return False
# Check that the timestamp/uid has not been tampered with
if not constant_time_compare(self._make_token_with_timestamp(user, ts), token):
return False
return True
def _make_token_with_timestamp(self, user, timestamp):
# timestamp is number of days since 2001-1-1. Converted to
# base 36, this gives us a 3 digit string until about 2121
ts_b36 = int_to_base36(timestamp)
# By hashing on the internal state of the user and using state
# that is sure to change (the password salt will change as soon as
# the password is set, at least for current Django auth, and
# last_login will also change), we produce a hash that will be
# invalid as soon as it is used.
# We limit the hash to 20 chars to keep URL short
key_salt = "django.contrib.auth.tokens.PasswordResetTokenGenerator"
# Ensure results are consistent across DB backends
joined_timestamp = '' if user.date_joined is None else user.date_joined.replace(microsecond=0, tzinfo=None)
value = (six.text_type(user.pk) +
six.text_type(joined_timestamp) + six.text_type(timestamp))
hash = salted_hmac(key_salt, value).hexdigest()[::2]
return "%s-%s" % (ts_b36, hash)
user_is_active_token_generator = UserIsActiveTokenGenerator()
registration_email_user_not_is_active.html
Dear, {{ user.username }}!<br>
You join to {{ domain }}!<br>
url to activate <a href="{{ protocol }}://{{ domain }}/activate/{{ uid }}/{{ token }}/">{{ protocol }}://{{ domain }}/activate/{{ uid }}/{{ token }}/</a>
welcome!
urls.py
...
url(r'^activate/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
views.activate, name='activate')
...
views.py
def activate(request, uidb64=None, token=None,
token_generator=user_is_active_token_generator,
template_name='common/activate.html'):
UserModel = get_user_model()
assert uidb64 is not None and token is not None # checked by URLconf
try:
# urlsafe_base64_decode() decodes to bytestring on Python 3
uid = force_text(urlsafe_base64_decode(uidb64))
user = UserModel._default_manager.get(pk=uid)
except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist):
user = None
if user is not None and user.is_active:
validlink = True
title = _('Link no longer relevant')
elif user is not None and token_generator.check_token(user, token):
user.is_active = True
user.save()
validlink = True
title = _('Account activated')
else:
validlink = False
title = _('Link not correct')
context = {
'title': title,
'validlink': validlink,
}
return TemplateResponse(request, template_name, context)
При таком подходе не понадобится дополнительная модель Activation или какие-то поля в бд.
# Проверки должны находиться в django forms, а не во вьюхе.
validate_email(email)
len(password) >= 5
password != password2
User.objects.filter(username=email, is_active=True).exists()