Как определить, был ли адрес электронной почты пользователя подтвержден с использованием Django, allauth, rest-auth и пользовательского пользователя

Я использую Django 2.0.10 с rest-framework, rest-auth и allauth. У меня есть пользовательская модель.

У меня есть проверка электронной почты с использованием представления Аллаута. Письмо с подтверждением отправляется, когда пользователь регистрируется. Если я нажму на ссылку в электронном письме, я попаду на страницу с кнопкой, чтобы проверить электронную почту. Это все работает без ошибок. Однако, что я не могу выяснить, что это на самом деле делает. Нет поля в данных пользователя, кажется, изменить.

Я хочу, чтобы пользователи могли регистрироваться и входить в систему, но добавлять контент на сайт только после того, как они подтвердили свою электронную почту.

Редактировать: этот пост дает часть ответа, но не говорит, как сохранить статус проверки как свойство пользователя, чтобы вы могли проверить его во внешнем интерфейсе при загрузке пользовательских данных.

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'

апи / 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'),
...
]

пользователи / 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

Когда пользователь входит в систему, как я могу узнать, подтвердили ли они свой адрес электронной почты? Спасибо за любую помощь!

4 ответа

Решение

Ага! Благодаря этому посту и этому посту, я думаю, что у меня есть ответ.

Статус адреса электронной почты сохраняется в отдельной таблице EmailAdress, а не в составе модели User. Это можно получить в наборе моделей следующим образом:

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

            ...

Это вернет True, если у пользователя есть подтвержденный адрес электронной почты.

Однако гораздо полезнее добавить статус проверки пользователю. Это можно сделать с помощью сигнала, как описано здесь.

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

Теперь в api.py вы можете проверить это так:

print(self.request.user.email_verified)

Это работает, если у вас есть один адрес электронной почты, который нельзя изменить или удалить. Если вы разрешите несколько адресов электронной почты, я думаю, вам нужно будет сделать больше проверок и соответственно обновить статус пользователя. Но у меня есть только один адрес электронной почты, который используется для входа в систему, поэтому я думаю, что все в порядке.

Я думаю, что было бы лучше сделать 'email_verified' частью профиля пользователя, но это рабочая демонстрация.

Для этого есть функция.

Синтаксис:

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

Согласно исходному коду, это делает то же самое, что и принятый ответ, когдаemailявляетсяNone. В противном случае он проверяет, есть ли у пользователя подтвержденный адрес электронной почты.

У меня была такая же проблема, я смог решить ее, используя приведенный ниже код:

      #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

]

Приведенный выше код позволил мне создавать новых пользователей с адресом регистрации. После чего отправляется электронное письмо со ссылкой. Когда пользователи нажимают на ссылку, они перенаправляются на страницу входа, а их адрес электронной почты проверяется в базе данных.

Попробуйте SerializerMethodField

Официальный пример:

      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
Другие вопросы по тегам