Django Rest Framework error: {'user': [ErrorDetail(string='This field is required.', code='required')]}

Question:

Unable to add a new customer to the database..
I made a class Named customer that has a one-to-one relationship with a class named User that is an AbstractUser

I want to send the data through rest API so that I can create a new customer in the customer table and a new user that is One To One Related to the customer from the same view.
User Model

class User(AbstractUser):
# Add additional fields here
id = None
email = models.EmailField(max_length=254, primary_key=True)
name = models.CharField(max_length=100)
password = models.CharField(max_length=100)
is_patient = models.BooleanField(default=False)
is_doctor = models.BooleanField(default=False)
is_homesampler = models.BooleanField(default=False)
is_pathologist = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
date_joined = models.DateTimeField(auto_now_add=True)
last_login = models.DateTimeField(auto_now=True)
first_name = None
last_name = None
username = None

USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name', 'password']
objects = CustomUserManager()

def __str__(self):
    return self.email

# Ensure that the password is hashed before saving it to the database
def save(self, *args, **kwargs):
    self.password = make_password(self.password)
    super(User, self).save(*args, **kwargs)

def has_perm(self, perm, obj=None):
    return self.is_superuser

User Serializer

class UserSerializer(serializers.ModelSerializer):

class Meta:
    model = get_user_model()
    # fields = (['id', 'username', 'email', 'name'])
    fields = '__all__'

customer Model

class customer(models.Model):
user = models.OneToOneField(
    get_user_model(), on_delete=models.CASCADE, primary_key=True)
real = models.BooleanField(default=False)

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

Customer Serializer

class CustomerSerializer(serializers.ModelSerializer):
userdata = UserSerializer(read_only=True, source='user')

class Meta:
    model = customer
    fields = '__all__'

    def create(self, validated_data):
        user_data = validated_data.pop('user')
        user = get_user_model().objects.create(**user_data)
        user.is_Patient = True
        customer = customer.objects.create(user=user, **validated_data)
        return customer

Create Customer View

# Create add customer API
@api_view(['POST'])
def addCustomer(request):

    customer_serializer = CustomerSerializer(data=request.data)
    if(customer_serializer.is_valid()):
        customer_serializer.save()
    print(customer_serializer.errors)

    return Response({'message': 'okay'})

Body of API Call

{
    "email" : "[email protected]",
    "password": "Abc"
}

So the question is how can I create a view so that I can create a new user and a customer using just one API Call

Asked By: usamayaseen

||

Answers:

Your call body doesn’t match CustomerSerializer.

CustomerSerializer fields are "user" and "rest", so you only can pass these two unless you do something like these:

class CustomerSerializer(serializers.ModelSerializer):
    userdata = UserSerializer(read_only=True, source='user')
    email = serializers.EmailField(write_only=True)
    password = serializers.CharField(write_only=True)

    class Meta:
        model = customer
        fields = ["userdata", "email", "password", "id", "real"]


    def create(self, validated_data):
        email = validated_data.pop("email")
        password = validated_data.pop("password")

        user = get_user_model().objects.create(**{
          "email": email,
          "password": password
        })
        user.is_Patient = True
        customer = customer.objects.create(user=user, **validated_data)
        
        return customer

About the create method, it won’t function correctly:

Because:

  1. We shouldn’t use create to create a new user instead we should use create_user More
  2. I noticed that you removed the username so this method would be useless 🙂
  3. After user.is_Patient = True, you forgot to save the user

The correct code would be:

class CustomerSerializer(serializers.ModelSerializer):
    userdata = UserSerializer(read_only=True, source='user')
    email = serializers.EmailField(write_only=True)
    password = serializers.CharField(write_only=True)

    class Meta:
        model = customer
        fields = ["userdata", "email", "password", "id", "real"]


    def create(self, validated_data):
        email = validated_data.pop("email")
        password = validated_data.pop("password")

        user = get_user_model().objects.create(email=email)
        user.set_password(password)
        user.is_Patient = True
        user.save()

        customer = customer.objects.create(user=user, **validated_data)
        
        return customer

NOTE 1:

# Ensure that the password is hashed before saving it to the database
def save(self, *args, **kwargs):
    self.password = make_password(self.password)
    super(User, self).save(*args, **kwargs)

It is the wrong approach to make_password here because whenever you make a change in your user, it would run.

the ideal approach would be using user.set_password("new_pass") whenever you get a new password from the user.

NOTE 2:

When you pass read_only to a serializer, this means would be ignored if you passed it as data to the serializer.

About write_only, it’s the opposite of read_only. It would not be returned if you called serializer.data. For example, we only want to write to the password, and we won’t want to read it from the serializer, so we made it write_only.

UPDATE: Adding a name

You have to add a write_only field and then pass it to create method

class CustomerSerializer(serializers.ModelSerializer):
    userdata = UserSerializer(read_only=True, source='user')
    email = serializers.EmailField(write_only=True)
    password = serializers.CharField(write_only=True)
    name = serializers.CharField(write_only=True)
    
    class Meta:
        model = customer
        fields = ["userdata", "email", "password", "id", "real", "name"]


    def create(self, validated_data):
        email = validated_data.pop("email")
        password = validated_data.pop("password")
        name = validated_data.pop("name")
        
        user = get_user_model().objects.create(email=email, name=name)
        user.set_password(password)
        user.is_Patient = True
        user.save()

        customer = customer.objects.create(user=user, **validated_data)

        return customer
Answered By: Abdollah Keshtkar