Search code examples
pythondjangodjango-rest-frameworkdjango-rest-viewsets

What determines what fields can be updated in DjangoRestFramework


I currently have a serializer with the following fields

class Meta:
    model = Asset
    fields = ('id', 'uuid', 'asset_category', 'asset_sub_category',
              'make_label',
              'asset_code', 'serial_number', 'model_number',
              'checkin_status', 'created_at',
              'last_modified', 'current_status', 'asset_type',
              'allocation_history', 'specs', 'purchase_date',
              'notes', 'assigned_to', 'asset_location'
              )

Serializer

class AssetSerializer(serializers.ModelSerializer):
    checkin_status = serializers.SerializerMethodField()
    allocation_history = serializers.SerializerMethodField()
    assigned_to = UserSerializer(read_only=True)
    asset_category = serializers.SerializerMethodField()
    asset_sub_category = serializers.SerializerMethodField()
    make_label = serializers.SerializerMethodField()
    asset_type = serializers.SerializerMethodField()
    model_number = serializers.SlugRelatedField(
        queryset=AssetModelNumber.objects.all(),
        slug_field="model_number"
    )

    class Meta:
        model = Asset
        fields = ('id', 'uuid', 'asset_category', 'asset_sub_category',
                  'make_label',
                  'asset_code', 'serial_number', 'model_number',
                  'checkin_status', 'created_at',
                  'last_modified', 'current_status', 'asset_type',
                  'allocation_history', 'specs', 'purchase_date',
                  'notes', 'assigned_to', 'asset_location'
                  )
        depth = 1
        read_only_fields = ("uuid",)

View

class ManageAssetViewSet(ModelViewSet):
    serializer_class = AssetSerializer
    queryset = Asset.objects.all()
    # permission_classes = [IsAuthenticated, IsAdminUser]
    # authentication_classes = (FirebaseTokenAuthentication,)
    http_method_names = ['get', 'post', 'put', 'delete']
    filter_backends = (filters.DjangoFilterBackend,)
    filterset_class = AssetFilter

    def get_object(self):
        queryset = Asset.objects.all()
        obj = get_object_or_404(queryset, uuid=self.kwargs['pk'])
        return obj

Model Asset Model. Some fields have been ommited

class Asset(models.Model):
    """Stores all assets"""
    uuid = models.UUIDField(unique=True, default=uuid.uuid4, editable=False)
    asset_code = models.CharField(
        unique=True, null=True, blank=True, max_length=50)
    serial_number = models.CharField(
        unique=True, null=True, blank=True, max_length=50)
    created_at = models.DateTimeField(auto_now_add=True, editable=False)
    asset_location = models.ForeignKey('AndelaCentre', blank=True, editable=True, null=True,
                                       on_delete=models.PROTECT)
    purchase_date = models.DateField(
        validators=[validate_date],
        null=True, blank=True)
    last_modified = models.DateTimeField(auto_now=True, editable=False)
    assigned_to = models.ForeignKey('AssetAssignee',
                                    blank=True,
                                    editable=False,
                                    null=True,
                                    on_delete=models.PROTECT)
    model_number = models.ForeignKey(AssetModelNumber,
                                     null=True,
                                     on_delete=models.PROTECT)
    current_status = models.CharField(editable=False, max_length=50)
    notes = models.TextField(editable=False, default=" ", )

However, on the browsable Api, only 4 fields are showing on the UPDATE/PUT form as shown in the diagram below enter image description here

What could be the reason some of the other fields are not appearing here. What determines which fields are updatable??


Solution

  • Well, the problem is when you set depth = 1 ModelSerializer tries to generate a NestedSerializer field for any foreignkey related field which you have not explicitly mentioned. And that NestedSerializer field is a read only field. That's why Assest Location is not being displayed. Remove that depth = 1 line and DRF will map the said field with the default mapping i.e. PrimaryKeyRelatedFiel and you will see that the said field is displayed.