1
votes

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"}
3
Have you tried to remove depth=1? I assume it overrides your fields=[....,'user']Klim Bim
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 possibleIsaac Hatilima
Another approach is in ProvinceSerializer, change user to created_byBrianD
This actually helped. Thank you.Isaac Hatilima

3 Answers

1
votes

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)
0
votes

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
0
votes

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.