首页 > 解决方案 > Django Rest Auth - 社交视图的自定义注册逻辑

问题描述

我正在使用 Django Rest Framework 和 Django Rest Auth 构建一个 REST API。

我的用户有一个消费者资料。

class UserConsumerProfile(
    SoftDeletableModel,
    TimeStampedModel,
    UniversallyUniqueIdentifiable,
    Userable,
    models.Model
):
    def __str__(self):
        return f'{self.user.email} ({str(self.uuid)})'

正如你所看到的,它由一些 mixin 组成,它们为它提供了一个 UUID、一个时间戳和一个更新的字段以及与用户的 OneToOne 关系。我在关系中使用此消费者配置文件将数据链接到用户。

此消费者资料应在用户注册后立即创建。

这是我为注册编写的序列化程序:

from profiles.models import UserConsumerProfile
from rest_auth.registration.serializers import RegisterSerializer

class CustomRegisterSerializer(RegisterSerializer):
    def custom_signup(self, request, user):
        profile = UserConsumerProfile.objects.create(user=user)
        profile.save()

我在设置中连接了这个序列化器:

REST_AUTH_REGISTER_SERIALIZERS = {
    "REGISTER_SERIALIZER": "accounts.api.serializers.CustomRegisterSerializer"
}

当用户使用他的电子邮件注册时,它可以完美运行。但当他使用 facebook 注册时,并没有创建任何消费者资料。

我认为社交视图在创建用户时也会使用寄存器序列化程序?社交注册后如何运行自定义逻辑?

编辑赏金:

以下是我用于 Django Rest Auth 的设置:

# django-allauth configuration

ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_CONFIRM_EMAIL_ON_GET = True
ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS = 1
ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE = True
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
ACCOUNT_ADAPTER = 'accounts.adapter.CustomAccountAdapter'

SOCIALACCOUNT_ADAPTER = 'accounts.adapter.CustomSocialAccountAdapter'
SOCIALACCOUNT_PROVIDERS = {
    'facebook': {
        'METHOD': 'oauth2',
        'SCOPE': ['email', 'public_profile', 'user_friends'],
        'AUTH_PARAMS': {'auth_type': 'reauthenticate'},
        'INIT_PARAMS': {'cookie': True},
        'FIELDS': [
            'id',
            'email',
            'name',
            'first_name',
            'last_name',
            'verified',
            'locale',
            'timezone',
            'link',
            'gender',
            'updated_time',
        ],
        'EXCHANGE_TOKEN': True,
        'LOCALE_FUNC': 'path.to.callable',
        'VERIFIED_EMAIL': True,
        'VERSION': 'v2.12',
    }
}

# django-rest-auth configuration

REST_SESSION_LOGIN = False
OLD_PASSWORD_FIELD_ENABLED = True

REST_AUTH_SERIALIZERS = {
    "TOKEN_SERIALIZER": "accounts.api.serializers.TokenSerializer",
    "USER_DETAILS_SERIALIZER": "accounts.api.serializers.UserDetailSerializer",
}

REST_AUTH_REGISTER_SERIALIZERS = {
    "REGISTER_SERIALIZER": "accounts.api.serializers.CustomRegisterSerializer"
}

这是自定义适配器(以防万一):

from allauth.account.adapter import DefaultAccountAdapter
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
from allauth.utils import build_absolute_uri
from django.http import HttpResponseRedirect
from django.urls import reverse


class CustomAccountAdapter(DefaultAccountAdapter):

    def get_email_confirmation_url(self, request, emailconfirmation):
        """Constructs the email confirmation (activation) url."""
        url = reverse(
            "accounts:account_confirm_email",
            args=[emailconfirmation.key]
        )
        ret = build_absolute_uri(
            request,
            url
        )
        return ret

    def get_email_confirmation_redirect_url(self, request):
        """
        The URL to return to after successful e-mail confirmation.
        """
        url = reverse(
            "accounts:email_activation_done"
        )
        ret = build_absolute_uri(
            request,
            url
        )
        return ret

    def respond_email_verification_sent(self, request, user):
        return HttpResponseRedirect(
            reverse('accounts:account_email_verification_sent')
        )


class CustomSocialAccountAdapter(DefaultSocialAccountAdapter):
    def get_connect_redirect_url(self, request, socialaccount):
        """
        Returns the default URL to redirect to after successfully
        connecting a social account.
        """
        assert request.user.is_authenticated
        url = reverse('accounts:socialaccount_connections')
        return url

最后,说说观点:

from allauth.socialaccount.providers.facebook.views import \
    FacebookOAuth2Adapter
from rest_auth.registration.views import SocialConnectView, SocialLoginView


class FacebookLogin(SocialLoginView):
    adapter_class = FacebookOAuth2Adapter


class FacebookConnect(SocialConnectView):
    adapter_class = FacebookOAuth2Adapter

我认为如果我像在问题的初始部分中那样连接序列化器,那么当有人使用 facebook 注册时,寄存器序列化器逻辑也会运行。

当有人使用 facebook 注册时,我需要做什么才能使该逻辑也运行?

(如果我无法修复它,我可以在每个 facebook 在客户端注册创建 userconsumerprofile 后发出第二个服务器请求,但这有点矫枉过正,并且会引入新的代码表面,从而导致更高的错误可能性.)

标签: djangodjango-rest-frameworkdjango-allauthdjango-rest-auth

解决方案


简要看一下,DefaultAccountAdapterDefaultSocialAccountAdapter可能有机会在您的 CustomAccountAdapter/CustomSocialAccountAdapter 中覆盖/实现 save_user(..) 以设置配置文件?

只看代码,似乎DefaultSocialAccountAdapter.save_user最终会调用DefaultAccountAdapter.save_user.

可能是这样的?

class CustomAccountAdapter(DefaultAccountAdapter):
    def save_user(self, request, user, form, commit=True):
        user = super(CustomAccountAdapter, self).save_user(request, user, form,
                                                           commit)
        UserConsumerProfile.objects.get_or_create(user=user)

        return user

如果 save_user 不适用于您的场景,适配器中还有一些其他“钩子”/功能可能值得我们研究。


推荐阅读