Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make sure all ListModelMixin views considered as list view #306

Merged
merged 3 commits into from
Mar 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/drf_yasg/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from django.db import models
from django.utils.encoding import force_text
from rest_framework import serializers, status
from rest_framework.mixins import DestroyModelMixin, RetrieveModelMixin, UpdateModelMixin
from rest_framework.mixins import DestroyModelMixin, RetrieveModelMixin, UpdateModelMixin, ListModelMixin
from rest_framework.parsers import FileUploadParser
from rest_framework.request import is_form_media_type
from rest_framework.settings import api_settings as rest_framework_settings
Expand Down Expand Up @@ -226,6 +226,10 @@ def is_list_view(path, method, view):
# a detail action is surely not a list route
return False

# for GenericAPIView, if it's a list view then it should be a list view
if isinstance(view, ListModelMixin):
return True

# for GenericAPIView, if it's a detail view it can't also be a list view
if isinstance(view, (RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin)):
return False
Expand Down
24 changes: 24 additions & 0 deletions testproj/snippets/migrations/0003_snippetviewer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 2.1.7 on 2019-03-16 14:06

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('snippets', '0002_auto_20181219_1016'),
]

operations = [
migrations.CreateModel(
name='SnippetViewer',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('snippet', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='viewers', to='snippets.Snippet')),
('viewer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='snippet_views', to=settings.AUTH_USER_MODEL)),
],
),
]
5 changes: 5 additions & 0 deletions testproj/snippets/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,8 @@ class Snippet(models.Model):

class Meta:
ordering = ('created',)


class SnippetViewer(models.Model):
snippet = models.ForeignKey(Snippet, on_delete=models.CASCADE, related_name='viewers')
viewer = models.ForeignKey('auth.User', related_name='snippet_views', on_delete=models.CASCADE)
8 changes: 7 additions & 1 deletion testproj/snippets/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from rest_framework import serializers
from rest_framework.compat import MaxLengthValidator, MinValueValidator

from snippets.models import LANGUAGE_CHOICES, STYLE_CHOICES, Snippet
from snippets.models import LANGUAGE_CHOICES, STYLE_CHOICES, Snippet, SnippetViewer


class LanguageSerializer(serializers.Serializer):
Expand Down Expand Up @@ -100,3 +100,9 @@ def update(self, instance, validated_data):
instance.style = validated_data.get('style', instance.style)
instance.save()
return instance


class SnippetViewerSerializer(serializers.ModelSerializer):
class Meta:
model = SnippetViewer
fields = '__all__'
3 changes: 3 additions & 0 deletions testproj/snippets/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@
urlpatterns = [
path('', views.SnippetList.as_view()),
path('<int:pk>/', views.SnippetDetail.as_view()),
path('views/<int:snippet_pk>/', views.SnippetViewerList.as_view()),
]
else:
from django.conf.urls import url

urlpatterns = [
url('^$', views.SnippetList.as_view()),
url(r'^(?P<pk>\d+)/$', views.SnippetDetail.as_view()),
url(r'^views/(?P<snippet_pk>\d+)/$', views.SnippetViewerList.as_view()),
]
34 changes: 32 additions & 2 deletions testproj/snippets/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
from djangorestframework_camel_case.render import CamelCaseJSONRenderer
from inflection import camelize
from rest_framework import generics, status
from rest_framework.generics import get_object_or_404
from rest_framework.pagination import PageNumberPagination
from rest_framework.parsers import FormParser, FileUploadParser

from drf_yasg import openapi
from drf_yasg.inspectors import SwaggerAutoSchema
from drf_yasg.utils import swagger_auto_schema
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from snippets.models import Snippet, SnippetViewer
from snippets.serializers import SnippetSerializer, SnippetViewerSerializer


class CamelCaseOperationIDAutoSchema(SwaggerAutoSchema):
Expand Down Expand Up @@ -93,3 +95,31 @@ def patch(self, request, *args, **kwargs):
def delete(self, request, *args, **kwargs):
"""delete method docstring"""
return super(SnippetDetail, self).patch(request, *args, **kwargs)


class SnippetViewerList(generics.ListAPIView):
"""SnippetViewerList classdoc"""
serializer_class = SnippetViewerSerializer
pagination_class = PageNumberPagination

parser_classes = (FormParser, CamelCaseJSONParser, FileUploadParser)
renderer_classes = (CamelCaseJSONRenderer,)
swagger_schema = CamelCaseOperationIDAutoSchema
lookup_url_kwarg = 'snippet_pk'

def get_object(self):
queryset = Snippet.objects.all()

# Perform the lookup filtering.
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field

filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
obj = get_object_or_404(queryset, **filter_kwargs)

# May raise a permission denied
self.check_object_permissions(self.request, obj)

return obj

def get_queryset(self):
return SnippetViewer.objects.filter(snippet=self.get_object())
56 changes: 56 additions & 0 deletions tests/reference.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,46 @@ paths:
tags:
- snippets
parameters: []
/snippets/views/{snippet_pk}/:
get:
operationId: snippetsViewsRead
description: SnippetViewerList classdoc
parameters:
- name: page
in: query
description: A page number within the paginated result set.
required: false
type: integer
responses:
'200':
description: ''
schema:
required:
- count
- results
type: object
properties:
count:
type: integer
next:
type: string
format: uri
x-nullable: true
previous:
type: string
format: uri
x-nullable: true
results:
type: array
items:
$ref: '#/definitions/SnippetViewer'
tags:
- snippets
parameters:
- name: snippet_pk
in: path
required: true
type: string
/snippets/{id}/:
get:
operationId: snippetsRead
Expand Down Expand Up @@ -1590,6 +1630,22 @@ definitions:
format: decimal
default: 0.0
minimum: 0.0
SnippetViewer:
required:
- snippet
- viewer
type: object
properties:
id:
title: ID
type: integer
readOnly: true
snippet:
title: Snippet
type: integer
viewer:
title: Viewer
type: integer
Todo:
required:
- title
Expand Down