如何在DRF + django-rest-auth中使用自定义用户模型保存有关注册的额外字段 [英] How to save extra fields on registration using custom user model in DRF + django-rest-auth

查看:88
本文介绍了如何在DRF + django-rest-auth中使用自定义用户模型保存有关注册的额外字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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



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



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



我正在使用:

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

这是到目前为止的代码:



使用了本教程以获取此代码



settings.py:

  import os 

#在项目内部构建路径,如下所示:os.path .join(BASE_DIR,...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__ file__)))


#快速启动开发设置-不适合生产
#参见https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/

#SECURIT Y警告:对生产中使用的密钥保密!
SECRET_KEY ='!gxred ^ * penrx * qlb = @ p)p(vb!& 6t78z4n!poz = zj + a0_9#sw1'

#安全警告:不要运行在生产中启用调试的情况下
DEBUG =真

ALLOWED_HOSTS = []


#应用程序定义

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 ',

'用户',
]

SITE_ID = 1

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

ROOT_URLCONF ='DRF_custom_user.urls'

模板= [
{
'BACKEND':'django.template.backends。 django.DjangoTemplates',
'DIRS':[],
'APP_DIRS':真,
'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'


#数据库
#https://docs.djangoproject.com/zh/2.1/ref/settings/#database s

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


#密码验证
#https://docs.djangoproject。 com / zh-CN / 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',
} ,
]


#国际化
#https://docs.djangoproject.com/en/2.1/topics/i18n/

LANGUAGE_CODE ='en-us'

TIME_ZONE ='UTC'

USE_I18N =真实

USE_L10N =真

USE_TZ =真


#静态文件(CSS,JavaScript,图像)
#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:

 从django.contrib.auth.models导入AbstractUser 
从django.db导入models


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

users.admin.py:

 从django.contrib导入管理员
从django.contrib.auth导入get_user_model
从django.contrib.auth.admin导入UserAdmin

从.forms从.models导入CustomUserCreationForm,CustomUserChangeForm
导入CustomUser

类Cus tomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
表单= CustomUserChangeForm
模型= CustomUser
list_display = ['email','preferred_locale']

admin .site.register(CustomUser,CustomUserAdmin)

users.forms.py:

 从django导入表单
从django.contrib.auth.forms import UserCreationForm,UserChangeForm
从.models import CustomUser


class CustomUserCreationForm(UserCreationForm):

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


类CustomUserChangeForm(UserChangeForm):

类元:
模型= CustomUser
字段= UserChangeForm.Meta .fields


解决方案

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



我在下面编写的内容



我将假设您知道如何设置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:

 来自Django。 contrib.auth.models从django.db导入AbstractUser 
导入模型


类CustomUser(AbstractUser):
preferred_locale = models.CharField(max_length = 2,空白= True,null = True)

告诉Django使用CustomUser模型



settings.py:

 
AUTH_USER_MODEL = 'users.CustomUser'

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

users.admin.py:

 来自django.con从.models导入trib导入admin 

导入CustomUser


admin.site.register(CustomUser)

进行迁移并运行迁移



这是我第一次这样做



在命令行中:

  python manage。 py makemigrations用户
python manage.py migration

注册具有额外字段的新用户



如果现在启动Django开发服务器,您将在admin中看到可以看到自定义用户模型以及其他字段。 / p>

但是当您转到' http: //127.0.0.1:8000/rest-auth/registration/ ',您还没有看到其他字段。



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




  • 序列化器'rest_auth.registration.RegisterSe rializer'

  • 适配器'allauth.account.adapter.DefaultAccountAdapter'



制作这两个版本的自定义版本,并继承其父类的所有功能。



创建自定义RegisterSerializer



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



users.serializers.py:

从rest_framework导入序列化器

 从allauth.account.adapter导入

从allauth.account.utils导入get_adapter
从rest_auth.registration.serializers导入setup_user_email

import RegisterSerializer


类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.valida ted_data.get('preferred_locale','')
return data_dict

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

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

此外,get_cleaned_data方法应返回包含所有内容的字典注册新用户时要保存的字段的数据。



这是原始方法(默认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','')
}

您会看到它返回了一个字典,其中包含新用户的所有数据。您想为添加到自定义用户模型中的每个额外字段添加一个keyval条目到字典中。



在我的情况下,只需要为该字段添加数据'preferred_locale',这是结果方法:

  def get_cleaned_data(self):
data_dict = super()。 get_cleaned_data()
data_dict ['preferred_locale'] = self.validated_data.get('preferred_locale','')
返回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:没有这样的表:allauth_socialaccount 

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



settings.py:

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

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

  python manage.py migration 

创建自定义AccountAdapter



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



解决此问题的最后一件事是创建自定义AccountAdapter



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



users.adapter.py:

 从allauth.account.adapter导入DefaultAccountAdapter 


类CustomAccountAdapter(DefaultAccountAdapter):

def save_user(自身,请求,用户,表单,提交= False):
用户= super()。save_user(请求,用户,表单) ,commit)
data = form.cleaned_data
user.preferred_locale = data.get('preferred_locale')
user.save()
返回用户

在这里,如果正确执行了上述步骤,则可以访问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 /'注册用户,并发送您添加的其他字段的数据。



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


Using Django REST Framework (DRF), with django-rest-auth, I created a custom user model with one extra field. My aim is to use the django-rest-auth registration endpoint to register a new user in one request, and thus sending all the data to create a new user, including the data for the extra field.

I am using AbstractUser, since it seems recommended for beginners, where more advanced developers could use AbstractBaseUser. This is also why the following SO answers looks too complicated for what I want to achieve: link here.

I know this question has been asked multiple times, but the answers are not exactly what I am looking for. For a beginner like me this is complicated stuff.

So, my question is, can anyone explain how to achieve what I want?

I am using:

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

Here's the code that I have up until this point:

Used this tutorial to get to this code

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

解决方案

I went looking for an answer myself. Spend some time digging in the source code. I realize this solution may be missing the actual validation for the extra fields added to the custom user model, but I will look into that later.

What I have written below I wrote with a potential blog post in mind.

I am going to assume you know how to set up a DRF project and install the above packages. The django-rest-auth documentation is clear on how to install that package (https://django-rest-auth.readthedocs.io/en/latest/index.html), make sure to also follow the steps to install the part of django-rest-auth for user registration.

Create a new app ‘users’

This app will hold my custom code for implementing the custom user model. I also install it in the Django main settings file:

settings.py:

INSTALLED_APPS = [
    ...
    'users',
]

Create my custom user model

Notice that I just added one custom field, but you can add whatever fields you want ofcourse.

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)

Tell django to use the CustomUser model

settings.py:

…
AUTH_USER_MODEL = 'users.CustomUser'

Register Custom user model at Django admin

users.admin.py:

from django.contrib import admin

from .models import CustomUser


admin.site.register(CustomUser)

Make migrations and run them

This is the first time I do this for this project.

In command line:

python manage.py makemigrations users
python manage.py migrate

Registering new users with extra fields

If you start the Django development server now, you’ll see in the admin that you can see the custom user model, with the extra fields.

But when you go to ‘http://127.0.0.1:8000/rest-auth/registration/’ you don’t see the extra fields yet.

In the process of user registration two important classes are used, namely:

  • a serializer ‘rest_auth.registration.RegisterSerializer’
  • an adapter ‘allauth.account.adapter.DefaultAccountAdapter’

We’ll make a custom version of both of these, inheriting all the functionality of it’s parent class.

Creating a custom RegisterSerializer

Create a new file ‘serializers.py’ in the users app/folder.

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

Here I create a new field for each extra field on the custom user model. So in my case a added this:

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

Also, the get_cleaned_data method should return a dict which contains all the data for the fields that you want to have saved when registering a new user.

This is what the original method (of the default RegisterSerializer looks like):

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

As you can see it returns a dictionary, containing all the data for the new user. You want to add a keyval entry to this dictionary for each extra field you have added to your custom user model.

In my case, needing only to add data for the field ‘preferred_locale’, this is the resulting method:

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

Tell django to use this new serializer

settings.py:

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

Preventing errors

If you will try to register a new user, you might get the following error in the console where your development server is running: ConnectionRefusedError: [Errno 111] Connection refused

Although a user is still created, you can fix this error by adding the following line to your settings.py file:

settings.py:

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

Another error that will occur when you delete a user is:

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

To solve this, add this to your settings.py:

settings.py:

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

After that, you should apply migrations before you can continue:

python manage.py migrate

Creating a custom AccountAdapter

After the above steps, going to ‘http://127.0.0.1:8000/rest-auth/registration/’ will show you the extra fields. But when you register a new user, and send the data for the extra fields, the extra field’s data is not saved.

The last thing we need to do to solve this is to create a custom AccountAdapter

In our users app/folder create a new file named ‘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

Here, if you have followed the above steps correctly, you can access the data of the extra added fields in the form.cleaned_data dictionary. This is the dictionary that is returned by the get_cleaned_data method from our custom RegisterSerializer.

In the save_user method above, we can then use this data and save it to the appropriate fields, like so:

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

Tell Django to use this new adapters

settings.py:

ACCOUNT_ADAPTER = 'users.adapter.CustomAccountAdapter'

Now you can register your user, using the django-rest-auth registration endpoint '/rest-auth/registration/', and send the data for the extra fields you added. This will all be saved in one request.

Again, I realize that custom validation for each field needs to be added. But that's another topic that I will dive into later, and update the post when I found out how that works precisely.

这篇关于如何在DRF + django-rest-auth中使用自定义用户模型保存有关注册的额外字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆