You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django DRF TypeError问题及图书馆系统功能实现求助

问题解决与功能实现

报错原因与修复

1. 序列化器create方法位置错误

UserSerializer和LibrarianUserSerializer的create方法被错误定义在Meta类内部,导致DRF无法识别自定义创建逻辑,会尝试将嵌套字段reg_data/reg_data_l直接传递给User模型构造函数,引发参数不匹配错误。

修复后代码片段(serializer.py):

class UserSerializer(serializers.ModelSerializer):
    reg_data = MemberSerializer()

    class Meta:
        model = User
        fields = ('username','password','reg_data',)

    def create(self, validated_data):
        reg_info = validated_data.pop('reg_data')
        user_reg = User.objects.create(**validated_data)
        user_reg.set_password(validated_data['password'])
        user_reg.is_member = True  # 标记用户为会员
        user_reg.save()
        Member.objects.create(user=user_reg, **reg_info)
        return user_reg

class LibrarianUserSerializer(serializers.ModelSerializer):
    reg_data_l = LibrarianSerializer()

    class Meta:
        model = User
        fields = ('username','password','reg_data_l',)

    def create(self, validated_data):
        reg_info = validated_data.pop('reg_data_l')
        user_reg = User.objects.create(**validated_data)
        user_reg.set_password(validated_data['password'])
        user_reg.is_librarian = True  # 标记用户为馆员
        user_reg.save()
        Librarian.objects.create(user=user_reg, **reg_info)
        return user_reg

2. 视图类序列化器匹配错误

原LibrarianAPI和MemberAPI错误使用UserSerializer,且ModelViewSet默认包含全量CRUD操作,不适合注册场景。改用专门的创建视图实现注册功能:

修复后代码片段(views.py):

from rest_framework.generics import CreateAPIView

# 会员注册视图(无需登录)
class MemberRegistrationAPI(CreateAPIView):
    serializer_class = UserSerializer
    permission_classes = []

# 馆员注册视图(无需登录)
class LibrarianRegistrationAPI(CreateAPIView):
    serializer_class = LibrarianUserSerializer
    permission_classes = []

功能完善实现

1. 模型补充(书籍状态与关联)

修改Books模型,添加借阅状态和关联会员字段:

class Books(models.Model):
    BOOK_STATUS = (('AVAILABLE', '可用'), ('BORROWED', '已借出'))
    book_name = models.CharField(max_length=50)
    book_author = models.CharField(max_length=30)
    book_isbn = models.PositiveIntegerField(unique=True)
    status = models.CharField(max_length=10, choices=BOOK_STATUS, default='AVAILABLE')
    borrowed_by = models.ForeignKey(Member, on_delete=models.SET_NULL, null=True, blank=True)

    def __str__(self):
        return f"{self.book_name} - {self.status}"

2. 自定义权限类

创建permissions.py,区分不同角色的操作权限:

from rest_framework.permissions import BasePermission

class IsLibrarian(BasePermission):
    def has_permission(self, request, view):
        return request.user.is_authenticated and request.user.is_librarian

class IsMember(BasePermission):
    def has_permission(self, request, view):
        return request.user.is_authenticated and request.user.is_member

class IsOwnerOrLibrarian(BasePermission):
    def has_object_permission(self, request, view, obj):
        # 会员可删除自己账号,馆员可删除所有会员
        return request.user.is_librarian or (request.user.is_member and obj.user == request.user)

3. 视图功能实现(views.py)

from rest_framework import status
from rest_framework.response import Response
from rest_framework.decorators import action

# 馆员管理视图
class LibrarianAPI(ModelViewSet):
    queryset = Librarian.objects.all()
    serializer_class = LibrarianSerializer
    permission_classes = [IsLibrarian]

    # 管理书籍
    @action(detail=False, methods=['get', 'post', 'put', 'delete'], url_path='books')
    def manage_books(self, request):
        if request.method == 'GET':
            books = Books.objects.all()
            return Response(BookSerializer(books, many=True).data)
        elif request.method == 'POST':
            serializer = BookSerializer(data=request.data)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data, status=status.HTTP_201_CREATED)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        # 单条书籍的PUT/DELETE逻辑可自行补充

    # 管理会员
    @action(detail=False, methods=['get', 'post', 'put', 'delete'], url_path='members')
    def manage_members(self, request):
        if request.method == 'GET':
            members = Member.objects.all()
            return Response(MemberSerializer(members, many=True).data)
        elif request.method == 'POST':
            serializer = UserSerializer(data=request.data)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data, status=status.HTTP_201_CREATED)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        # 单条会员的PUT/DELETE逻辑可自行补充

# 会员操作视图
class MemberAPI(ModelViewSet):
    queryset = Member.objects.all()
    serializer_class = MemberSerializer
    permission_classes = [IsMember]
    lookup_field = 'user__id'

    # 查看可用书籍
    @action(detail=False, methods=['get'], url_path='available-books')
    def available_books(self, request):
        books = Books.objects.filter(status='AVAILABLE')
        return Response(BookSerializer(books, many=True).data)

    # 借阅书籍
    @action(detail=False, methods=['post'], url_path='borrow-book')
    def borrow_book(self, request):
        book_id = request.data.get('book_id')
        try:
            book = Books.objects.get(id=book_id, status='AVAILABLE')
            member = Member.objects.get(user=request.user)
            book.status = 'BORROWED'
            book.borrowed_by = member
            book.save()
            return Response({"message": "书籍借阅成功"}, status=status.HTTP_200_OK)
        except Books.DoesNotExist:
            return Response({"error": "书籍不存在或已借出"}, status=status.HTTP_404_NOT_FOUND)

    # 归还书籍
    @action(detail=False, methods=['post'], url_path='return-book')
    def return_book(self, request):
        book_id = request.data.get('book_id')
        try:
            book = Books.objects.get(id=book_id, status='BORROWED', borrowed_by__user=request.user)
            book.status = 'AVAILABLE'
            book.borrowed_by = None
            book.save()
            return Response({"message": "书籍归还成功"}, status=status.HTTP_200_OK)
        except Books.DoesNotExist:
            return Response({"error": "书籍不存在或未被您借阅"}, status=status.HTTP_404_NOT_FOUND)

    # 删除自己的账号
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        if instance.user != request.user:
            return Response({"error": "无权删除此账号"}, status=status.HTTP_403_FORBIDDEN)
        user = instance.user
        self.perform_destroy(instance)
        user.delete()
        return Response({"message": "账号已删除"}, status=status.HTTP_204_NO_CONTENT)

# 书籍通用视图
class BooksAPI(ModelViewSet):
    queryset = Books.objects.all()
    serializer_class = BookSerializer
    permission_classes = [IsAuthenticated]

    # 仅馆员可修改/删除书籍
    def get_permissions(self):
        if self.action in ['create', 'update', 'partial_update', 'destroy']:
            return [IsLibrarian()]
        return [IsAuthenticated()]

4. JWT登录配置

使用djangorestframework-simplejwt实现JWT登录:

  1. 安装依赖:pip install djangorestframework-simplejwt
  2. 在settings.py添加配置:
INSTALLED_APPS = [
    # ...
    'rest_framework',
    'rest_framework_simplejwt',
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ),
}

from datetime import timedelta
SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=30),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
}
  1. 在urls.py添加登录路由:
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView

urlpatterns = [
    # ...
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    path('api/register/member/', MemberRegistrationAPI.as_view(), name='member_register'),
    path('api/register/librarian/', LibrarianRegistrationAPI.as_view(), name='librarian_register'),
    # 其他视图路由...
]

内容的提问来源于stack exchange,提问作者Aakash Bhaikatti

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 19:06:32