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 addedmaster
parent
81f0b1a2ea
commit
86c1675c58
|
|
@ -6,7 +6,7 @@ from collections import OrderedDict
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from rest_framework import serializers, status
|
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.parsers import FileUploadParser
|
||||||
from rest_framework.request import is_form_media_type
|
from rest_framework.request import is_form_media_type
|
||||||
from rest_framework.settings import api_settings as rest_framework_settings
|
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
|
# a detail action is surely not a list route
|
||||||
return False
|
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
|
# for GenericAPIView, if it's a detail view it can't also be a list view
|
||||||
if isinstance(view, (RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin)):
|
if isinstance(view, (RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin)):
|
||||||
return False
|
return False
|
||||||
|
|
|
||||||
|
|
@ -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)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -18,3 +18,8 @@ class Snippet(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('created',)
|
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)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from django.contrib.auth import get_user_model
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework.compat import MaxLengthValidator, MinValueValidator
|
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):
|
class LanguageSerializer(serializers.Serializer):
|
||||||
|
|
@ -100,3 +100,9 @@ class SnippetSerializer(serializers.Serializer):
|
||||||
instance.style = validated_data.get('style', instance.style)
|
instance.style = validated_data.get('style', instance.style)
|
||||||
instance.save()
|
instance.save()
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class SnippetViewerSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = SnippetViewer
|
||||||
|
fields = '__all__'
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,13 @@ if django.VERSION[:2] >= (2, 0):
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', views.SnippetList.as_view()),
|
path('', views.SnippetList.as_view()),
|
||||||
path('<int:pk>/', views.SnippetDetail.as_view()),
|
path('<int:pk>/', views.SnippetDetail.as_view()),
|
||||||
|
path('views/<int:snippet_pk>/', views.SnippetViewerList.as_view()),
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url('^$', views.SnippetList.as_view()),
|
url('^$', views.SnippetList.as_view()),
|
||||||
url(r'^(?P<pk>\d+)/$', views.SnippetDetail.as_view()),
|
url(r'^(?P<pk>\d+)/$', views.SnippetDetail.as_view()),
|
||||||
|
url(r'^views/(?P<snippet_pk>\d+)/$', views.SnippetViewerList.as_view()),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,15 @@ from djangorestframework_camel_case.parser import CamelCaseJSONParser
|
||||||
from djangorestframework_camel_case.render import CamelCaseJSONRenderer
|
from djangorestframework_camel_case.render import CamelCaseJSONRenderer
|
||||||
from inflection import camelize
|
from inflection import camelize
|
||||||
from rest_framework import generics, status
|
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 rest_framework.parsers import FormParser, FileUploadParser
|
||||||
|
|
||||||
from drf_yasg import openapi
|
from drf_yasg import openapi
|
||||||
from drf_yasg.inspectors import SwaggerAutoSchema
|
from drf_yasg.inspectors import SwaggerAutoSchema
|
||||||
from drf_yasg.utils import swagger_auto_schema
|
from drf_yasg.utils import swagger_auto_schema
|
||||||
from snippets.models import Snippet
|
from snippets.models import Snippet, SnippetViewer
|
||||||
from snippets.serializers import SnippetSerializer
|
from snippets.serializers import SnippetSerializer, SnippetViewerSerializer
|
||||||
|
|
||||||
|
|
||||||
class CamelCaseOperationIDAutoSchema(SwaggerAutoSchema):
|
class CamelCaseOperationIDAutoSchema(SwaggerAutoSchema):
|
||||||
|
|
@ -93,3 +95,31 @@ class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||||
def delete(self, request, *args, **kwargs):
|
def delete(self, request, *args, **kwargs):
|
||||||
"""delete method docstring"""
|
"""delete method docstring"""
|
||||||
return super(SnippetDetail, self).patch(request, *args, **kwargs)
|
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())
|
||||||
|
|
|
||||||
|
|
@ -440,6 +440,46 @@ paths:
|
||||||
tags:
|
tags:
|
||||||
- snippets
|
- snippets
|
||||||
parameters: []
|
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}/:
|
/snippets/{id}/:
|
||||||
get:
|
get:
|
||||||
operationId: snippetsRead
|
operationId: snippetsRead
|
||||||
|
|
@ -1590,6 +1630,22 @@ definitions:
|
||||||
format: decimal
|
format: decimal
|
||||||
default: 0.0
|
default: 0.0
|
||||||
minimum: 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:
|
Todo:
|
||||||
required:
|
required:
|
||||||
- title
|
- title
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue