1

I have two models (User and Province) and serializers. The user creates a province and in the province model I have the user id. The challenge is when creating nested serializers, I only want the first_name and last_name from the user but is is giving me everything including the password. Below are the serializers

class UserSerializer(serializers.ModelSerializer):
        
    class Meta:
        model = User
        fields = ['first_name', 'last_name']


class ProvinceSerializer(serializers.ModelSerializer):
    user = UserSerializer(many=True, read_only=True)
    class Meta:
        model = Province
        fields = ['id', 'province_uid', 'province_name', 'date_created','created_by','user' ]
        read_only_fields = ('id','province_uid', 'date_created')
        depth = 1

Edit Below is the response and I would like to only have the first_name and last_name from the user.

      "id": 1,
      "province_uid": "9c74aeda-0734-465f-847b-0fa89ca7ea21",
      "province_name": "Lusaka",
      "date_created": "2021-07-12T06:42:22.973441+02:00",
      "created_by": {
        "id": 1,
        "password": "db_password",
        "user_uid": "3d0f353c-260a-41b7-a535-3ea5a20fe8a5",
        "username": "admin",
        "email": "[email protected]",
        "cell": "260966477311",
        "first_name": "Global",
        "last_name": "Admin"}
4
  • Have you tried to remove depth=1? I assume it overrides your fields=[....,'user'] Commented Jul 14, 2021 at 4:10
  • Hi Klim, if I remove depth=1 then I wont get the name of the user who created the record, it will only show the created_by : 1. I would want to get the names only if that's something possible Commented Jul 14, 2021 at 4:14
  • Another approach is in ProvinceSerializer, change user to created_by Commented Jul 14, 2021 at 7:38
  • This actually helped. Thank you. Commented Jul 14, 2021 at 15:19

3 Answers 3

1

I tested it a little more and you should adjuste the code a little bit and you don´t need a UserSerializer for that case in ProvinceSerializer.

I don´t know your model but where did you get created_by-field? Is it a part of your model? If not it won´t work to change user to created_by as mentioned.

model

class Province(models.Model):
    province_uid = models.UUIDField()
    province_name = models.CharField(max_length=255)
    date_created = models.DateTimeField(auto_now_add=True)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

serializer

class ProvinceSerializer(serializers.ModelSerializer):

    created_by = serializers.SerializerMethodField()

    class Meta:
        model = Province
        exclude = ['user']
        read_only_fields = ('id', 'province_uid', 'date_created')

    def get_created_by(self, obj):
        return {'first_name': obj.user.first_name, 'last_name': obj.user.last_name}

view

class TestView(views.APIView):

    def get(self, request):
        serializer = ProvinceSerializer(Province.objects.all(), many=True)
        return Response(data=serializer.data)
Sign up to request clarification or add additional context in comments.

Comments

0

Mark the password field as write only.

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['first_name', 'last_name']
        extra_kwargs = {
                'password': {'write_only': True, 'required': False}
        }

Update:

You need to serialize created_by by providing UserSerializer else the depth=1 will populate all the data from foreign key relation.

Also not sure why you need many=True for user attribute in the ProvinceSerializer. Can only comment on it after seeing the models

class ProvinceSerializer(serializers.ModelSerializer):
    user = UserSerializer(many=True, read_only=True)
    created_by = UserSerializer(read_only=True)
    class Meta:
        model = Province
        fields = ['id', 'province_uid', 'province_name', 'date_created','created_by','user' ]
        read_only_fields = ('id','province_uid', 'date_created')
        depth = 1

2 Comments

Hi MjZac, sorry maybe my english is not that good. From the UserSerializer I am only using/needing it when displaying the provinces and I want to see who created it. I have noticed that even if I remove fields from the UserSerializer I still get all the model fields. I have edited the post kindly check and advise.
@IsaacHatilima I have updated my answer ( and your English looks good to me :) )
0

Both the answers did exactly what I needed thank you so much Klim and MjZac, wish I could get you coffee. To anyone with a similar challenge, just pick any of the two and it will work. To answer Klims question, created_by is a foreign key of the User model. I have it as;

created_by = models.ForeignKey(User, verbose_name="Created By", on_delete=models.CASCADE, null=False)

in the Province model. Below are the serializers with one commented after testing solution:

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('first_name', 'last_name')
        read_only_fields = ('first_name', 'last_name')

# class ProvinceSerializer(serializers.ModelSerializer):
#     created_by = UserSerializer(read_only=True)
#     class Meta:
#         model = Province
#         fields = ['id', 'province_uid', 'province_name', 'date_created','created_by' ]
#         read_only_fields = ('id','province_uid', 'date_created','created_by')
#         depth = 1

class ProvinceSerializer(serializers.ModelSerializer):
    
    created_by = serializers.SerializerMethodField()

    class Meta:
        model = Province
        fields = ['id', 'province_uid', 'province_name', 'date_created','created_by' ]
        read_only_fields = ('id', 'province_uid', 'date_created')

    def get_created_by(self, obj):
        return {'first_name': obj.created_by.first_name, 'last_name': obj.created_by.last_name}

Notice I added read_only_fields on the UserSerializer because they required input on my Swagger UI, right now I am getting "created_by": {} on Swagger but I can live with that as I find ways of removing it completely. Happy coding and do stay safe.

Comments

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.