9

This is django and django rest framework. I have 2 models: User and phone.

The 1st problem:

I want to be able to update user data(email) alongside phone data(phone numbers) in 1 single api update response. Phone number can be 0 or many. Well, like partial=True actually. If a user just want to update phone numbers, don't update email and vice versa.


Additional info: At the time of registering, phone is not included. Just basic user info (last name, first name, email, password). The phone can only get updated in the user profile form after registration is done. The user profile form is actually linking to multiple models, which is User and Phone

The 2nd problem:

How to serialize multiple phone_numbers and save to db?

class User(AbstractBaseUser):
    email = models.EmailField(unique=True, default='')
    USERNAME_FIELD = 'email'


class Phone(models.Model):
    phone_number = models.CharField(max_length=10)
    owner = models.ForeignKey(User)

--------------------------------------
class UserSerializer(serializers.ModelSerializer):
    phone_number = PhoneSerializer(required=False, many=True)

    class Meta:
        model = User
        fields = ('email, 'phone_number')


class PhoneSerializer(serializers.ModelSerializer):

     class Meta:
          Model = Phone
          fields = ('phone_number')

The html form would have plus sign at the phone number field to indicate a new phone number can be added. example is here

email : [email protected]
phone number: 23423432423
(add more)
phone number: 3423423423
(add more)
...

The expected json

{
'email': '[email protected]',
'phone_number': '32432433223234'
}

or if many phone numbers are added

{
'email': '[email protected]',
'phone_number': '32432433223234',
'phone_number': '324342322342323'
}

or maybe

{
'email': '[email protected]',
'phone_number': ['32432433223234','324342322342323']
}

or maybe

{
'email': '[email protected]',
'Phone': [{'id': 1, 'phone_number': '32432433223234'}, {'id': 2, 'phone_number': '324342322342323'}]
}

is this json possible to do? how can serializer and modelviewset do it? sorry I'm totally new to drf

3
  • Have you tried reading the drf API guide on this topic? Commented Apr 17, 2016 at 17:14
  • Hm, I edited my question because it wasn't clear I guess. Yep I read the relation guide but phone is only created after user registration, which is in the user profile page. Commented Apr 17, 2016 at 17:42
  • The Formvalidation link is broken - this is the updated one formvalidation.io/guide/examples/adding-dynamic-field Commented Feb 14, 2023 at 10:20

1 Answer 1

12
+50
  1. Default version for any nested objects.

You need to add serializer create and update methods:

class UserSerializer(serializers.ModelSerializer):
    phones = PhoneSerializer(required=False, many=True)

    class Meta:
        model = User
        fields = ('email', 'phone_number')

    def create(self, validated_data):
        phones_data = validated_data.pop('phones', [])
        user = super(UserSerializer, self).create(validated_data)
        for phone_data in phones_data:
            user.phone_set.create(phone_number=phone_data['phone_number'])
        return user

    def update(self, instance, validated_data):
        phones_data = validated_data.pop('phones', [])
        user = super(UserSerializer, self).update(instance, validated_data)
        # delete old
        user.phone_set.exclude(phone__in=[p['phone_number'] for p in phones_data]).delete()
        # create new
        for phone_data in phones_data:
            user.phone_set.get_or_create(phone_number=phone_data['phone_number'])
        return user

Request for creation:

{"email": "[email protected]" "phones": [{"phone_number": 12}, {"phone_number": 123}]}

Request for update:

{"phones": [{"phone_number": 22}]}
  1. Optimization for current structure:

UPDATE

  1. phones_data = validated_data.pop('phones') -> phones_data = validated_data.pop('phones', []), to handle case if there are no phones in request.

  2. Should phone updates and creation be done in modelviewset?

    No, serializer is responsible of transformation native data -> internal objects. So if it receive phones data - it should create PhoneNumber objects.

Sign up to request clarification or add additional context in comments.

3 Comments

The phone is CharField actually. I have edited the question. Anyway, the situation is like this. At the time of registering, phone is not included. Just basic user info (last name, first name, email, password). The phone can only get updated in the user profile form after registration is done. The user profile form is actually linking to multiple models, which is User and Phone.
I don't think the phones_data = validated_data.pop('phones') is suitable to be in the UserSerializer because phones doesn't exist at the time of registration. Should phone updates and creation be done in modelviewset? (sorry if this wasn't clear in the question itself. )
I have updated my answer. phones_data = validated_data.pop('phones') - this line will extract not DB phone, it will extract data about phones, that will be used for phones creation in DB.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.