Method Not Allowed - Django Rest Framework - django

I am getting error while updating. When i add data it is added successfully. This error is only comming for UpdateAPIView
{
"detail": "Method \"POST\" not allowed."
}
urls.py
path('groups/update/<int:pk>', views.GroupsUpdateAPIView.as_view(), name='api_groups_update'),
Views.py
class GroupsUpdateAPIView(generics.UpdateAPIView):
queryset = Groups.objects.all()
serializer_class = GroupsAddSerialzer
permission_classes = [UserIsAuthenticated]
def perform_update(self, serializer):
serializer.save(
group_updated_by = self.request.auth.application.user,
)
Serializer.py
class GroupsAddSerialzer(serializers.ModelSerializer):
class Meta:
model = Groups
fields = ['group_name', 'group_description', 'group_status']

The UpdateAPIView view uses the HTTP methods PUT and PATCH. The method POST is used to create a new instance with CreateAPIView view.

Related

unable to specify the fields in drf queryset using drf-yasg

class ABC(generics.ListCreateApiView):
#swagger_auto_schema(
operation_description="THIS API IS TO CREATE MESSAGES IN A LIST ",
auto_schema=AcceptFormDataSchema,
request_body=MessageGetSerializer
)
def get_queryset(self):
data =self.request.GET.get("code")
...
#swagger_auto_schema(
operation_description="THIS API IS TO CREATE MESSAGES IN A LIST ",
request_body=openapi.Schema(
type=openapi.TYPE_OBJECT,
required=["data"],
properties={
"code": openapi.Schema(type=openapi.TYPE_STRING),
},
)
)
def post(self, request):
brand_code = request.data.get("code")
...
#serializer.py
class MessageSerializer(serializers.ModelSerializer):
class Meta:
model = Messages
fields = ("message_id", "content", "description")
My post method is working fine with the fields which I required using the same serializer but it's not working for the get_queryset method. Can anyone please suggest something on how I will get the fields using drf-yasg?
You shouldn't decorate get_queryset as that is an internal function and not part of the ApiView's endpoints. You probably see a get request in swagger because the ListCreateApiView you're using defines handlers for get and post methods.
Since you're not overriding the get method handler, you can inject a decorator into the ApiView's get method using Django's method_decorator, as indicated in drf-yasg section on swagger_auto_schema decorator.
The following is an example implementation to your ApiView that should also document the get endpoint.
#method_decorator(
name='get',
decorator=swagger_auto_schema(
operation_description="description from swagger_auto_schema via method_decorator"
)
)
class ABC(generics.ListCreateApiView):
serializer_class = MessageSerializer
def get_queryset(self):
data =self.request.GET.get("code")
...
#swagger_auto_schema(
operation_description="THIS API IS TO CREATE MESSAGES IN A LIST ",
request_body=openapi.Schema(
type=openapi.TYPE_OBJECT,
required=["data"],
properties={
"code": openapi.Schema(type=openapi.TYPE_STRING),
},
)
)
def post(self, request):
brand_code = request.data.get("code")
...
--------------
#serializer.py
class MessageSerializer(serializers.ModelSerializer):
class Meta:
model = Messages
fields = ("message_id", "content", "description")

Django: multiple update

I want to update multiple instances of my models. I can currently update them one by one just fine.
I want to be able to update them with a PUT request to my URL:
www.my-api.com/v1/mymodels/
with the request data like so:
[ { "mymodel_id": "id1", "name": "foo"}, { "mymodel_id": "id2", "alert_name": "bar"} ]
If I try it this way now, I receive the following Django error:
Serializers with many=True do not support multiple update by default, only multiple create.
For updates it is unclear how to deal with insertions and deletions.
If you need to support multiple update, use a `ListSerializer` class and override `.update()` so you can specify the behavior exactly.
My model has a Serializer class MyModelSerializer
class MyModelSerializer(ModelSerializerWithFields):
class Meta:
model = MyModel
fields = "__all__"
def to_representation(self, instance):
data = super().to_representation(instance)
if instance.name is None:
del data['name']
return data
ModelSerializerWithFields extends serializers.ModelSerializer.
The View for MyModel is very basic:
class MyModelViewSet(MultipleDBModelViewSet):
serializer_class = MyModelSerializer
queryset = MyModel.objects.none()
MultipleDBModelViewSet extends BulkModelViewSet, and contains
def filter_queryset(self, queryset):
ids = self.request.query_params.get("ids", None)
if ids:
return queryset.filter(pk__in=json.loads(ids))
# returns normal query set if no param
return queryset
At which level do I need to use the ListSerializer class? ie: in ModelSerializerWithFields or in MyModelSerializer? Or somewhere else completely?
If anyone has any examples of this implementation, I'd be very grateful
Serializer Must be inherited from BulkSerializerMixin
So the serializer code will be like
from rest_framework_bulk.serializers import BulkListSerializer, BulkSerializerMixin
class SimpleSerializer(BulkSerializerMixin,
ModelSerializer):
class Meta(object):
model = SimpleModel
# only required in DRF3
list_serializer_class = BulkListSerializer
At Viewset don't forget to use the
filter_queryset method.
So your view will be like
class MyModelViewSet(MultipleDBModelViewSet):
serializer_class = MyModelSerializer
queryset = MyModel.objects.none()
def filter_queryset(self, queryset):
return queryset.filter(<some_filtering>)

How to Create a Django Model Instance That Contains a Required OneToOneField with DRF Serializers

The Question
Let's say I have a model that contains a OneToOneField like so:
models.py
class Event(models.Model)
# some basic fields...
class EventDetail(models.Model):
event = models.OneToOneField(Event, on_delete=models.CASCADE,
related_name='event_detail')
# other basic fields, all with default values...
What is a proper way to implement a POST request that intends to create a new Event in the database that automatically creates a default EventDetail linked to it if it is None in the request header, using Django Rest Framework's Serializers?
My Attempt
test.py
class EventTestCase(APITestCase):
def test_post(self):
# Need to provide an id, or else an error occurs about the OneToOneField
event = Event(id=1)
serializer = serializers.EventSerializer(event)
res = self.api_client.post('/event/', serializer.data)
views.py
def post(self, request, format=None):
serializer = EventSerializer(
data=request.data)
# ***This always returns false!***
if serializer.is_valid():
try:
instance = serializer.save()
except ValueError:
return Response(status=status.HTTP_400_BAD_REQUEST)
serializers.py
class EventSerializer(serializers.ModelSerializer):
serialization_title = "Event"
event_detail = EventDetailSerializer()
class Meta:
model = Event
exclude = ('id',)
error_status_codes = {
HTTP_400_BAD_REQUEST: 'Bad Request'
}
class EventDetailSerializer(serializers.ModelSerializer):
serialization_title = "Event Detail"
class Meta:
model = models.EventDetail
exclude = ('id',)
error_status_codes = {
HTTP_400_BAD_REQUEST: 'Bad Request'
}
As noted in a comment above, serializer.is_valid() always returns false with the error:
{'event_detail': [u'This field may not be null.']}
I understand that this is complaining because both EventDetail and Event need to be created in order for a OneToOne relationship to be added, but how do you deal with this using Serializers when a OneToOneField is required and yet not provided by the client?
Thank you for your help.
Disclaimer: I am using Django 1.11
You can declare the EventDetailSerializer with read_only=True or required=False and then handle the creation of the EventDetail in different ways, for example: you could have a post_save signal that listens to the Event class - once a new Event object has been created, you can then create the initial EventDetail object, or you perform this creation after your serializer.save() on the post definition, or even on your create method of your EventSerializer.
edit: an example on how you could perform the creation using the EventDetailSerializer and overriding the create method of your EventSerializer.
def create(self, validated_data):
detail = self.initial_data.get('event_detail')
instance = super().create(validated_data)
if detail:
serializer = EventDetailSerializer(data=detail)
serializer.is_valid(raise_exception=True)
serializer.save(event=instance)
return instance

POST, PUT, DELETE in Django RESTful framework not working

I'd like to post to my Django server using POST so I can add a employee item.
views.py
class EmployeeList(generics.ListAPIView):
queryset = Employee.objects.all()
serializer_class = EmployeeSerializer
class EmployeeDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Employee.objects.all()
serializer_class = EmployeeSerializer
models.py
class Employee(models.Model):
firstname=models.CharField(max_length=10)
lastname=models.CharField(max_length=10)
emp_id=models.IntegerField()
def __str__(self):
return self.firstname;
serializer.py
class EmployeeSerializer(serializers.ModelSerializer):
class Meta:
model=Employee
# fields={'firstname','lastname'}
fields='__all__'
My POST request:
{
"id": 8,
"firstname": "zxcvb",
"lastname": "bnmmm",
"emp_id": 3
}
Error I got:
{
"detail": "Method \"POST\" not allowed."
}
Even after using generics.RetrieveUpdateDestroyAPIView I can't POST, PUT or DELETE.
PS: I’m new to Django REST framework.
To provide a post method handler you'll need a view with a create() action (CreateAPIView or ListCreateAPIView). Check the documention about concrete view classes.
Change your EmployeeList view to inherit from ListCreateAPIView for add read-write endpoints to represent a collection of employees
class EmployeeList(generics.ListCreateAPIView):
queryset = Employee.objects.all()
serializer_class = EmployeeSerializer
Now, just register the views with the URL conf as usual
urlpatterns = [
path('employees/', EmployeeList, name='employee-list'),
path('employees/<int:pk>/', EmployeeDetail, name='employee-detail'),
]
Concrete view classes map methods defined in mixins such ListModelMixin, CreateModelMixin, RetrieveModelMixin, etc. to HTTP methods. By defaults, theese actions (see here) are mapped to the following HTTP methods
list - get
create - post
retrieve - get
update - put
partial_update - patch
destroy - delete

Django REST Framework - is_valid() always passing and empty validated_data being returned

I have the following JSON GET request going to the server that defines a product configuration:
{'currency': ['"GBP"'], 'productConfig': ['[{"component":"c6ce9951","finish":"b16561c9"},{"component":"048f8bed","finish":"b4715cda"},{"component":"96801e41","finish":"8f90f764"},{"option":"6a202c62","enabled":false},{"option":"9aa498e0","enabled":true}]']}
I'm trying to validate this through DRF, and I have the following configuration:
views.py
class pricingDetail(generics.ListAPIView):
authentication_classes = (SessionAuthentication,)
permission_classes = (IsAuthenticated,)
parser_classes = (JSONParser,)
def get(self, request, *args, **kwargs):
pricingRequest = pricingRequestSerializer(data=request.query_params)
if pricingRequest.is_valid():
return Response('ok')
serializers.py
class pricingComponentSerializer(serializers.ModelSerializer):
class Meta:
model = Component
fields = ('sku',)
class pricingFinishSerializer(serializers.ModelSerializer):
class Meta:
model = Finish
fields = ('sku',)
class pricingOptionSerializer(serializers.ModelSerializer):
class Meta:
model = ProductOption
fields = ('sku',)
class pricingConfigSerializer(serializers.ModelSerializer):
finish = pricingFinishSerializer(read_only=True, many=True)
component = pricingComponentSerializer(read_only=True, many=True)
option = pricingOptionSerializer(read_only=True, many=True)
enabled = serializers.BooleanField(read_only=True)
class pricingCurrencySerializer(serializers.ModelSerializer):
class Meta:
model = Currency
fields = ('currencyCode',)
class pricingRequestSerializer(serializers.Serializer):
config = pricingConfigSerializer(read_only=True)
currency = pricingCurrencySerializer(read_only=True)
As you can see I'm trying to validate multiple models within the same request through the use of inline serializers.
My problem
The code above allow everything to pass through is_valid() (even when I make an invalid request, and, it also returns an empty validated_data (OrderedDict([])) value.
What am I doing wrong?
extra information
the JS generating the GET request is as follows:
this.pricingRequest = $.get(this.props.pricingEndpoint, { productConfig: JSON.stringify(this.state.productConfig), currency: JSON.stringify(this.state.selectedCurrency) }, function (returnedData, status) {
console.log(returnedData);
I currently don't have a computer to dig through the source but you might want to check the read_only parameters on your serializer. Afaik this only works for showing data in responses.
You can easily check by using ipdb (ipython debugger)
Just put:
import ipdb; ipdb.set_trace()
Somewhere you want to start debugging, start you server and start the request.