如何在DRF + django-rest-auth

时间:2018-12-29 12:10:17

标签: django django-rest-framework django-allauth django-rest-auth

使用带有django-rest-auth的Django REST Framework(DRF),我创建了一个带有一个额外字段的自定义用户模型。 我的目标是使用django-rest-auth注册端点在一个请求中注册一个新用户,从而发送所有数据以创建一个新用户,包括多余字段的数据。 < / p>

我使用的是AbstractUser,因为它似乎是推荐给初学者的,在初学者中,更高级的开发人员可以使用AbstractBaseUser。这也是为什么以下SO答案对我想要实现的目标来说太复杂了:link here

我知道这个问题已经被问过多次了,但是答案并不完全是我想要的。对于像我这样的初学者来说,这是复杂的事情。

所以,我的问题是,任何人都可以解释如何实现我想要的吗?

我正在使用:

Django              2.1.4
django-allauth      0.38.0
django-rest-auth    0.9.3
djangorestframework 3.9.0

这是到目前为止我掌握的代码:

使用this tutorial来获取此代码

settings.py:

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '!gxred^*penrx*qlb=@p)p(vb!&6t78z4n!poz=zj+a0_9#sw1'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'rest_framework',
    'rest_framework.authtoken',

    'rest_auth',

    'django.contrib.sites',
    'allauth',
    'allauth.account',
    'rest_auth.registration',

    'users',
]

SITE_ID = 1

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'DRF_custom_user.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'DRF_custom_user.wsgi.application'


# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}


# Password validation
# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/2.1/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/

STATIC_URL = '/static/'

AUTH_USER_MODEL = 'users.CustomUser'

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

users.models.py:

from django.contrib.auth.models import AbstractUser
from django.db import models


class CustomUser(AbstractUser):
    preferred_locale = models.CharField(blank=True, null=True, max_length=2)

users.admin.py:

from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin

from .forms import CustomUserCreationForm, CustomUserChangeForm
from .models import CustomUser

class CustomUserAdmin(UserAdmin):
    add_form = CustomUserCreationForm
    form = CustomUserChangeForm
    model = CustomUser
    list_display = ['email', 'preferred_locale']

admin.site.register(CustomUser, CustomUserAdmin)

users.forms.py:

from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser


class CustomUserCreationForm(UserCreationForm):

    class Meta(UserCreationForm):
        model = CustomUser
        fields = ('email', )


class CustomUserChangeForm(UserChangeForm):

    class Meta:
        model = CustomUser
        fields = UserChangeForm.Meta.fields

2 个答案:

答案 0 :(得分:2)

我自己去寻找答案。花一些时间来研究源代码。我意识到该解决方案可能会缺少对添加到自定义用户模型中的额外字段的实际验证,但是稍后我会进行研究。

我在下面写的内容是在考虑潜在博客帖子的情况下写的。

我将假设您知道如何设置DRF项目并安装上述软件包。 django-rest-auth文档明确说明了如何安装该软件包(https://django-rest-auth.readthedocs.io/en/latest/index.html),请确保还遵循以下步骤安装django-rest-auth的一部分以进行用户注册。

“用户”创建新应用

此应用将保存我的用于实现自定义用户模型的自定义代码。我还将其安装在Django主要设置文件中:

settings.py:

INSTALLED_APPS = [
    ...
    'users',
]

创建我的自定义用户模型

请注意,我刚刚添加了一个自定义字段,但是您可以添加任何您想要的字段。

users.models.py:

from django.contrib.auth.models import AbstractUser
from django.db import models


class CustomUser(AbstractUser):
    preferred_locale = models.CharField(max_length=2, blank=True, null=True)

告诉Django使用CustomUser模型

settings.py:

…
AUTH_USER_MODEL = 'users.CustomUser'

在Django管理员处注册自定义用户模型

users.admin.py:

from django.contrib import admin

from .models import CustomUser


admin.site.register(CustomUser)

进行迁移并运行

这是我第一次为此项目做。

在命令行中:

python manage.py makemigrations users
python manage.py migrate

使用其他字段注册新用户

如果现在启动Django开发服务器,您将在管理员中看到带有附加字段的自定义用户模型。

但是当您转到“ http://127.0.0.1:8000/rest-auth/registration/”时,您还没有看到额外的字段。

在用户注册过程中,使用了两个重要的类,即:

  • 序列化器“ rest_auth.registration.RegisterSerializer”
  • 适配器‘allauth.account.adapter.DefaultAccountAdapter’

我们将为这两者定制一个版本,并继承其父类的所有功能。

创建自定义RegisterSerializer

在用户应用/文件夹中创建一个新文件“ serializers.py”。

users.serializers.py:

from rest_framework import serializers

from allauth.account.adapter import get_adapter
from allauth.account.utils import setup_user_email

from rest_auth.registration.serializers import RegisterSerializer


class CustomRegisterSerializer(RegisterSerializer):
    preferred_locale = serializers.CharField(
        required=False,
        max_length=2,
    )

    def get_cleaned_data(self):
        data_dict = super().get_cleaned_data()
        data_dict['preferred_locale'] = self.validated_data.get('preferred_locale', '')
        return data_dict

在这里,我为自定义用户模型上的每个其他字段创建一个新字段。因此,在我的情况下,添加了以下内容:

preferred_locale = serializers.CharField(
        required=False,
        max_length=2,
    )

此外,get_cleaned_data方法应返回一个dict,其中包含注册新用户时要保存的字段的所有数据。

这是原始方法(默认RegisterSerializer的样子):

def get_cleaned_data(self):
    return {
        'username': self.validated_data.get('username', ''),
        'password1': self.validated_data.get('password1', ''),
        'email': self.validated_data.get('email', '')
    }

如您所见,它返回一个字典,其中包含新用户的所有数据。您想为已添加到自定义用户模型中的每个额外字段向此字典添加键值条目。

对于我来说,只需为“ preferred_locale”字段添加数据,这就是结果方法:

def get_cleaned_data(self):
    data_dict = super().get_cleaned_data()
    data_dict['preferred_locale'] = self.validated_data.get('preferred_locale', '')
    return data_dict

告诉Django使用此新的序列化程序

settings.py:

REST_AUTH_REGISTER_SERIALIZERS = {
    'REGISTER_SERIALIZER': 'users.serializers.CustomRegisterSerializer',
}

防止错误

如果尝试注册新用户,则在运行开发服务器的控制台中可能会收到以下错误: ConnectionRefusedError:[Errno 111]连接被拒绝

尽管仍在创建用户,但是您可以通过将以下行添加到settings.py文件中来解决此错误:

settings.py:

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

删除用户时将发生的另一个错误是:

django.db.utils.OperationalError: no such table: allauth_socialaccount

要解决此问题,请将其添加到您的settings.py:

settings.py:

INSTALLED_APPS = [
    ...
    'allauth.socialaccount',  
]

在那之后,您应该先应用迁移,然后才能继续:

python manage.py migrate

创建自定义AccountAdapter

完成上述步骤后,转到“ http://127.0.0.1:8000/rest-auth/registration/”将为您显示其他字段。但是,当您注册新用户并发送多余字段的数据时,多余字段的数据不会保存。

要解决此问题,我们要做的最后一件事是创建一个自定义AccountAdapter

在我们的用户应用/文件夹中,创建一个名为“ adapter.py”的新文件:

users.adapter.py:

from allauth.account.adapter import DefaultAccountAdapter


class CustomAccountAdapter(DefaultAccountAdapter):

    def save_user(self, request, user, form, commit=False):
        user = super().save_user(request, user, form, commit)
        data = form.cleaned_data
        user.preferred_locale = data.get('preferred_locale')
        user.save()
        return user

在这里,如果您正确地执行了上述步骤,则可以访问form.cleaned_data词典中添加的额外字段的数据。这是由自定义RegisterSerializer中的get_cleaned_data方法返回的字典。

在上面的save_user方法中,我们可以使用此数据并将其保存到适当的字段中,如下所示:

user.preferred_locale = data.get('preferred_locale')

告诉Django使用此新适配器

settings.py:

ACCOUNT_ADAPTER = 'users.adapter.CustomAccountAdapter'

现在,您可以使用django-rest-auth注册端点'/ rest-auth / registration /'注册用户,并发送添加的其他字段的数据。这将全部保存在一个请求中。

同样,我意识到需要为每个字段添加自定义验证。但这是我以后将要探讨的另一个主题,当我发现它的工作原理时,将对其进行更新。

答案 1 :(得分:1)

让我们打破您的问题。请注意,我正在向您解释Django REST Framework的基础。

覆盖用户模型

  • [x]步骤1:您覆盖User模型:您是这样做的。另类?是。创建一个具有OneToOneForeignKey指向User模型的模型。
  • [x]步骤2:使用此CustomUserModel。为此,您需要在AUTH_USER_MODEL Link to official documentation中设置settings.py
  • []步骤3:创建一个UserManager来处理用户的注册和其他信息。您还没有这样做。

通过API注册

  • []创建一个serializer,其中清楚提及最终用户期望的所有必填字段。如果没有自定义字段,您甚至可以使用serializer.ModelSerializer
  • []处理serializer本身中的显式验证。如果需要,请使用def validate(self, attrs)。这是official document链接。
  • []最后,创建一个视图并使用APIView,因为您想使用上面创建的UserManager注册用户。

我还可以参考您自己开发的应用程序。这里是链接:DRF-USER。我在某种程度上自定义了User模型,并遵循相同的过程。

希望这会有所帮助。