简体   繁体   中英

How to tell if user's email address has been verified using Django, allauth, rest-auth and a custom user

I'm using Django 2.0.10 with rest-framework, rest-auth and allauth. I have a custom user model.

I've got email verification working by using the allauth view. The verification email is sent when a user registers. If I click the link in the email, I'm taken to a page with a button to click to verify the email. This all works without error. However what I can't find out is what this actually does . No field in the user's data seems to change.

The behaviour I want is for users to be able to register and login, but only to be able to add content to the site after they have verified their email.

Edit: this post gives part of the answer but doesn't say how to save the verification status as a property of the user so that you can check it in the front end when you load the user data.

settings.py

# django rest auth
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_USERNAME_REQUIRED = False
OLD_PASSWORD_FIELD_ENABLED = True
LOGOUT_ON_PASSWORD_CHANGE = False
ACCOUNT_EMAIL_VERIFICATION = 'optional'

api/urls.py

from allauth.account.views import confirm_email

urlpatterns = [
    re_path(r'^rest-auth/registration/account-confirm-email/(?P<key>[-:\w]+)/$', confirm_email,
     name='account_confirm_email'),
...
]

users/models.py

import uuid 

from django.contrib.auth.models import AbstractUser, UserManager
from django.db import models
from django.utils.http import int_to_base36

class CustomUserManager(UserManager):
    def get_by_natural_key(self, username):
        case_insensitive_username_field = '{}__iexact'.format(self.model.USERNAME_FIELD)
        return self.get(**{case_insensitive_username_field: username})

ID_LENGTH = 12

def pkgen():
    from base64 import b32encode
    from hashlib import sha1
    from random import random

    pk = int_to_base36(uuid.uuid4().int)[:ID_LENGTH]
    return pk

class CustomUser(AbstractUser):
    objects = CustomUserManager()
    slug = models.CharField(max_length=ID_LENGTH, default=pkgen, editable=False)
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

    def __str__(self):
        return self.email

When a user logs in, how can I find out if they have verified their email address? Thanks for any help!

Aha! Thanks to this post and this post , I think I have an answer.

The email address's status is saved in a separate table EmailAdress, not as part of the User model. This can be accessed in a modelviewset as follows:

api.py

from allauth.account.admin import EmailAddress

class ListViewSet(viewsets.ModelViewSet):
    ...

    def get_queryset(self):
        # can view public lists and lists the user created
        if self.request.user.is_authenticated:
            print('is there a verified email address?')
            print(EmailAddress.objects.filter(user=self.request.user, verified=True).exists())

            ...

This will return True if the user has any verified email address.

However, it's much more useful to add the verification status to the user. This can be done with a signal as explained here .

views.py

from allauth.account.signals import email_confirmed
from django.dispatch import receiver

@receiver(email_confirmed)
def email_confirmed_(request, email_address, **kwargs):
    user = email_address.user
    user.email_verified = True

    user.save()

Now in api.py you can check like this:

print(self.request.user.email_verified)

This works if you have a single email address that can't be changed or deleted. If you allow multiple email addresses I guess you'd need to make more checks and update the user's status accordingly. But I have only a single email address which is used for login, so I think that's OK.

I think it would be better practice to make 'email_verified' part of a user profile, but this is a working demo.

I had the same problem, I was able to solve this by using the code below:

#Project-level folder urls.py
from django.contrib import admin
from django.urls import path, include
from allauth.account.views import ConfirmEmailView, EmailVerificationSentView 
#These ConfirmEmailView, EmailVerificationSentView are very important
#I used other allauth/ dj-rest-auth views and they didn't automatically verify the email.

urlpatterns = [
path('admin/', admin.site.urls),
path('dj-rest-auth/registration/account-confirm-email/<str:key>/',
    ConfirmEmailView.as_view()), #This is at the top because apparently won't work if below. #Integral to problem solution

path('dj-rest-auth/', include('dj_rest_auth.urls')),
path('dj-rest-auth/registration/', include('dj_rest_auth.registration.urls')),
path('api-auth', include('rest_framework.urls')),

 path('dj-rest-auth/registration/account-confirm-email/', 
  EmailVerificationSentView.as_view(),
        name='account_email_verification_sent'),#Integral to problem solution

]

The code above allowed me to create new users with the registration url. After which are sent an email with a link. When users click on the link they are redirected to the login page, with their email now verified in the database.

Try SerializerMethodField

The official example:

from django.contrib.auth.models import User
from django.utils.timezone import now
from rest_framework import serializers

class UserSerializer(serializers.ModelSerializer):
    days_since_joined = serializers.SerializerMethodField()

    class Meta:
        model = User
        fields = '__all__'

    def get_days_since_joined(self, obj):
        return (now() - obj.date_joined).days

There's a function for this.

Syntax:

from allauth.account.utils import has_verified_email
has_verified_email(user, email=None) -> bool

According to the source code this does the same thing as what the accepted answer does when email is None . Otherwise it checks if the user has that verified email.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM