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')
Related
I use redoc + drf_yasg to generate the documentation of my api made with django-rest-framwork.
It works well as soon as I add an entry point it appears well in the documentation.
However, I don't understand how to add in the documentation the search parameters that can be given during the query.
For example, in the list function, I can pass 2 optional parameters, an id and name:
class TechnicalDataViewSet(viewsets.ViewSet):
"""
A simple ViewSet for listing or retrieving machine.
"""
permission_classes = [permissions.IsAuthenticated]
def list(self, request):
id_machine = self.request.query_params.get('id_machine')
name = self.request.query_params.get('name')
queryset = TechnicalData.objects.all()
if id_machine:
queryset = queryset.filter(machine__id=id_machine)
if name:
queryset = queryset.filter(name=name)
serializer = TechnicalDataSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
queryset = TechnicalData.objects.all()
technical_data = get_object_or_404(queryset, pk=pk)
serializer = TechnicalDataSerializer(technical_data)
return Response(serializer.data)
I don't see any mention of the parameters in the documentation. How can I add them?
I don't know if this helps but here is how I implemented redoc:
schema_view = get_schema_view(
openapi.Info(
title="test API",
default_version='v1',
description=f"""
# Public API
**Reserved to authenticated users**
Token Lifetime :
* ACCESS_TOKEN_LIFETIME : {duration(settings.SIMPLE_JWT.get('ACCESS_TOKEN_LIFETIME'))}
* REFRESH_TOKEN_LIFETIME : {duration(settings.SIMPLE_JWT.get('REFRESH_TOKEN_LIFETIME'))}
### Links
[Get access token](************)
### Format
Use `?format=` option in url:
* `?format=api` (default)
* `?format=json`
""",
terms_of_service="",
contact=openapi.Contact(email="***********"),
),
public=True,
permission_classes=(permissions.AllowAny,),
)
urlpatterns = [
path('docs/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
]
You have to add your parameters to your view if you want to give info about them. I use drf_yasg with swagger but maybe this might help.
Here's an example.
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
class TechnicalDataViewSet(viewsets.ViewSet):
"""
A simple ViewSet for listing or retrieving machine.
"""
permission_classes = [permissions.IsAuthenticated]
id_machine = openapi.Parameter('id_machine', openapi.IN_QUERY, type=openapi.TYPE_INTEGER, required=False)
name = openapi.Parameter('account', openapi.IN_QUERY, type=openapi.TYPE_STRING, required=False)
#swagger_auto_schema(manual_parameters=[id_machine, name])
def list(self, request):
id_machine = self.request.query_params.get('id_machine')
name = self.request.query_params.get('name')
queryset = TechnicalData.objects.all()
if id_machine:
queryset = queryset.filter(machine__id=id_machine)
if name:
queryset = queryset.filter(name=name)
serializer = TechnicalDataSerializer(queryset, many=True)
return Response(serializer.data)
#swagger_auto_schema(manual_parameters=[id_machine, name])
def retrieve(self, request, pk=None):
queryset = TechnicalData.objects.all()
technical_data = get_object_or_404(queryset, pk=pk)
serializer = TechnicalDataSerializer(technical_data)
return Response(serializer.data)
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.
I have an API that returns list of all likes.
Here is model for likes:
class Like(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
class Meta:
constraints = [
models.UniqueConstraint(fields=['user', 'post'], name='like_once')
]
Here is my view:
class likeList(mixins.CreateModelMixin, mixins.DestroyModelMixin, generics.ListAPIView):
serializer_class = likeserializers
def get_queryset(self):
return Like.objects.all()
def post(self, request, *args, **kwargs):
try:
return self.create(request, *args, **kwargs)
except IntegrityError:
content = {'error': 'IntegrityError'}
return Response(content, status=status.HTTP_400_BAD_REQUEST)
def destroy(self, request, *args, **kwargs):
try:
return self.destroy(request, *args, **kwargs)
except IntegrityError:
content = {'error': 'IntegrityError'}
return Response(content, status=status.HTTP_400_BAD_REQUEST)
Here are my urls:
from django.conf import settings
from django.conf.urls.static import static
from django.urls import path
from . import views as API_views
urlpatterns = [
path('users/', API_views.userList.as_view(), name = 'users'),
path('users/id=<int:id>/', API_views.userListbyID.as_view(), name = 'usersid'),
path('posts/', API_views.postList.as_view(), name = 'post'),
path('posts/id=<int:id>', API_views.postListbyID.as_view(), name = 'postid'),
path('likes/', API_views.likeList.as_view(), name = 'likes'),
path('likes/test/<int:id>', API_views.likeListbyID.as_view(), name = 'likesid'),
path('likes/<int:id>', API_views.LikeCountView.as_view(), name='likecount'),
path('follows/', API_views.followList.as_view(), name = 'follows'),
path('follows/id=<int:id>', API_views.followListbyID.as_view(), name = 'followsid'),
]
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL, document_root=settings.MEDIA_URL)
I call the delete api with json body {"user":userid,"post":postid}
however, I receive method Not Allowd (HTTP 405). Also I can see on the API page "Allow: GET, POST, HEAD, OPTIONS"
What am I doing wrong?
FYI: The get and create methods work as expected.
You need to use two different views for these operations. List and Create mixins do not require object pk but Destroy mixin does. You need to split Destroy mixin to another view as sampled in docs. If you do not supply pk to delete, the operation fails.
I just want to use caching to a ViewSet too slow :(, with Django REST Framework.
I've do this :
...
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.decorators.vary import vary_on_cookie
...
class PRPKViewSet(viewsets.ModelViewSet):
serializer_class = PrpkSerializer
queryset = Prpk.objects.all().order_by('begin')
# Authentification !
permission_classes = (IsAuthenticated,)
# Only 'get' method
http_method_names = ['get']
# Cache requested url for each user for 2 hours
# #method_decorator(vary_on_cookie)
#method_decorator(cache_page(60*2))
def get_queryset(self):
""" allow rest api to filter by submissions """
queryset = Prpk.objects.all().order_by('begin')
highway = self.request.query_params.get('highway', None)
if highway is not None:
queryset = queryset.filter(highway=highway)
return queryset
But when querying, I've this error :
TypeError: _wrapped_view() missing 1 required positional argument: 'request'
Memcached is installed.
So, can I caching just one ViewSet (not using an extension ?) ?
Thanks a lot.
F.
Decorate dispatch instead of get_queryset.
#method_decorator(cache_page(60*2))
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
I'm trying to update an object of my database (but only one of the field), the problem is that when I try to make the update i get an error that says that the PUT method is not allowed.
Here's my View:
class DeviceViewSet(viewsets.ModelViewSet):
"""
Show, create and filter devices.
"""
queryset = Device.objects.all()
serializer_class = DeviceSerializer
def list(self, request, *args, **kwargs):
devices = Device.objects.filter(user=request.user.pk, role='E')
serializer = DeviceSerializer(devices, many=True)
return Response(serializer.data)
def create(self, request, *args, **kwargs):
data = {
'registration_id': request.data['regId'], 'user': request.user.pk, 'device_id': request.data['imei'],
'type': 'android', 'label': request.data['label'], 'role': request.data['role']
}
serializer = DeviceSerializer(data=data)
if serializer.is_valid():
serializer.save()
device = Device.objects.filter(device_id=request.data['imei'])
device.send_message("Enhorabuena!", "El dispositivo se ha registrado correctamente.")
return Response(serializer.data)
return Response(serializer.errors)
def update(self, request, *args, **kwargs):
device = Device.objects.filter(device_id=request.data['imei'])
device.registration_id = request.data['regId']
device.save()
serializer = DeviceSerializer(device)
return Response({'ok': 'oks'})
My serializer:
class DeviceSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), required=False)
class Meta:
model = Device
fields = '__all__'
My url:
from django.conf.urls import url
from rest_framework.urlpatterns import format_suffix_patterns
from decaught import views
urlpatterns = [
url(r'^devices/$', views.DeviceViewSet),
]
urlpatterns = format_suffix_patterns(urlpatterns)
I'm using Postman to send a PUT Request:
Any idea what is wrong?
When PUTting the resource identifier should be in the URL (pk). PUT request is idempotent.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT
In the DRF documentation, pk is passed as an argument to the update method
def update(self, request, pk=None):
pass
http://www.django-rest-framework.org/api-guide/viewsets/#viewset-actions
Instead of passing it as a key:value pair and accessing it through request.data PUT call should be like
localhost:8000/devices/<PK-HERE>/
(sorry for not so good english)
The error is in our URL. You need to select some device to PUT information. Try with localhost:8000/devices/1/. I'm assuming that your API take objects by pk