Rayees Namathponnan
Rayees Namathponnan

Reputation: 217

Nested AbstractUser model failing during update operation with "null value in column "user_id" violates not-null constraint"

I am creating nested abstract user "Teacher from User", My use case is "Create User" -> "Then make user to teacher"

I am able to create user and make the user to Teacher, but I am unable to update the field, In below case want to update "teacher_cost"

Model.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

Serializer.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")

view.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

urls.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"),

]

Getting below error while calling post command to update the user profile

post

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

Form json data

{

     "teacher_cost": "25.00"
    }

Getting below error

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


Upvotes: 0

Views: 177

Answers (1)

aman kumar
aman kumar

Reputation: 3156

since in model Teacher user is required fields, and you are sending the post request, it will create the new teacher with that user.If you want create new teacher then you should use the post, for that you need the user

if you want update the details of the user you can use patch method, checkout the put method code

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)


now you can make patch request on http://localhost:8002/api/v1/teacherlist/rayees/ with given data it will update the record.

Upvotes: 1

Related Questions