I am using Django Rest Swagger to document my API which is made with Django Rest Framework.
My APIClass has two methods: get and post. And I want the documentation to note this difference but it seems that it expect both to have the same params. Look:
This is generating documentation well:
class Product(APIView):
"""
Do something with product
param1 -- Product name
param2 -- Category
"""
def get(self, request, format=None):
...
def post(self, request, format=None):
...
But what I want is this, which is not generating documentation well:
class Product(APIView):
def get(self, request, format=None):
"""
Get products
param1 -- Parameter for filtering by category
"""
...
def post(self, request, format=None):
"""
Create a new product
param1 -- Product name
param2 -- Category
"""
...
How can I do it? Any idea?? Thanks so much! ;-)
Josep your updated method documentation is correct. The issue you report is well-known in the django-rest-swagger community. See this commit for the fix. Unfortunately Mr. Gibbons has not merged this fix into master, but you can certainly fork and patch as many other people have done.
Related
Following this tutorial, I'm trying to add a url to which one could make a POST request without a model:
router.register(r'send_message', SendMessageViewSet, base_name='send_message')
I don't need a GET, but I added one for debugging purposes:
class SendMessageViewSet(ViewSet):
def get(self, request, *args, **kwargs):
return Response(HTTP_200_OK)
def create(self, request, *args, **kwargs):
...
Yet I'm able to get the "list" (url with no pk) but not the specific resource.
Thanks!
You probably have missed my link to the full repository.
You are doomed because you are overriding get instead of list.
See https://github.com/linovia/drf-demo/blob/master/drf_demo/model_less/views.py for a full working example.
In the documentation for dynamic extension of DRF they describe the possibility to to add query parameters through the viewset.(instructions)
These added query parameters act as if they were being passed in the url request, thereby triggering dynamic actions available through the extension.
But I can't get this to work. It seems impossible to make the serializer or router to recognize the alteration of the request instance.
Any suggestions as to where to learn more about how this works, or alternative ways to do it would be greatly appreciated.
class EventViewSet(DynamicModelViewSet):
# …
def list(self, request, *args, **kwargs):
# sideload location by default
request.query_params.add('include[]', 'location.')
# filter for status=current by default
status = request.query_params.get('filter{status}')
if not status:
request.query_params.add('filter{status}','current')
return super(EventViewSet, self).list(request, *args, **kwargs)
I'm new to Django. I've worked through some tutorials, written some models, views, forms etc. but I don't understand how they can be tested since either nothing is returned, or whatever is returned is so tightly bound it doesn't make sense in terms of the test.
For example here are two view classes:
class ListBlogPostView(ListView):
model = BlogPost
template_name = 'app/blogpost_list.html'
class CreateBlogPostView(CreateView):
model = BlogPost
template_name = 'app/blogpost_edit.html'
form_class = BlogPostForm
def get_success_url(self):
return reverse('blogpost-list')
def get_context_data(self, **kwargs):
context = super(CreateBlogPostView, self).get_context_data(**kwargs)
context['action'] = reverse('blogpost-new')
return context
The first, ListBlogPostView, doesn't return anything. How can I ever check that this is working correctly?
The second has a couple of functions but their return values are not things I can test with an assert
How could I ever use a TDD approach to Django?
I'm used to using nunit and MS 'unit' tests in Visual Studio, mocking etc
Actually, Django's generic class-based views are not called generic for no reason.
You can view the source of the both ListView and CreateView.
The ListView has a GET method handler:
class BaseListView(MultipleObjectMixin, View):
"""A base view for displaying a list of objects."""
def get(self, request, *args, **kwargs):
self.object_list = self.get_queryset()
allow_empty = self.get_allow_empty()
if not allow_empty:
# ...
context = self.get_context_data()
return self.render_to_response(context)
Which returns a valid Django response and can be tested in tests.py.
If you look at the CreateView, it inherits from BaseCreateView (just like ListView inherits from BaseListView):
class BaseCreateView(ModelFormMixin, ProcessFormView):
"""
Base view for creating an new object instance.
Using this base class requires subclassing to provide a response mixin.
"""
def get(self, request, *args, **kwargs):
# ...
def post(self, request, *args, **kwargs):
# ...
Which also inherits from ProcessFormView:
class ProcessFormView(View):
"""Render a form on GET and processes it on POST."""
def get(self, request, *args, **kwargs):
"""Handle GET requests: instantiate a blank version of the form."""
return self.render_to_response(self.get_context_data())
def post(self, request, *args, **kwargs):
"""
Handle POST requests: instantiate a form instance with the passed
POST variables and then check if it's valid.
"""
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
The GET request will result in a valid response. As you see, the POST method handler here returns either self.form_valid(form) or self.form_invalid(form) depending on the form status.
You can see the source of these two methods in ViewMixin:
def form_valid(self, form):
"""If the form is valid, redirect to the supplied URL."""
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form):
"""If the form is invalid, render the invalid form."""
return self.render_to_response(self.get_context_data(form=form))
Both of these methods return a valid testable Django responses.
In conclusion, both of your ListBlogPostView and CreateBlogPostView can be directly tested in tests.py. You just need to have a more detailed look at the implementation of Django's generic views. The power of open-source!
Testing view unlike function most of the time will not have a return value. The way that I go about doing it is to assert the html response.
So for the ListBlogPostView it depends on what is in the blogpost_list.html template.
A general view test should look like this:
class ListBlogPostViewTest(TestCase):
def test_blogpost_list_view(self):
response = self.client.get(reverse('blogpost-list'))
html = response.content.decode('utf8')
self.assertTrue(html.startswith('<html>'))
self.assertIn('<title>BlogPost lists</title>', html)
self.assertTrue(html.endswith('</html>'))
For view that have context you can actually check if it is being retrieved and passed correctly to view.
blogPost = BlogPost.object.get(id=1)
self.assertEqual(response.context['blogPost'].name, blogPost.name)
How could I ever use a TDD approach to Django?
As for TDD, you just have to test the html view first before creating it. It really depend on how detail you like to test and find the balance for it. I prefer to test mainly on the context being set and important html element is in the view.
You can still surely test lot of parameters -
status code on get and post request
variables in context data (such as form)
assert template used
creation of object on post request in case of create view
check for permissions using status codes
The thing is tests on django views are technically integration tests. As long as your tests are granular enough, means you don't test code for forms or models in views, I don't see any problem provided you follow Classical TDD.
I'm struggling with DRF 3.6 auto-generated interactive documentation to provide input parameters to fill in interactive mode.
As a result, I get an empty windows for my POST request (which would require 3 parameters actually):
With Swagger, I could do it directly in docstring with some YAML.
Now, after browsing DRF documentation, I can't find the way to do it.
class ActivateCustomerView(APIView):
permission_classes = (AllowAny,)
def post(self, request):
""" View dedicated to activating a pre-recorded customer
# Should I add some parameters here?
"""
serializer = ActivateCustomerSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
# ...
I Got the answer from Tom Christie:
serializer_class by itself isn't enough - the view needs to implement get_serializer, see: https://github.com/encode/django-rest-framework/blob/master/rest_framework/schemas.py#L570
So in My case, adding this works well:
def get_serializer(self):
return ActivateCustomerSerializer()
EDIT: I forgot to answer regarding the input parameters. I believe that would be based on the serializer. Have you tried specifying your serializer_class?
With DRF's inbuilt documentation generator, you'd want to put your docstrings on the class level and include the request method as such:
class ActivateCustomerView(APIView):
"""
post:
View dedicated to activating a pre-recorded customer
# Should I add some parameters here?
# if you have a get request
get:
# your docs for the get handler
"""
permission_classes = (AllowAny,)
def post(self, request):
serializer = ActivateCustomerSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
# ...
I am trying to write a view in which a post can be created and in the same page, the object_list will be displayed. And even an object can be updated and deleted.
Country Capital
India Delhi UPDATE DELETE
USA Washington UPDATE DELETE
----- ------
I would appreciate helping me in achieve this or suggesting a similar type of question.
What you're looking for are Mixins.
Try creating a detail view class with the following parameters:
mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView
For example:
class ObjectDetail(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView):
queryset = Object.objects.all()
As has proposed by Daniel, if you like DRF, ViewSets are also a decent alternative. However, they're not exactly succinct so I generally avoid them when possible.
Something like a ModelViewSet, however, is extremely clear-cut and the approach I generally choose.
Here's an example:
class ObjectViewSet(viewsets.ModelViewSet):
queryset = Object.objects.all()
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
Beautiful, isn't it?
For more details, see the DRF tutorial: http://www.django-rest-framework.org/tutorial/6-viewsets-and-routers/
You are mixing view and template. View handle requests and template show content and links.
You will have ListView, which will contain list of posts. In template you add forms for update, form for create and forms for delete. Each form will have attribute action with link to proper view. So update forms will have link to url with UpdateView, create forms to CreateView, and delete to DeleteView. In each form you set redirect back to ListView. This way if you want to use only Django.
OR
If you really want to everything handle on one page without refreshing and redirecting. You can use ajax and django-rest-framework and its viewset. In viewset you can handle lists, create, update, push, detail, in one class.
Viewset:
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
def destroy(self, request, pk=None):
pass