创建第一个应用user
在项目目录中创建 apps
文件夹用于存放所有应用。
my_project/
manage.py
my_project/
apps/
users/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py
logs/
info.log
先创建好应用名文件夹,通过运行命令在指定目录创建新应用
python manage.py startapp users my_project/my_project/apps/users
# 或进入apps目录
python ../../manage.py startapp users
在settings.py文件中,追加导包路径,原因如下:
1.使注册子应用时,可以省略apps的路径,
2.修改Django认证模型类时,必须应用名.模型名的格式,通过追加导包路径解决apps这一层路径。
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
sys.path为系统导包路径,这句话的意思是在系统导包路径列表中,在前位插入一个有BASE_DIR加apps拼接的路径。
在settings.py中,INSTALLED_APPS配置项中追加应用
INSTALLED_APPS = [
...
'users.apps.UsersConfig', # 用户
]
编辑users下的models.py文件,根据需求修改user的数据模型,本例中增加手机号。
Django中扩展内置用户模型有两种方式:
1.扩展Profile模型:创建一个名为 Profile
的新模型并与 User
模型关联。例如:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
mobile = models.CharField(max_length=11, unique=True, verbose_name='手机号')
# 其他想添加的字段
在这种方法中,我们创建一个新的模型(通常称为Profile
),该模型包含用户额外的信息,并通过一对一关系字段与内置的User
模型关联。通常用于在不修改内置User
模型的前提下添加额外信息。
用途:
- 当你对Django的默认用户模型基本满意,但还需要存储一些额外的用户信息,如手机号、地址、生日等。
- 需要保留使用Django内置的用户认证和权限系统的能力。
- 稍后如果有新的字段需求,可以轻松地添加到
Profile
模型中。
在这个Profile
模型中,通过OneToOneField
与User
模型创建了一对一的关系。这意味着每一个User
实例都可以有一个与之对应的Profile
实例。
2.自定义用户模型
在这种方法中,我们通过继承AbstractUser
(包含了User的全部功能)或AbstractBaseUser
(需要自行实现一些功能)来创建完全定制的用户模型。
用途:
- 当Django的默认用户模型和认证系统的许多方面都不符合你的需求时。
- 当你想要一个比较干净的用户模型,可能包含很少的默认字段,或者想要使用不同的字段作为用户名字段。
- 当你想完全控制用户表的数据库层面实现时。
在这个自定义用户模型中,通过继承AbstractUser
实现了扩展。如果需要更大的灵活性,可以从AbstractBaseUser
继承并定义更多的自定义行为。
注意:
- 自定义用户模型需要在第一次运行
migrate
之前在你的项目中定义,否则会很难更改。 - 设置自定义用户模型之后,你应该在
settings.py
中指定AUTH_USER_MODEL
去使用这个新模型,如下设置:
本例中采用第二种方法:
users/model.py
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class User(AbstractUser):
mobile = models.CharField(max_length=11, unique=True, verbose_name='手机号')
class Meta:
db_table = 'user_users' #可以不填,不填默认应用名_模型名
verbose_name = '用户'
verbose_name_plural = verbose_name
settings.py中添加:
# 修改Django认证系统使用的模型类
AUTH_USER_MODEL = 'users.User'
执行迁移命令
python manage.py makemigrations
python manage.py migrate
注册用户
检验用户名是否存在
思路
- 视图层创建
UsernameCountView
类,通过get
方法获取username值,通过User
模型过滤器查询出该username有几条,返回前端。 - URL中添加接口地址,在url中直接通过属性名和正则验证用户名格式。
代码
users/view.py
class UsernameCountView(APIView):
"""检测用户名是否重复"""
def get(self, request, username):
# 查询用户名是否存在
count = User.objects.filter(username=username).count()
# 返回查询结果
data = {'username': username, 'count': count}
return Response(data)
users/url.py
from django.urls import path, re_path
from . import views
urlpatterns = [
...
re_path(r'^username/(?P<username>[a-zA-Z0-9_]{5,20})/count/$', views.UsernameCountView.as_view()), # 检查用户名是否已存在
...
]
检验手机号是否存在
思路
- 视图层创建
MobileCountView
类,通过get
方法获取mobile值,通过User
模型过滤器查询出该mobile有几条,返回前端。 - URL中添加接口地址,在url中直接通过属性名和正则验证手机号格式。
代码
users/view.py
class MobileCountView(APIView):
"""检测手机号是否重复"""
def get(self, request, mobile):
# 查询手机号是否存在
count = User.objects.filter(mobile=mobile).count()
# 返回查询结果
data = {'mobile': mobile, 'count': count}
return Response(data)
users/url.py
from django.urls import path, re_path
from . import views
urlpatterns = [
...
re_path(r'^mobile/(?P<mobile>1[3-9]\d{9})/count/$', views.MobileCountView.as_view()), # 检查手机号是否已存在
...
]
新增用户
思路
- 用户可以复用Django的用户模型和方法,所以
UserView
继承CreateAPIView
,但是由于注册时要填写的信息与Django默认出入比较大,所以自定义新的序列化器。 - 新增用户序列化器中要添加模型中没有的数据
password2
和agree
,对密码、用户名的默认属性进行修改,对各需要验证的参数进行验证。最后添加用户。 - 新增用户时要删除模型中没有的字段
password2
和agree
,将password
存储在一个变量里并从validated_data中删除。存储密码时要通过set_password
方法加密。 - url中添加接口地址。
代码
users/views.py
class UserView(CreateAPIView):
"""用户注册"""
# 指定序列化器
serializer_class = CreateUserSerializer
users/serializer.py
from rest_framework import serializers
from .models import User
import re
class CreateUserSerializer(serializers.ModelSerializer):
"""注册用户序列化器"""
password2 = serializers.CharField(style={'input_type': 'password'}, write_only=True)
agree = serializers.CharField(write_only=True)
class Meta:
model = User
fields = ('id', 'username', 'password', 'password2', 'mobile', 'agree')
extra_kwargs = {
'password': {
'write_only': True,
'min_length': 8,
'max_length': 20,
'error_messages': {
'min_length': '密码长度8-20个字符',
'max_length': '密码长度8-20个字符',
},
},
'username': {
'min_length': 5,
'max_length': 20,
'error_messages': {
'min_length': '用户名长度5-20个字符',
'max_length': '用户名长度5-20个字符',
},
},
}
def validate_password2(self, value):
"""验证密码"""
password = self.initial_data.get('password')
if password != value:
raise serializers.ValidationError('两次输入的密码不一致')
return value
def validate_mobile(self, value):
"""验证手机号格式"""
if not re.match(r'^1[3-9]\d{9}$', value):
raise serializers.ValidationError('手机号格式错误')
return value
def validate_agree(self, value):
"""验证协议"""
if value != 'true':
raise serializers.ValidationError('请同意用户协议')
return value
def create(self, validated_data):
"""创建用户"""
del validated_data['password2']
del validated_data['agree']
# 删除validated_data中的password属性并将password赋值给password变量
password = validated_data.pop('password')
user = User(**validated_data)
user.set_password(password)
user.save()
return user
users/url.py
urlpatterns = [
path('users/', views.UserView.as_view()), # 注册用户
...
]
jwt
jwt使用方法
安装
pip install djangorestframework-simplejwt
配置
settings.py
REST_FRAMEWORK = {
...
'DEFAULT_AUTHENTICATION_CLASSES': (
...
'rest_framework_simplejwt.authentication.JWTAuthentication',
)
...
}
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': datetime.timedelta(days=1), # 设置JWT认证的token的过期时间
'REFRESH_TOKEN_LIFETIME': datetime.timedelta(days=7), # 设置JWT认证的token的刷新时间
'ROTATE_REFRESH_TOKENS': False, # True时,每次使用刷新令牌获取新的访问令牌后,原刷新令牌将失效
'BLACKLIST_AFTER_ROTATION': True,
}
代码
注册后直接返回token
users/serializers.py create方法
# 导包
from rest_framework_simplejwt.tokens import RefreshToken
# 注册序列化器
token = serializers.CharField(read_only=True)
refresh = RefreshToken.for_user(user) # 使用Simple JWT的方法创建新的令牌
user.token = {
'refresh': str(refresh), # 获取刷新令牌字符串
'access': str(refresh.access_token), # 获取访问令牌字符串
}
登录
利用simplejwt进行登录
配置路由
# JWT登录
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
urlpatterns = [
...
# 登录接口
path('authorizations/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
# 刷新token
path('authorizations/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
...
]
修改登录成功返回响应结果
users/utils.py 没有此文件就创建一个
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
"""自定义返回数据"""
def validate(self, attrs):
# 获取token
token = super().validate(attrs)
data = {
'username': self.user.username, # 响应结果增加用户名
'userId': self.user.id, # 响应结果增加用户id
'refresh': token['refresh'],
'access': token['access']
}
return data
配置setting.py
SIMPLE_JWT = {
...
# 用于生成访问令牌和刷新令牌的序列化器
"TOKEN_OBTAIN_SERIALIZER": "users.utils.MyTokenObtainPairSerializer", # 指向自定义的序列化器
}
多账号登录
user/utils.py
def get_user_by_account(account):
"""
根据帐号获取用户对象
:param account: 帐号
:return: User对象或者None
"""
try:
# 手机号
if re.match(r'1[3-9]\d{9}', account):
user = User.objects.get(mobile=account)
# 用户名
else:
user = User.objects.get(username=account)
except User.DoesNotExist:
return None
else:
return user
class UsernameMobileAuthBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
# 获取用户对象
user = get_user_by_account(username)
if user and user.check_password(password):
return user
配置setting.py
# 修改Django认证系统使用的模型类
AUTHENTICATION_BACKENDS = [
'users.utils.UsernameMobileAuthBackend',
]