django-rest-framework icon indicating copy to clipboard operation
django-rest-framework copied to clipboard

In browsable API unselecting the checkbox for a boolean field doesn't update the field, but using raw data does update the field

Open piotrsynowiec opened this issue 2 years ago • 9 comments

I noticed that when I unselect the boolean field that was already True to set it to False via browsable API – the field doesn't update.

Here's an example: https://capture.dropbox.com/k3xcrwjBS9aK4fVT

After unselecting "invoiced" and clicking PUT, the data stays the same (I edited the price value too): https://capture.dropbox.com/bT46reH9ushS8nge

But when I edit the RAW data text field: https://capture.dropbox.com/pDYjaAHHsvNqE79K

The value gets updated: https://capture.dropbox.com/mC9UwhQBLrt1yEOE

It works on all of my endpoints the same way. I wonder if this is a bug or if I am doing something wrong.

piotrsynowiec avatar Nov 16 '22 13:11 piotrsynowiec

Just FYI, GitHub allows you to upload images 😅 It would be helpful if you share also some code to see what may be happening here.

reddytocode avatar Nov 27 '22 16:11 reddytocode

Here is the model:

` class Currency(TimeStampedModel): company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name="companies") name = models.CharField(choices=settings.CURRENCY_CHOICES, default='USD', max_length=3) owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

class Meta:
    verbose_name_plural = "Currencies"

def __str__(self):
    return self.name

``

here's the serializer:

`class CurrencySerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) company = serializers.PrimaryKeyRelatedField(read_only=True) name = serializers.ChoiceField(choices=settings.CURRENCY_CHOICES) owner = serializers.StringRelatedField()

class Meta:
    model = Currency
    fields = '__all__'

def create(self, validated_data):
    return Currency.objects.create(**validated_data)

def update(self, instance, validated_data):
    instance.name = validated_data.get('name', instance.name)
    instance.company = validated_data.get('company', instance.company)
    instance.owner = validated_data.get('owner', instance.owner)
    instance.save()
    return instance`

here're the views:

`class CurrencyListCreateAPIView(generics.ListCreateAPIView): serializer_class = CurrencySerializer permission_classes = [ IsAuthenticated, IsOwner, ]

def perform_create(self, serializer):
    user = self.request.user
    kwarg_company = self.kwargs.get("company")
    company = get_object_or_404(Company, pk=kwarg_company, owner=user)

    if serializer.is_valid():
        currency_name = serializer.validated_data['name']
        if company:
            if Currency.objects.select_related('owner', 'company').filter(company_id=kwarg_company).filter(
                    name=currency_name).exists():
                raise ValidationError(
                    "You have already added this currency!")
            else:
                return serializer.save(owner=user, company=company)
        else:
            raise ValidationError("You don't own that Company")

def get_queryset(self):
    user = self.request.user
    kwarg_company = self.kwargs.get("company")

    return Currency.objects.select_related('company', 'owner').filter(
        owner=user).filter(
            company_id=kwarg_company).order_by("-created_at")`

piotrsynowiec avatar Nov 28 '22 10:11 piotrsynowiec

Sorry, I don't know how to post the code so it is correctly shown.

piotrsynowiec avatar Nov 28 '22 10:11 piotrsynowiec

Hi, is that issue already solved? If not - can I work on it?

Peim8 avatar Dec 15 '22 14:12 Peim8

It is not solved for me 🙂

piotrsynowiec avatar Dec 15 '22 15:12 piotrsynowiec

Hi, is that issue already solved? If not - can I work on it?

you can work on it.

auvipy avatar Dec 15 '22 16:12 auvipy

ListCreateAPIView

this code is not right, ListCreateAPIView only support get and post, not support put

fatelei avatar Jan 17 '23 08:01 fatelei

Please, use the ModelViewSet and ModelSerializers https://www.django-rest-framework.org/api-guide/serializers/#modelserializer https://www.django-rest-framework.org/api-guide/viewsets/#modelviewset

class CurrencySerializer(serializers.ModelSerializer):
    id = serializers.IntegerField(read_only=True)
    company = serializers.PrimaryKeyRelatedField(read_only=True)
    name = serializers.ChoiceField(choices=settings.CURRENCY_CHOICES)
    owner = serializers.StringRelatedField()
    
    class Meta:
        model = Currency
        fields = ["id", "company", "name", "owner"]

image

You can spli the serializer and use different serializer in ModelViewSet with def_get_serializer_class base on self.action

Like:

# views.py
from rest_framework import viewsets

class MyModelViewSet(viewsets.ModelViewSet):
      # other 
      def get_serializer_class(self):
           if self.action == "create":
              # return create serializer class
           elif self.action in ["update", "partial_update"]:
             # return the serualizer class made for the PATCH/PUT
           # made last one for the list/retieve action

#serializers.py
class CurrencyUpdateSerializer(serializers.ModelSerializer):
    name = serializers.ChoiceField(choices=settings.CURRENCY_CHOICES)
    owner = serializers.StringRelatedField()
    
    class Meta:
        model = Currency
        fields = ["name", "owner"]

class CurrencyRetrieveListSerializer(serializers.ModelSerializer):
     class Meta:
        fields = "__all__"

You have more control for action of drf and the HTTP method https://www.django-rest-framework.org/api-guide/viewsets/#viewset-actions

FraCata00 avatar Nov 28 '23 13:11 FraCata00

Hi, is that issue already solved? If not - can I work on it?

you can work on it.

I'm facing this same issue as well. Do you guys know when it could be fixed?

Dostonbek1 avatar Dec 08 '23 06:12 Dostonbek1