make sure all ListModelMixin views considered as list view (#306)

* make sure all ListModelMixin views considered as list view
* test to make sure all ListModelMixin views considered as list view added
master
Dimas Ari 2019-04-01 07:57:14 +08:00 committed by Cristi Vîjdea
parent 81f0b1a2ea
commit 86c1675c58
7 changed files with 132 additions and 4 deletions

View File

@ -6,7 +6,7 @@ from collections import OrderedDict
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
@ -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

View File

@ -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)),
],
),
]

View File

@ -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)

View File

@ -4,7 +4,7 @@ from django.contrib.auth import get_user_model
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):
@ -100,3 +100,9 @@ class SnippetSerializer(serializers.Serializer):
instance.style = validated_data.get('style', instance.style)
instance.save()
return instance
class SnippetViewerSerializer(serializers.ModelSerializer):
class Meta:
model = SnippetViewer
fields = '__all__'

View File

@ -8,10 +8,13 @@ if django.VERSION[:2] >= (2, 0):
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()),
]

View File

@ -2,13 +2,15 @@ from djangorestframework_camel_case.parser import CamelCaseJSONParser
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):
@ -93,3 +95,31 @@ class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
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())

View File

@ -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
@ -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