This is the response from Django when making axios api call in the frontend (array of JSON objects).
[
{
"id": 1,
"title": "How to create a django-react app",
"body": "You should first do this stuff and that"
},
{
"id": 5,
"title": "How to connect django with react",
"body": "Get this and that stuff"
}
]
But this is the response that I want (JSON object of JSON objects).
Is a Python dictionary the same as a Javascript object or Hashmap?
Is there some kind of middleware I can use to convert the shape?
Is this a job that the serializers.py needs to do or the views.py? How can I change the response from an array of objects to an object or objects?
{
1: {
"id": 1,
"title": "How to create a django-react app",
"body": "You should first do this stuff and that"
},
5: {
"id": 5,
"title": "How to connect django with react",
"body": "Get this and that stuff"
}
}
serializers.py
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = '__all__'
views.py
class ArticleViewSet(ViewSet):
queryset = Article.objects.all()
def list(self, request):
serializer = ArticleSerializer(ArticleViewSet.queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
article = get_object_or_404(ArticleViewSet.queryset, pk=pk)
serializer = ArticleSerializer(article, many=False)
return Response(serializer.data)
You should be able to build a dictionary in the ArticleViewSet's list method:
class ArticleViewSet(ViewSet):
queryset = Article.objects.all()
def list(self, request):
serializer = ArticleSerializer(ArticleViewSet.queryset, many=True)
return Response({article['id']: article for article in serializer.data})
def retrieve(self, request, pk=None):
# ...
Related
My site has a Vue frontend with a DRF backend. I have a model with a multi-value field that looks like this:
stages = StageSerializer(
many=True,
read_only=True
)
In the to_representation() method, I process the stages data into three different elements to facilitate features for the frontend.
def to_representation(self, instance):
results = super().to_representation(instance)
return render_stages(results)
The problem is, when I post (PATCH actually) to an update view, which ends up calling the update() method of the serializer, the to_representation() method seems to be called twice. I could post the code from render_stages(), but I don't see how that could be causing the problem. Suffice to say that it takes this, from the call to super().to_representation():
{
.
. other fields
.
"stages": [
{
"id": 1,
"name": "To Do"
},
{
"id": 2,
"name": "In Progress"
},
{
"id": 3,
"name": "Done"
}
]
}
...and turns it into this:
{
"stages": [
"To Do",
"In Progress",
"Done"
],
"stages_order": "1,2,3",
"stages_data": [
{
"id": 1,
"name": "To Do",
"order": 1
},
{
"id": 2,
"name": "In Progress",
"order": 2
},
{
"id": 3,
"name": "Done",
"order": 3
}
]
}
As you can see, the format of the "stages" element changes after the first pass through to_representation() from a list of dicts to a list of strings. So when to_representation() gets called a second time, it raises an exception.
On a list view, it only gets called once. It's just on an update view that it gets called twice. Can someone explain why that's happening?
Update: More details:
The view. The settings model only has one row - pk=1 - so this is a departure from the norm.
class SettingsUpdate(ViewMetaMixin, UpdateAPIView):
serializer_class = SettingsSaveSerializer
permission_classes = (IsAuthenticated, CanAccessEndpoint)
queryset = Settings.objects.none()
def get_object(self):
obj = Settings.objects.get(pk=1)
self.check_object_permissions(self.request, obj)
return obj
def put(self, request, *args, **kwargs):
return self.patch(request, *args, **kwargs)
def patch(self, request, *args, **kwargs):
response = self.partial_update(request, *args, **kwargs)
return Response({
'results': response.data,
'messages': [
{
'message': _('Settings updated.'),
'message_type': 'status',
}
]
})
The serializer:
class SettingsSaveSerializer(SettingsSerializer):
stages = StageSerializer(
many=True,
read_only=True
)
class Meta:
model = Settings
fields = '__all__'
def to_representation(self, instance):
results = super().to_representation(instance)
return render_stages(results)
...validations omitted...
...create method omitted...
def update(self, instance, validated_data):
stages = get_initial_data_field(self.initial_data, 'stages', many=True)
if stages is not None: # Only process "stages" if it's present in form data
validated_data['stages_order'] = Stage.objects.save_stages(instance, stages)
return super().update(instance, validated_data)
I am trying to update multiple objects using IDs which i am passing in every objects that need to be updated but can't find any way to do it successfully. Here is my code
models.py
class EventTicket(models.Model):
id = models.UUIDField(primary_key=True, default=uuid_generate_v1mc, editable=False)
name = models.CharField(max_length=250)
description = models.TextField(max_length=1000)
views.py
class EventTicketView(APIView, PaginationHandlerMixin):
permission_classes = (AllowAny,)
def get_object(self, ticket_id):
try:
return EventTicket.objects.get(id=ticket_id)
except EventTicket.DoesNotExist():
raise status.HTTP_400_BAD_REQUEST
def patch(self, request, *args, **kwargs):
for each_ticket in request.data:
ticket_id = self.get_object(each_ticket['ticket_id'])
serializer = EventTicketSerializer(instance=ticket_id,data=request.data,partial=True)
if serializer.is_valid():
serializer.save()
result = {
'message': "updated sucessfully"
}
return Response(result, status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializers.py
class EventTicketSerializer(serializers.ModelSerializer):
class Meta:
model = EventTicket
fields = ['name', 'description']
```
I have to send data like list of multiple objects :::
[
{
"ticket_id": "054665ea-4fde-11ea-94b2-9f415c43ba4c",
"name": "chris",
"description":"The golden ticket for day only",
},
{
"ticket_id": "054656ea-4fde-11ea-94b2-9f415c43ba4c",
"name": "daut",
"description":"The premium ticket for day only",
}
]
The following code will give you a proper understanding of updating multiple objects in single request.
For updating multiple objects in a single request it is best practice to use the PUT method instead of PATCH.
Here body data given is.
BODY DATA
{
"ids":[
"5e41770d2e8fa013d1f034ec",
"5e41772c2e8fa013d1f034ee",
"5e4177702e8fa013d1f034f2",
"5e453f302e8fa075aa18b277",
"5e4a314f2e8fa070c5251a0a"
]
}
I'am updating the enabled attribute from False to True for given ids of DemoConfig model.
In the same way, you can update your data. As per your requirement, you can write validate methods to validate the body data.
Serializer has written to serialized the instance data for the response.
class DemoAPI(APIView):
def get_object(self, obj_id):
try:
return DemoConfig.objects.get(id=obj_id)
except (DemoConfig.DoesNotExist, ValidationError):
raise status.HTTP_400_BAD_REQUEST
def validate_ids(self, id_list):
for id in id_list:
try:
DemoConfig.objects.get(id=id)
except (DemoConfig.DoesNotExist, ValidationError):
raise status.HTTP_400_BAD_REQUEST
return True
def put(self, request, *args, **kwargs):
id_list = request.data['ids']
self.validate_ids(id_list=id_list)
instances = []
for id in id_list:
obj = self.get_object(obj_id=id)
obj.enabled = True
obj.save()
instances.append(obj)
serializer = DemoSerializer(instances, many=True)
return Response(serializer.data)
Serialiser for this view is:
class DemoSerializer(DocumentSerializer):
class Meta:
model = DemoConfig
fields = '__all__'
Output:
{
"data": [
{
"id": "5e41770d2e8fa013d1f034ec",
"name": "CONFIG_1",
"enabled": true,
},
{
"id": "5e41772c2e8fa013d1f034ee",
"name": "CONFIG_2",
"enabled": true,
},
{
"id": "5e4177702e8fa013d1f034f2",
"name": "CONFIG_3",
"enabled": true,
},
{
"id": "5e453f302e8fa075aa18b277",
"name": "CONFIG_4",
"enabled": true,
},
{
"id": "5e4a314f2e8fa070c5251a0a",
"name": "CONFIG_5",
"enabled": true,
}
]
}
As per your code requirement you need to use put method in follwoing way.
def put(self, request, *args, **kwargs):
data = request.data
ticket_ids = [i['ticket_id'] for i in data]
self.validate_ids(ticket_ids)
instances = []
for temp_dict in data:
ticket_id = temp_dict['ticket_id']
name = temp_dict['name']
description = temp_dict['description']
obj = self.get_object(ticket_id)
obj.name = name
obj.description = description
obj.save()
instances.append(obj)
serializer = DemoSerializer(instances, many=True)
return Response(serializer.data)
So I am trying to send a request body for DELETE in DRF. I know by default,DRF doesn't support bulk operations so I am using django-rest-framework-bulk.
Now as per the tutorial, I am making sure the bulk deletes are allowed only when the query is filtered.I am using BulkModelViewset.
Consider this json array:
[{
"id": "1",
"first_name": "bruce",
"second_name": "banner",
"team": "avengers"
},
{
"id": "2",
"first_name": "clark",
"second_name": "kent",
"team": "Justice League"
}
{
"id": "3",
"first_name": "dead",
"second_name": "pool",
"team": "x force"
}]
Now the I could delete in bulk by passing a filter like :
DELETE /api_name/?first_name=bruce,clark
But my frontend people want to send a request body because if there are 100 objects which are needed to be deleted, they are not expected to send 100 ids as comma separated string in query parameter.
Is there a way I could send a request body to be deleted like I do in POST,PUT,etc. Like,
[{
"id": "1",
"first_name": "bruce",
"second_name": "banner",
"team": "avengers"
},
{
"id": "2",
"first_name": "clark",
"second_name": "kent",
"team": "Justice League"
}]
and the passed objects would be deleted.
I am new to REST principles so there could be fault in my entire logic. I tried to find answers online but couldn't find something specific.Thanks
Sample Viewset:
class TeamViewSet(BulkModelViewSet):
serializer_class = TeamViewSerializer
queryset = TeamView.objects.all()
filter_backends = (DjangoFilterBackend,filters.OrderingFilter,)
filter_class =TeamViewFilter
ordering = ('id','second_name','first_name','team')
def allow_bulk_destroy(self, qs, filtered):
return filtered
You have two options for customizing this logic:
Using APIView Link: https://www.django-rest-framework.org/tutorial/3-class-based-views/
Using ViewSet Link: https://www.django-rest-framework.org/api-guide/viewsets/#viewset-actions
I suggest to go for second one and override destroy method of the viewset. Otherwise, if you want to go with the first option, you should override delete method.
Examples from the documentation:
1.
class UserViewSet(viewsets.ViewSet):
"""
Example empty viewset demonstrating the standard
actions that will be handled by a router class.
If you're using format suffixes, make sure to also include
the `format=None` keyword argument for each action.
"""
def list(self, request):
pass
def create(self, request):
pass
def retrieve(self, request, pk=None):
pass
def update(self, request, pk=None):
pass
def partial_update(self, request, pk=None):
pass
# THIS ONE SHOULD BE OVERRIDED
def destroy(self, request, pk=None):
pass
class SnippetDetail(APIView):
"""
Retrieve, update or delete a snippet instance.
"""
def get_object(self, pk):
try:
return Snippet.objects.get(pk=pk)
except Snippet.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
snippet = self.get_object(pk)
serializer = SnippetSerializer(snippet)
return Response(serializer.data)
def put(self, request, pk, format=None):
snippet = self.get_object(pk)
serializer = SnippetSerializer(snippet, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
# THIS ONE SHOULD BE OVERRIDED
def delete(self, request, pk, format=None):
snippet = self.get_object(pk)
snippet.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
I am fetching data from RetrieveAPIView I want to overwrite it.
class PostDetailAPIView(RetrieveAPIView):
queryset = Post.objects.all()
serializer_class = PostDetailSerializer
lookup_field = 'slug'
http://127.0.0.1:8000/api/posts/post-python/
it return me result
{
"id": 2,
"title": "python",
"slug": "post-python",
"content": "content of python"
}
I want to overwrite this with some extra parameters like
[
'result':
{
"id": 2,
"title": "python",
"slug": "post-python",
"content": "content of python"
},
'message':'success'
]
you can override the retrieve method. in this way you can send extra data to response.
def retrieve(self, request, *args, **kwargs):
response = super().retrieve(request, args, kwargs)
response.data["custom_data"] = "my custom data"
return response
Ok, in comment I made wrong call, you want to overwrite get() method of your View.
class PostDetailAPIView(RetrieveAPIView):
queryset = Post.objects.all()
serializer_class = PostDetailSerializer
lookup_field = 'slug'
def get(self, request, slug):
post = self.get_object(slug)
serializer = PostDetailSerializer(post)
return Response({
'result': serializer.data,
'message': 'success'
})
Notes
1. I names second argument of get function slug because it's your lookup_field, but it should be name you used in urls.
2. You could instead overwrite retrieve() function
3. This is answer specific for your question, you should also read this answer
I have a model Foo which I use as the model for my vanilla DRF serializer.
models.py
class Foo(models.Model):
name = models.CharField(max_length=20)
description = models.TextField()
is_public = models.BooleanField(default=False)
serializers.py
class FooSerializer(serializers.ModelSerializer):
class Meta:
model = Foo
views.py
class FooRetrieveAPIView(RetrieveAPIView):
queryset = Foo.objects.all()
serializer_class = FooSerializer
Now the result of this endpoint is being used by front-end code, which is then the basis on how the next page to show is identified. Anyway, I need to change the structure of the results returned for both status 200 (existing record) and 404 (non-existent record).
Actual result (from vanilla DRF):
$ curl localhost:8000/foo/1/ # existing record
{"id": 1, "name": "foo", "description": "foo description", is_public=false}
$ curl localhost:8000/foo/2/ # non-existent record
{"detail": "Not found."}
How I want the results to be:
$ curl localhost:8000/foo/1/
{"error": "", "foo": {"id": 1, "name": "foo", "description": "foo description", is_public=false}}
$ curl localhost:8000/foo/2/
{"error": "Some custom error message", "foo": null}
I've mostly used vanilla DRF so things are pretty straightforward so this customization of the response structure is a bit new to me.
Django version used: 1.9.9
DRF version used: 3.3.x
You can rewrite retrieve method in your view in order to update your serializer response data
class FooRetrieveAPIView(RetrieveAPIView):
queryset = Foo.objects.all()
serializer_class = FooSerializer
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
data = serializer.data
# here you can manipulate your data response
return Response(data)
I had a similar problem and didn't want to create a custom exception handler as I wanted more control over defining error messages for api calls.
After a bit of hand banging I used the following code in viewset to get custom error message while updating the record using partial_update.
def partial_update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
try:
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
headers = self.get_success_headers(serializer.data)
response = {"status": "True", "message": "", "data": serializer.data}
return Response(response, status=status.HTTP_201_CREATED, headers=headers)
except exception.Http404:
headers = ""
response = {"status": "False", "message": "Details not found", "data": ""}
return Response(response, status=status.HTTP_200_OK, headers=headers)