I would like to update a record in my database with the REST framework. I'm getting an message of method not allowed for anything but "GET".
views.py
class MetadataViewTest(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView):
queryset = deployment.objects.all()
serializer_class = SerializerTest
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
urls.py
urlpatterns = [
path('api/metadata_test/<int:pk>/', views.MetadataViewTest.as_view())
]
serializers.py
class SerializerTest(serializers.ModelSerializer):
class Meta:
model = deployment
fields = [field.name for field in deployment._meta.fields]
I've tried the request through postman as a PATCH PUT or POST at api/metadata_test/20/
This is a simplified version, I plan on overriding the get put and delete functions.
Update your code to:
views.py
class MetadataViewTest(generics.RetrieveUpdateDestroyAPIView):
queryset = deployment.objects.all()
serializer_class = SerializerTest
urls.py
urls.py
urlpatterns = [
path('api/metadata_test/', views.MetadataViewTest.as_view()) # Should to the list, not detail
]
Call API like this PUT/PATCH: api/metadata_test/20/
It better if you use viewsets.GenericViewSet instead of generics.GenericAPIView because you are using ...ModelMixin.
Your router like this
from rest_framework import routers
api_router = routers.DefaultRouter()
api_router.register('metadata_test', MetadataViewTest, basename='metadata_test')
...
All you need to do is instead of calling /api/metadata_test/ call /api/metadata_test/12345/
Where 12345 is the id/pk of the record. I am assuming that id is the primary key of the model.
Related
I'm looking for how to make a GET in django swagger framework by refClient (a unique CharField in my client's model).
I found on the internet that I have to customize the routers, I have that as routers :
from rest_framework.schemas import get_schema_view
from rest_framework_swagger.renderers import SwaggerUIRenderer, OpenAPIRenderer
schema_view = get_schema_view(
title='Swagger documentation',
renderer_classes = [OpenAPIRenderer, SwaggerUIRenderer],
)
# Routers provide an easy way of automatically determining the URL conf.
router = routers.DefaultRouter(trailing_slash=False)
router.register(r'clients/{refClient}', ClientViewSet)
# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
url(r'^admin/', admin.site.urls),
# To show the swagger documentation
url(r'^swagger/', schema_view, name="docs"),
url(r'^api/v1/', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
]
But I got this error
ValueError: invalid literal for int() with base 10: 'refClient'
[21/Jul/2017 15:25:22] "GET /swagger/ HTTP/1.1" 500 133494
Should I add something to my serializers's configuration
class ClientSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Client
fields = ('refClient', 'nom', 'prenom')
Or to my views ?
class ClientViewSet(viewsets.ModelViewSet):
queryset = Client.objects.all()
serializer_class = ClientSerializer
def get_queryset(self):
""" GET : get all clients """
return Client.objects.all()
def create(self, request):
""" POST : Create a Client object """
return super(ClientViewSet, self).create(request)
def retrieve(self, request, pk=None):
""" GET : Returns a single client item """
return super(ClientViewSet, self).retrieve(request, pk)
def update(self, request, *args, **kwargs):
""" PUT : Updates a single client item """
return super(ClientViewSet, self).update(request, *args, **kwargs)
def partial_update(self, request, *args, **kwargs):
""" PATCH : Partiel update a client """
return super(ClientViewSet, self).partial_update(request, *args, **kwargs)
def destroy(self, request, pk=None):
""" DELETE : Delete a client """
return super(ClientViewSet, self).destroy(request, pk)
Basically how I can customize my swagger ?
By default django rest frameworks uses this
lookup_field = 'pk'
lookup_url_kwarg = None
You can override this in your ClientViewSet class, change to
lookup_field = 'refClient'
However if you want to support both lookups, either by 'pk' or by 'refClient' I suggest to have a different endpoint for the later one (eg. /api/client_url/ref/), or add filtering option in your list view (eg. /api/client_url?refClient='something')
I am using Djano REST Framework for constructing APIs .I want something like below
def addTwoNumber(a,b):
return a+b
class MyView(viewsets.ModelViewSet):
def create(self, request, *args, **kwargs):
my_result=addTwoNumber(request.data.get('firstnum'),request.data.get('secondnum'))
return Response(data={"my_return_data":my_result})
That is , I want a view that doesn't deals with the queryset &serializer_class attributes. Is it possible ? Can anyone help me ?
Why are you using ModelViewSet? Just use APIView http://www.django-rest-framework.org/api-guide/views/
# views.py
from rest_framework.views import APIView
def addTwoNumber(a,b):
return a+b
class MyView(APIView):
def post(self, request, *args, **kwargs):
my_result=addTwoNumber(request.data.get('firstnum'),request.data.get('secondnum'))
return Response(data={"my_return_data":my_result})
# urls.py
urlpatterns = [
url(r'^myview/$', MyView.as_view()),
...
]
I have a model like so:
class GiveAbsolute(serializers.Field):
def to_native(self,value):
# this where it give an error (self doesn't have request)
# what i want it to give full url
# like: http://www.blabla.com/othermodel/1
return reverse('link_to_othermodel',
args=[value],
request=self.request)
class SomethingSerializer(serializers.ModelSerializer):
# field with foreign key
othermodel = GiveAbsolute(source="othermodel.id")
class Meta:
model=Something
fields("fields1","othermodel")
is there a way to achieve this ?
thanks
From the source
The request object is an entry of the context dictionary. ie.
request = self.context.get('request')
In your case, just do:
self.request = self.context.get('request')
then build the url
self.request.build_absolute_uri(reverse('some_url_name'))
Based on the answer of mariodev, here is a reusable solution for Models ; I use it to provide URLs to service (see them as metheds) on django models.
Reusable components
serializers.py
class RequestAwareSerializer(serializers.ModelSerializer):
"""
A serializer which fields can access the request object.
"""
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(RequestAwareSerializer, self).__init__(*args, **kwargs)
class APIMethodField(serializers.Field):
""" To get the absolute URL of a method accessible via the API
"""
def __init__(self, url_action_name, *args, **kwargs):
self._url_name = url_action_name
super(APIMethodField, self).__init__(source='*', *args, **kwargs)
def to_native(self, obj):
"""
#param objid the ID of the object
#param method_url_name, the name of the url, as in urls.py
"""
return reverse_lazy(self._url_name, args=[obj.id],
request=self.parent.request)
views.py
class ChattyModelViewSet(ModelViewSet):
""" ModelViewSet which informs the serializer about the request
(abstract)
"""
def get_serializer(self, instance=None, data=None,
files=None, many=False, partial=False):
serializer_class = self.get_serializer_class()
context = self.get_serializer_context()
return serializer_class(instance, data=data, files=files, many=many,
partial=partial, context=context,
request=self.request)
Example use
urls.py
url(r'^v1/maildomain/(?P<maildomain_id>\d+)/check/$',
views.MailDomainDetail.as_view(), name='maildomain_dns_check')
serializers.py
class MailDomainSerializer(RequestAwareSerializer):
checkdns_url = APIMethodField(url_action_name='maildomain_dns_check')
class Meta:
model = MailDomain()
fields = ('name', 'checkdns_url')
views.py
class MailDomainView(ChattyModelViewSet):
model = MailDomain
serializer_class = MailDomainSerializer
The only thing in DRF, that has an access to request object is the view, so you need to figure out how to pass your request from view to serializer, for example in generic ListView you can use get_serializer.
Then, when you already have it in your serializer, you can use self.parent (which is a parent serializer) to capture it from the field itself:
class GiveAbsolute(serializers.Field):
def to_native(self,value):
return reverse('link_to_othermodel',
args=[value],
request=self.parent.request)
class SomethingSerializer(serializers.ModelSerializer):
# field with foreign key
othermodel = GiveAbsolute(source="othermodel.id")
class Meta:
model=Something
fields=("fields1","othermodel")
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(SomethingSerializer, self).__init__(*args, **kwargs)
class SomethingView(generics.ListAPIView):
model = Something
serializer_class = SomethingSerializer
def get_serializer(self, instance=None, data=None,
files=None, many=False, partial=False):
serializer_class = self.get_serializer_class()
context = self.get_serializer_context()
return serializer_class(instance, data=data, files=files, many=many,
partial=partial, context=context, request=self.request)
I am trying to build an API view, to handle user management using django rest framework version 2.3.10 with django 1.6. I tried to build a ModelViewSet which based on the URL pk value it would return either current user or public user.
I tried to add a dispatch function which will assigned pk to current user, but it seems like this function is running too soon that its always seeing the user as anonymous
class UserViewSet(viewsets.ModelViewSet):
"""
"""
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (IsOwnerOrCreateOnly, )
def dispatch(self, request, *args, **kwargs):
if kwargs.get('pk') == 'current' and not request.user.is_anonymous():
kwargs['pk'] = request.user.pk
resp = super(CurrentUserViewSet, self).dispatch(request, *args, **kwargs)
return resp
I tried to do the below, which works
class UserViewSet(viewsets.ModelViewSet):
"""
"""
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (IsOwnerOrCreateOnly, )
def retrieve(self, request, *args, **kwargs):
if self.kwargs.get('pk') == u'current' and not request.user.is_anonymous():
self.kwargs['pk'] = request.user.pk
return super(CurrentUserViewSet, self).retrieve(request, *args, **kwargs)
but, I don't want to override each and every function on several ModelViewSet classes I have, so, is there a way to use something similar to the dispatcher whereby I can check if the pk is equal to "current" and then assign current user to it?
Another question, how can I change the returned fields programmatically? for example when querying current user I want to include the first and last name from the user model, but when querying by primary key, I want first and last name to not return as response? any suggestions on how todo that?
I got the same problem I solved it by using method "initial" instead of "dispatch"
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (IsOwnerOrCreateOnly, )
def initial(self, request, *args, **kwargs):
# logic - code #
if kwargs.get('pk') == 'current' and not request.user.is_anonymous():
kwargs['pk'] = request.user.pk
# end #
resp = super(CurrentUserViewSet, self).initial(request, *args, **kwargs)
return resp
see " dispatch "
method in https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/views.py
for better understanding.
Override viewsets.ModelViewSet class with your pk check implementation and use that new class, something like this:
class GenericUserViewSet(viewsets.ModelViewSet):
def retrieve(self, request, *args, **kwargs):
if self.kwargs.get('pk') == u'current' and not request.user.is_anonymous():
self.kwargs['pk'] = request.user.pk
return super(CurrentUserViewSet, self).retrieve(request, *args, **kwargs)
class UserViewSet(GenericUserViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (IsOwnerOrCreateOnly, )
And for the second question, perhaps creating two serializers (public and current) and changing serializer_class to either one of them in init of GenericUserViewSet may do the trick, I haven't tested this but it's an idea:
class GenericUserViewSet(viewsets.ModelViewSet):
def __init__(self, *args, **kwargs):
if self.kwargs.get('pk') == u'current' and not request.user.is_anonymous():
self.serializer_class = UserSerializer
else:
self.serializer_class = PublicUserSerializer
super(GenericUserViewSet, self).__init__(*args, **kwargs)
I'm assuming that you want to save the current user to your DB model, yes?
If so this should be fairly easy to fix, just add this method to your views:
def pre_save(self, obj):
obj.user = self.request.user
This will execute just before the model is saved. I use this all the time and it works great.
The other thing you can do is write a mixin class in a generic way that does want you want then inherit it in each of the views you need it in. Assuming that is that you have a solution that works, but just don't want to mimic you code all over the place.
I have the following model that basically stores a random hash value for each tag associated with a particular user.
class PublicTags(models.Model):
tag = models.ForeignKey(Tag, related_name='hashes')
hash_value = models.CharField(max_length=103)
user = models.ForeignKey(User, related_name='tags')
public = models.BooleanField(default=False)
class Meta:
unique_together = ('tag', 'user')
verbose_name_plural = 'Public Tag Hashes'
def __unicode__(self):
return u'%s: %s' % (self.tag, self.user, self.hash_value)
I am trying to use the Django Rest Framework to do the following:
Create a view that will be accessed at api/v1/public_tags.
I will use AJAX to post data using the Django Rest API.
On a post, the system does the following:
It checks to see if there is already a Public Tag row for the tag id (sent via post)
If not, it creates a random hash_value for that tag and user.
I am confused about how to use the Django Rest Framework to accomplish this task.
I got this far:
class PublicTagView(RetrieveUpdateAPIView):
model = PublicTags
serializer_class = serializers.PublicTagSerializer
permission_classes = (IsAuthenticated,)
class RetrieveUpdateAPIView(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
generics.SingleObjectAPIView):
"""
Concrete view for retrieving or updating a model instance.
FIXME: the newest version of rest_framework has this class
"""
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def create(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
As far as I understand you need to query an individual record and create a record.
In this case generic class based views are fine enough to start with.
You can then map these views in your urls.py like this:
urlpatterns = patterns('',
url(r'^api/v1/public_tags/$', views.PublicTagsList.as_view()),
url(r'^api/v1/public_tags/(?P<pk>[0-9]+)/$', views.PublicTagsDetail.as_view()),
)
This is valid if you use primary key to fetch individual record.