DRF 用户系统实战 #

一、用户系统概述 #

1.1 功能需求 #

  • 用户注册
  • 用户登录(JWT)
  • 个人资料管理
  • 密码修改
  • 头像上传

1.2 技术栈 #

  • Django REST Framework
  • djangorestframework-simplejwt
  • Pillow

二、用户模型 #

2.1 扩展用户模型 #

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

class User(AbstractUser):
    avatar = models.ImageField(upload_to='avatars/', null=True, blank=True)
    phone = models.CharField(max_length=20, blank=True)
    bio = models.TextField(max_length=500, blank=True)
    birth_date = models.DateField(null=True, blank=True)
    website = models.URLField(blank=True)
    
    class Meta:
        verbose_name = '用户'
        verbose_name_plural = '用户'

2.2 配置 #

python
AUTH_USER_MODEL = 'users.User'

三、序列化器 #

3.1 注册序列化器 #

python
class UserRegisterSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True, min_length=8)
    password_confirm = serializers.CharField(write_only=True)
    
    class Meta:
        model = User
        fields = ['username', 'email', 'password', 'password_confirm', 'phone']
    
    def validate_email(self, value):
        if User.objects.filter(email=value).exists():
            raise serializers.ValidationError('该邮箱已被注册')
        return value
    
    def validate(self, data):
        if data['password'] != data['password_confirm']:
            raise serializers.ValidationError({'password_confirm': '两次密码不一致'})
        return data
    
    def create(self, validated_data):
        validated_data.pop('password_confirm')
        user = User.objects.create_user(**validated_data)
        return user

3.2 用户序列化器 #

python
class UserSerializer(serializers.ModelSerializer):
    articles_count = serializers.SerializerMethodField()
    followers_count = serializers.SerializerMethodField()
    
    class Meta:
        model = User
        fields = ['id', 'username', 'email', 'avatar', 'phone', 'bio', 
                  'birth_date', 'website', 'articles_count', 'followers_count']
        read_only_fields = ['id', 'username']
    
    def get_articles_count(self, obj):
        return obj.articles.count()
    
    def get_followers_count(self, obj):
        return obj.followers.count()

3.3 密码修改序列化器 #

python
class PasswordChangeSerializer(serializers.Serializer):
    old_password = serializers.CharField(write_only=True)
    new_password = serializers.CharField(write_only=True, min_length=8)
    new_password_confirm = serializers.CharField(write_only=True)
    
    def validate_old_password(self, value):
        user = self.context['request'].user
        if not user.check_password(value):
            raise serializers.ValidationError('旧密码错误')
        return value
    
    def validate(self, data):
        if data['new_password'] != data['new_password_confirm']:
            raise serializers.ValidationError({'new_password_confirm': '两次密码不一致'})
        return data
    
    def save(self):
        user = self.context['request'].user
        user.set_password(self.validated_data['new_password'])
        user.save()
        return user

四、JWT配置 #

4.1 安装和配置 #

python
INSTALLED_APPS = [
    ...
    'rest_framework_simplejwt',
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],
}

from datetime import timedelta

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
    'ROTATE_REFRESH_TOKENS': True,
    'BLACKLIST_AFTER_ROTATION': True,
    'AUTH_HEADER_TYPES': ('Bearer',),
}

4.2 自定义Token视图 #

python
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer

class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
    @classmethod
    def get_token(cls, user):
        token = super().get_token(user)
        token['username'] = user.username
        token['email'] = user.email
        return token
    
    def validate(self, attrs):
        data = super().validate(attrs)
        data['user'] = UserSerializer(self.user).data
        return data

class CustomTokenObtainPairView(TokenObtainPairView):
    serializer_class = CustomTokenObtainPairSerializer

五、视图 #

5.1 用户视图集 #

python
class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    
    def get_serializer_class(self):
        if self.action == 'create':
            return UserRegisterSerializer
        if self.action == 'change_password':
            return PasswordChangeSerializer
        return UserSerializer
    
    def get_permissions(self):
        if self.action == 'create':
            return [AllowAny()]
        if self.action in ['update', 'partial_update', 'destroy', 'change_password']:
            return [IsAuthenticated(), IsOwner()]
        return [AllowAny()]
    
    @action(detail=False, methods=['get', 'put', 'patch'])
    def me(self, request):
        if request.method == 'GET':
            serializer = self.get_serializer(request.user)
            return Response(serializer.data)
        
        serializer = self.get_serializer(
            request.user, 
            data=request.data, 
            partial=request.method == 'PATCH'
        )
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data)
    
    @action(detail=False, methods=['post'])
    def change_password(self, request):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response({'message': '密码修改成功'})
    
    @action(detail=False, methods=['post'])
    def upload_avatar(self, request):
        if 'avatar' not in request.FILES:
            return Response({'error': '请选择图片'}, status=400)
        
        user = request.user
        user.avatar = request.FILES['avatar']
        user.save()
        
        return Response({'avatar': user.avatar.url})

六、URL配置 #

python
from rest_framework.routers import DefaultRouter
from rest_framework_simplejwt.views import TokenRefreshView, TokenVerifyView
from .views import UserViewSet, CustomTokenObtainPairView

router = DefaultRouter()
router.register(r'users', UserViewSet)

urlpatterns = [
    path('', include(router.urls)),
    path('token/', CustomTokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    path('token/verify/', TokenVerifyView.as_view(), name='token_verify'),
]

七、API接口 #

接口 方法 说明
/api/users/ POST 用户注册
/api/users/me/ GET 获取个人信息
/api/users/me/ PUT 更新个人信息
/api/token/ POST 登录获取Token
/api/token/refresh/ POST 刷新Token
/api/users/change_password/ POST 修改密码

八、总结 #

本章实现了完整的用户系统:

  • 用户注册和登录
  • JWT Token认证
  • 个人资料管理
  • 密码修改
  • 头像上传

用户系统是API的核心基础!

最后更新:2026-03-28