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登录:
- 安装依赖:
pip install djangorestframework-simplejwt - 在
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), }
- 在
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
相关产品推荐
相关产品推荐

