Python Django restframework 美多商城项目(二)——用户模块

创建第一个应用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模型中,通过OneToOneFieldUser模型创建了一对一的关系。这意味着每一个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默认出入比较大,所以自定义新的序列化器。
  • 新增用户序列化器中要添加模型中没有的数据password2agree,对密码、用户名的默认属性进行修改,对各需要验证的参数进行验证。最后添加用户。
  • 新增用户时要删除模型中没有的字段password2agree,将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',
]
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容