首页 > 解决方案 > 更新操作期间嵌套 AbstractUser 模型失败,“user_id 列中的空值违反非空约束”

问题描述

我正在创建嵌套抽象用户“来自用户的老师”,我的用例是“创建用户”->“然后让用户成为老师”

我能够创建用户并使用户成为教师,但我无法更新该字段,在以下情况下想要更新“teacher_cost”

模型.py


from django.contrib.postgres.fields import JSONField
from django.db import models
from django.contrib.auth.models import AbstractUser


class User(AbstractUser):
    uid = models.AutoField(verbose_name='ID', 
                                  serialize=False, 
                                  auto_created=True, 
                                  primary_key=True)
    TEACHER = "Teacher"
    STUDENT = "Student"

    user_type = models.CharField(max_length=30, default=STUDENT)
    contact_number = models.CharField(max_length=20, null=True, blank=True)
    address = models.TextField(null=True, blank=True)
    photo = models.ImageField(null=True, blank=True)
    image = models.ImageField(upload_to='users/',
                              default='default/avatar.png')
    approved = models.BooleanField(default=True)

    def save(self, *args, **kwargs):
        if self.user_type == User.TEACHER and self._state.adding:
            self.approved = False
        super().save(*args, **kwargs)

    @property
    def dishes(self):
        ret = self.teacher.dish_set.all()
        if ret:
            return ret
        else:
            return ''


class Teacher(models.Model):
    uid = models.AutoField(verbose_name='ID', 
                                  serialize=False, 
                                  auto_created=True, 
                                  primary_key=True)
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    bio = models.TextField(null=True, blank=True)
    teacher_cost = models.DecimalField(
        max_digits=5, decimal_places=2, null=True, blank=True)
    languages = models.CharField(
        max_length=50, null=True, blank=True)
    address = models.TextField(null=True, blank=True)

    def __str__(self):
        return self.user.username

序列化程序.py

from rest_framework import serializers, exceptions
from django.contrib.auth.forms import PasswordResetForm
from django.conf import settings
from .models import *
from rest_auth import serializers as rest_auth_serializers
from django.utils.translation import ugettext_lazy as _


class UserDetailsSerializer(serializers.ModelSerializer):
    """
    User model w/o password
    """
    class Meta:
        model = User
        fields = ('pk', 'username', 'email',
                  'first_name', 'last_name', 'contact_number', 'user_type', 'photo', 'address')
        read_only_fields = ('email', )


class UserTeacher(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('teacher',)


class TeacherDetails(serializers.ModelSerializer):
    class Meta:
        model = Teacher
        fields = '__all__'

class TeacherFullDetails(serializers.ModelSerializer):
    user_id = serializers.CharField(source='user.uid')
    username = serializers.CharField(source='user.username')
    first_name = serializers.CharField(source='user.first_name')
    last_name = serializers.CharField(source='user.last_name')
    photo = serializers.ImageField(
        source='user.photo', max_length=None, use_url=True)

    class Meta:
        model = Teacher
        fields = ('user_id', 'username', 'first_name', 'last_name', 'photo', 'teacher_cost')

class TeacherBriefDetails(serializers.ModelSerializer):
    uid = serializers.CharField(source='user.uid')
    first_name = serializers.CharField(source='user.first_name')
    last_name = serializers.CharField(source='user.last_name')

    class Meta:
        model = Teacher
        fields = ('uid', 'first_name', 'last_name',)

class TeacherProfileDetails(serializers.ModelSerializer):
    contact_number = serializers.CharField(source='user.contact_number', required=False)
    first_name = serializers.CharField(source='user.first_name', required=False)
    last_name = serializers.CharField(source='user.last_name', required=False)
    email = serializers.CharField(source='user.email', required=False)
    photo = serializers.ImageField(
        source='user.photo', max_length=None, use_url=True, required=False)
    user = UserDetailsSerializer(read_only=True)
    teacher_cost = serializers.CharField()

    class Meta:
        model = Teacher
        fields = ('user', 'first_name', 'last_name', 'contact_number', 'email', 'photo',
                  'bio', 'teacher_cost')

class TeacherProfileSerializer(serializers.ModelSerializer):
    user = UserDetailsSerializer()
    class Meta:
        model = Teacher
        fields = ("bio", "teacher_cost", "user")

视图.py

from rest_framework import generics, viewsets, status, permissions
from .models import *
from .serializers import *
from rest_framework.response import Response
from django.db.models import Q
from .permissions import IsAuthenticatedAndOwner, IsTeacher
from django.shortcuts import get_object_or_404
from django.utils.datastructures import MultiValueDictKeyError
from rest_framework.views import APIView
import json
import logging

class TeacherProfile(viewsets.ModelViewSet):
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]
    lookup_field = "username"

    def get_object(self):
        if self.action == "partial_update":
            return get_object_or_404(User, username=self.kwargs['username'])
        return get_object_or_404(Teacher, user__username=self.kwargs['username'])

    def get_serializer_class(self):
        if self.action == "partial_update":
            return UserDetailsSerializer
        else:
            return TeacherProfileDetails

class TeacherListCreateAPIView(APIView):
        logger = logging.getLogger(__name__)

        def get(self, request, *args, **kwargs):
            teacherList = Teacher.objects.filter(user__username=kwargs["username"])
            self.logger.info("Printing teacher list")
            self.logger.info(teacherList)
            serializers = TeacherProfileDetails(teacherList, many=True)
            return Response(serializers.data)
        def post(self, request, *args, **kwargs):
            self.logger.info("-----------------put ------------")
            serializers = TeacherProfileDetails(data=request.data)
            # photo = request.FILES['file']
            if serializers.is_valid():
                serializers.save()
                return Response(serializers.data, status=status.HTTP_201_CREATED)
            return Response(serializers.errors, status=status.HTTP_400_BAD_REQUEST)
       def put(self, request, *args, **kwargs):
            serializers = TeacherProfileDetails(data=request.data, many=True)
            self.logger.info(serializers)
            if serializers.is_valid():
                serializers.save()
                return Response(serializers.data, status=status.HTTP_201_CREATED)
            return Response(serializers.errors, status=status.HTTP_400_BAD_REQUEST)


# /user/<str:username>/profile
class UserProfile(viewsets.ModelViewSet):
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]
    lookup_field = "username"

    def get_object(self):
        return get_object_or_404(User, username=self.kwargs['username'])

    def get_serializer_class(self):
        return UserDetailsSerializer

网址.py

from django.urls import path
from .views import *

urlpatterns = [
    path('teacher/<str:username>/profile',
         TeacherProfile.as_view({'get': 'retrieve', 'delete': 'destroy', 'patch': 'partial_update'})),

   path('teacherlist/<str:username>/',
         TeacherListCreateAPIView.as_view(), name="teacher-list"),

]

调用 post 命令更新用户配置文件时出现以下错误

邮政

http://localhost:8002/api/v1/teacherlist/rayees/

表单json数据

{

     "teacher_cost": "25.00"
    }

低于错误

IntegrityError at /api/v1/teacherlist/rayees/
null value in column "user_id" violates not-null constraint
DETAIL:  Failing row contains (2, null, 25.00, null, null, null).


Request Method: POST
Request URL: http://localhost:8002/api/v1/teacherlist/rayees/
Django Version: 2.2.4


标签: djangodjango-modelsdjango-rest-frameworkdjango-formsdjango-views

解决方案


因为在模型中Teacher用户是必填字段,并且您正在发送发布请求,它将使用该用户创建新老师。如果您想创建新老师,那么您应该使用帖子,因为您需要该用户

如果要更新用户的详细信息,可以使用 patch 方法,检查 put 方法代码

class TeacherListCreateAPIView(APIView):
        logger = logging.getLogger(__name__)

        def get(self, request, *args, **kwargs):
            teacherList = Teacher.objects.filter(user__username=kwargs["username"])
            self.logger.info("Printing teacher list")
            self.logger.info(teacherList)
            serializers = TeacherProfileDetails(teacherList, many=True)
            return Response(serializers.data)
        def post(self, request, *args, **kwargs):
            """ creating new user """
            serializers = TeacherProfileDetails(data=request.data)
            # photo = request.FILES['file']
            if serializers.is_valid():
                serializer.validated_data['user'] = User.objects.filter(username=kwarsg['username'])
                serializers.save()
                return Response(serializers.data, status=status.HTTP_201_CREATED)
            return Response(serializers.errors, status=status.HTTP_400_BAD_REQUEST)


        def patch(self, request, *args, **kwargs):
            teacher = TeacherProfileDetails.objects.get(user__username=kwargs['username'])
            serializers = TeacherProfileDetails(data=request.data, instance=teacher)
            self.logger.info(serializers)
            if serializers.is_valid():
                serializers.save()
                return Response(serializers.data, status=status.HTTP_201_CREATED)
            return Response(serializers.errors, status=status.HTTP_400_BAD_REQUEST)


现在您可以使用给定的数据发出补丁请求,http://localhost:8002/api/v1/teacherlist/rayees/ 它将更新记录。


推荐阅读