From 59bdea45d16a945874b8cce827279973ed674812 Mon Sep 17 00:00:00 2001 From: luto Date: Wed, 10 Jan 2024 10:57:20 +0100 Subject: [PATCH 01/10] fix URLs in README example --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 59c5880c..df28aa0c 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,10 @@ It is not needed to add this library in your Django project's `settings.py` file The desired URL signatures are: ``` -/domain/ <- Domains list -/domain/{pk}/ <- One domain, from {pk} -/domain/{domain_pk}/nameservers/ <- Nameservers of domain from {domain_pk} -/domain/{domain_pk}/nameservers/{pk} <- Specific nameserver from {pk}, of domain from {domain_pk} +/domains/ <- Domains list +/domains/{pk}/ <- One domain, from {pk} +/domains/{domain_pk}/nameservers/ <- Nameservers of domain from {domain_pk} +/domains/{domain_pk}/nameservers/{pk} <- Specific nameserver from {pk}, of domain from {domain_pk} ``` How to do it (example): From 6ea0395d756f04ea7116f21c6e69fbc9538c51cf Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Wed, 10 Jan 2024 12:32:45 +0000 Subject: [PATCH 02/10] chore: remove Travis config and old README.rst --- .travis.yml | 26 -------------------------- README.rst | 19 ------------------- 2 files changed, 45 deletions(-) delete mode 100644 .travis.yml delete mode 100644 README.rst diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ae01bf9a..00000000 --- a/.travis.yml +++ /dev/null @@ -1,26 +0,0 @@ -language: python -python: - - "3.8" - - "3.9" - - "3.10" - - "3.11" - -install: pip install tox-travis -script: tox - -cache: pip - -matrix: - fast_finish: true - include: - - dist: jammy - python: "3.11" - -notifications: - webhooks: - urls: - - https://webhooks.gitter.im/e/09bd4c7cc8961aff307f - on_success: change # options: [always|never|change] default: always - on_failure: always # options: [always|never|change] default: always - on_start: never # options: [always|never|change] default: always - diff --git a/README.rst b/README.rst deleted file mode 100644 index 2f074e97..00000000 --- a/README.rst +++ /dev/null @@ -1,19 +0,0 @@ -drf-nested-routers -====================================== - -|build-status-image| |pypi-version| - -Overview --------- - -Nested resources for the Django Rest Framework - -Documentation -------------- - -Please see the README on the Github repo page: ``_ - -.. |build-status-image| image:: https://secure.travis-ci.org/alanjds/drf-nested-routers.svg?branch=master - :target: http://travis-ci.org/alanjds/drf-nested-routers?branch=master -.. |pypi-version| image:: https://img.shields.io/pypi/v/drf-nested-routers.svg - :target: https://pypi.python.org/pypi/drf-nested-routers From ff58fb632f0a0f5008aeca1bf24d3965f127205c Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Wed, 10 Jan 2024 13:22:26 +0000 Subject: [PATCH 03/10] docs: update CI badge in docs homepage --- docs/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/index.md b/docs/index.md index 25cbbbb7..290f8875 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,6 @@
- - + + From 641c3951e43a27e9585ca3386c7f347dc81282e4 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Tue, 30 Jan 2024 10:45:52 -0300 Subject: [PATCH 04/10] Update pytest-django from 4.5.2 to 4.8.0 --- requirements-tox.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-tox.txt b/requirements-tox.txt index b16342f8..f8a6c2a0 100644 --- a/requirements-tox.txt +++ b/requirements-tox.txt @@ -1,7 +1,7 @@ # Test requirements pytest==7.4.2 pytest-cov==4.1.0 -pytest-django==4.5.2 +pytest-django==4.8.0 flake8==6.1.0 ipdb==0.13.13 pytz==2023.3 From 1ba211a56320b684cbec5b480a4ace8db9d1ad45 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Thu, 1 Feb 2024 23:01:33 -0300 Subject: [PATCH 05/10] Update pytz from 2023.3 to 2024.1 --- requirements-tox.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-tox.txt b/requirements-tox.txt index f8a6c2a0..9df6f1ad 100644 --- a/requirements-tox.txt +++ b/requirements-tox.txt @@ -4,7 +4,7 @@ pytest-cov==4.1.0 pytest-django==4.8.0 flake8==6.1.0 ipdb==0.13.13 -pytz==2023.3 +pytz==2024.1 # wheel for PyPI installs wheel==0.42.0 From f06c5f66e6fb373306d967be74d2ec3208005152 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Mon, 11 Mar 2024 17:05:10 -0300 Subject: [PATCH 06/10] Update wheel from 0.42.0 to 0.43.0 --- requirements-tox.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-tox.txt b/requirements-tox.txt index f8a6c2a0..dc4ad785 100644 --- a/requirements-tox.txt +++ b/requirements-tox.txt @@ -7,7 +7,7 @@ ipdb==0.13.13 pytz==2023.3 # wheel for PyPI installs -wheel==0.42.0 +wheel==0.43.0 # MkDocs for documentation previews/deploys mkdocs==1.5.2 From 45dde2339d62d380951816b9a52cf45938770077 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sun, 24 Mar 2024 19:08:51 -0300 Subject: [PATCH 07/10] Update pytest-cov from 4.1.0 to 5.0.0 --- requirements-tox.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-tox.txt b/requirements-tox.txt index f8a6c2a0..c79f0dc1 100644 --- a/requirements-tox.txt +++ b/requirements-tox.txt @@ -1,6 +1,6 @@ # Test requirements pytest==7.4.2 -pytest-cov==4.1.0 +pytest-cov==5.0.0 pytest-django==4.8.0 flake8==6.1.0 ipdb==0.13.13 From 7f248070d5be53f8b4acafda567ce594fd6a0817 Mon Sep 17 00:00:00 2001 From: Marti Raudsepp Date: Wed, 24 Apr 2024 19:43:04 +0300 Subject: [PATCH 08/10] Remove Python 2.x compatibility code --- rest_framework_nested/relations.py | 11 ++--------- rest_framework_nested/routers.py | 16 ++++++---------- rest_framework_nested/serializers.py | 4 ++-- rest_framework_nested/viewsets.py | 4 ++-- runtests.py | 4 +--- setup.py | 5 ++--- tests/description.py | 2 -- tests/models.py | 3 +-- tests/serializers/test_serializers.py | 2 +- tests/test_dummy.py | 6 ------ tests/test_dynamic_routers.py | 16 ++++++++-------- tests/test_routers.py | 26 +++++++++++++------------- 12 files changed, 38 insertions(+), 61 deletions(-) delete mode 100644 tests/test_dummy.py diff --git a/rest_framework_nested/relations.py b/rest_framework_nested/relations.py index 2be920fc..245785dd 100644 --- a/rest_framework_nested/relations.py +++ b/rest_framework_nested/relations.py @@ -4,19 +4,12 @@ These fields allow you to specify the style that should be used to represent model relationships with hyperlinks. """ -from __future__ import unicode_literals from functools import reduce # import reduce from functools for compatibility with python 3 import rest_framework.relations from rest_framework.relations import ObjectDoesNotExist, ObjectValueError, ObjectTypeError from rest_framework.exceptions import ValidationError -# fix for basestring -try: - basestring -except NameError: - basestring = str - class NestedHyperlinkedRelatedField(rest_framework.relations.HyperlinkedRelatedField): lookup_field = 'pk' @@ -26,7 +19,7 @@ class NestedHyperlinkedRelatedField(rest_framework.relations.HyperlinkedRelatedF def __init__(self, *args, **kwargs): self.parent_lookup_kwargs = kwargs.pop('parent_lookup_kwargs', self.parent_lookup_kwargs) - super(NestedHyperlinkedRelatedField, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def get_url(self, obj, view_name, request, format): """ @@ -103,4 +96,4 @@ def __init__(self, view_name=None, **kwargs): assert view_name is not None, 'The `view_name` argument is required.' kwargs['read_only'] = True kwargs['source'] = '*' - super(NestedHyperlinkedIdentityField, self).__init__(view_name=view_name, **kwargs) + super().__init__(view_name=view_name, **kwargs) diff --git a/rest_framework_nested/routers.py b/rest_framework_nested/routers.py index bcac8b50..106eb829 100644 --- a/rest_framework_nested/routers.py +++ b/rest_framework_nested/routers.py @@ -25,7 +25,6 @@ urlpatterns = router.urls """ -from __future__ import unicode_literals import sys import re from rest_framework.routers import SimpleRouter, DefaultRouter # noqa: F401 @@ -37,7 +36,7 @@ IDENTIFIER_REGEX = re.compile(r"^[^\d\W]\w*$", re.UNICODE) -class LookupMixin(object): +class LookupMixin: """ Deprecated. @@ -45,14 +44,14 @@ class LookupMixin(object): """ -class NestedMixin(object): +class NestedMixin: def __init__(self, parent_router, parent_prefix, *args, **kwargs): self.parent_router = parent_router self.parent_prefix = parent_prefix self.nest_count = getattr(parent_router, 'nest_count', 0) + 1 - self.nest_prefix = kwargs.pop('lookup', 'nested_%i' % self.nest_count) + '_' + self.nest_prefix = kwargs.pop('lookup', f'nested_{self.nest_count}') + '_' - super(NestedMixin, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) if 'trailing_slash' not in kwargs: # Inherit trailing_slash only when not specified explicitly. @@ -84,10 +83,7 @@ def __init__(self, parent_router, parent_prefix, *args, **kwargs): nested_routes = [] parent_lookup_regex = parent_router.get_lookup_regex(parent_viewset, self.nest_prefix) - self.parent_regex = '{parent_prefix}/{parent_lookup_regex}/'.format( - parent_prefix=parent_prefix, - parent_lookup_regex=parent_lookup_regex - ) + self.parent_regex = f'{parent_prefix}/{parent_lookup_regex}/' # If there is no parent prefix, the first part of the url is probably # controlled by the project's urls.py and the router is in an app, # so a slash in the beginning will (A) cause Django to give warnings @@ -111,7 +107,7 @@ def __init__(self, parent_router, parent_prefix, *args, **kwargs): def check_valid_name(self, value): if IDENTIFIER_REGEX.match(value) is None: - raise ValueError("lookup argument '{}' needs to be valid python identifier".format(value)) + raise ValueError(f"lookup argument '{value}' needs to be valid python identifier") class NestedSimpleRouter(NestedMixin, SimpleRouter): diff --git a/rest_framework_nested/serializers.py b/rest_framework_nested/serializers.py index 38c50a80..1a315a2d 100644 --- a/rest_framework_nested/serializers.py +++ b/rest_framework_nested/serializers.py @@ -27,10 +27,10 @@ class NestedHyperlinkedModelSerializer(rest_framework.serializers.HyperlinkedMod def __init__(self, *args, **kwargs): self.parent_lookup_kwargs = kwargs.pop('parent_lookup_kwargs', self.parent_lookup_kwargs) - super(NestedHyperlinkedModelSerializer, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def build_url_field(self, field_name, model_class): - field_class, field_kwargs = super(NestedHyperlinkedModelSerializer, self).build_url_field( + field_class, field_kwargs = super().build_url_field( field_name, model_class ) diff --git a/rest_framework_nested/viewsets.py b/rest_framework_nested/viewsets.py index 9d3b1dcc..41e9b27a 100644 --- a/rest_framework_nested/viewsets.py +++ b/rest_framework_nested/viewsets.py @@ -17,7 +17,7 @@ def _force_mutable(querydict: dict) -> dict: querydict._mutable = initial_mutability -class NestedViewSetMixin(object): +class NestedViewSetMixin: def _get_parent_lookup_kwargs(self) -> dict: """ Locates and returns the `parent_lookup_kwargs` dict informing @@ -44,7 +44,7 @@ def get_queryset(self): Filter the `QuerySet` based on its parents as defined in the `serializer_class.parent_lookup_kwargs` or `viewset.parent_lookup_kwargs` """ - queryset = super(NestedViewSetMixin, self).get_queryset() + queryset = super().get_queryset() if getattr(self, 'swagger_fake_view', False): return queryset diff --git a/runtests.py b/runtests.py index 36ef9a86..f130bfab 100755 --- a/runtests.py +++ b/runtests.py @@ -1,6 +1,4 @@ #! /usr/bin/env python -from __future__ import print_function - import pytest import sys import os @@ -32,7 +30,7 @@ def flake8_main(args): def split_class_and_function(string): class_string, function_string = string.split('.', 1) - return "%s and %s" % (class_string, function_string) + return f"{class_string} and {function_string}" def is_function(string): diff --git a/setup.py b/setup.py index a5106c70..20f384a2 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import re import os import sys @@ -59,8 +58,8 @@ def get_package_data(package): os.system("python setup.py sdist bdist_wheel") os.system("twine upload dist/*") print("You probably want to also tag the version now:") - print(" git tag -a v{0} -m 'version {0}'".format(version)) - print(" git push origin v{0}".format(version)) + print(f" git tag -a v{version} -m 'version {version}'") + print(f" git push origin v{version}") sys.exit() diff --git a/tests/description.py b/tests/description.py index b46d7f54..62c979dd 100644 --- a/tests/description.py +++ b/tests/description.py @@ -1,5 +1,3 @@ -# -- coding: utf-8 -- - # Apparently there is a python 2.6 issue where docstrings of imported view classes # do not retain their encoding information even if a module has a proper # encoding declaration at the top of its source file. Therefore for tests diff --git a/tests/models.py b/tests/models.py index 29f4041f..8409ac58 100644 --- a/tests/models.py +++ b/tests/models.py @@ -1,4 +1,3 @@ -from __future__ import unicode_literals from django.db import models from rest_framework import serializers @@ -11,7 +10,7 @@ class CustomField(models.CharField): def __init__(self, *args, **kwargs): kwargs['max_length'] = 12 - super(CustomField, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) class RESTFrameworkModel(models.Model): diff --git a/tests/serializers/test_serializers.py b/tests/serializers/test_serializers.py index f822b7b3..9ed7b449 100644 --- a/tests/serializers/test_serializers.py +++ b/tests/serializers/test_serializers.py @@ -44,7 +44,7 @@ def setUpClass(cls): GrandChild1.objects.create(parent=child2, name='Child2-GrandChild1-C') Parent.objects.create(name='Parent2') - return super(TestSerializers, cls).setUpClass() + return super().setUpClass() def test_default(self): url = reverse('parent1-detail', kwargs={'pk': 1}) diff --git a/tests/test_dummy.py b/tests/test_dummy.py deleted file mode 100644 index 9f77d41b..00000000 --- a/tests/test_dummy.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.test import TestCase - - -class TestDummy(TestCase): - def test_one_plus_one(self): - assert 1 + 1 == 2 diff --git a/tests/test_dynamic_routers.py b/tests/test_dynamic_routers.py index 72b41d1d..b248b1ec 100644 --- a/tests/test_dynamic_routers.py +++ b/tests/test_dynamic_routers.py @@ -82,41 +82,41 @@ def test_dynamic_routes(self): self.assertFalse(hasattr(self.router, 'parent_regex')) urls = map_by_name(self.router.urls) self.assertEqual( - get_regex_pattern(urls['basicmodel-list']), u'^detail/$' + get_regex_pattern(urls['basicmodel-list']), '^detail/$' ) self.assertEqual( get_regex_pattern(urls['basicmodel-detail']), - u'^detail/(?P[^/.]+)/$' + '^detail/(?P[^/.]+)/$' ) self.assertEqual( get_regex_pattern(urls['basicmodel-set-password']), - u'^detail/(?P[^/.]+)/set_password/$' + '^detail/(?P[^/.]+)/set_password/$' ) def test_nested_parent(self): self.assertEqual( self.detail_router.parent_regex, - u'detail/(?P[^/.]+)/' + 'detail/(?P[^/.]+)/' ) urls = map_by_name(self.detail_router.urls) self.assertEqual( get_regex_pattern(urls['basicmodel-list']), - u'^detail/(?P[^/.]+)/list/$' + '^detail/(?P[^/.]+)/list/$' ) self.assertEqual( get_regex_pattern(urls['basicmodel-recent-users']), - u'^detail/(?P[^/.]+)/list/recent_users/$' + '^detail/(?P[^/.]+)/list/recent_users/$' ) self.assertEqual( get_regex_pattern(urls['basicmodel-detail']), - u'^detail/(?P[^/.]+)/list/(?P[^/.]+)/$' + '^detail/(?P[^/.]+)/list/(?P[^/.]+)/$' ) def test_nested_child(self): self.assertEqual( self.list_router.parent_regex, - u'detail/(?P[^/.]+)/list/(?P[^/.]+)/' + 'detail/(?P[^/.]+)/list/(?P[^/.]+)/' ) diff --git a/tests/test_routers.py b/tests/test_routers.py index 179e400d..51ba983a 100644 --- a/tests/test_routers.py +++ b/tests/test_routers.py @@ -74,20 +74,20 @@ def test_recursive_nested_simple_routers(self): self.assertFalse(hasattr(self.router, 'parent_regex')) urls = self.router.urls self.assertEqual(len(urls), 2) - self.assertEqual(get_regex_pattern(urls[0]), u'^a/$') - self.assertEqual(get_regex_pattern(urls[1]), u'^a/(?P[0-9a-f]{32})/$') + self.assertEqual(get_regex_pattern(urls[0]), '^a/$') + self.assertEqual(get_regex_pattern(urls[1]), '^a/(?P[0-9a-f]{32})/$') - self.assertEqual(self.a_router.parent_regex, u'a/(?P[0-9a-f]{32})/') + self.assertEqual(self.a_router.parent_regex, 'a/(?P[0-9a-f]{32})/') urls = self.a_router.urls self.assertEqual(len(urls), 2) - self.assertEqual(get_regex_pattern(urls[0]), u'^a/(?P[0-9a-f]{32})/b/$') - self.assertEqual(get_regex_pattern(urls[1]), u'^a/(?P[0-9a-f]{32})/b/(?P[^/.]+)/$') + self.assertEqual(get_regex_pattern(urls[0]), '^a/(?P[0-9a-f]{32})/b/$') + self.assertEqual(get_regex_pattern(urls[1]), '^a/(?P[0-9a-f]{32})/b/(?P[^/.]+)/$') - self.assertEqual(self.b_router.parent_regex, u'a/(?P[0-9a-f]{32})/b/(?P[^/.]+)/') + self.assertEqual(self.b_router.parent_regex, 'a/(?P[0-9a-f]{32})/b/(?P[^/.]+)/') urls = self.b_router.urls self.assertEqual(len(urls), 2) - self.assertEqual(get_regex_pattern(urls[0]), u'^a/(?P[0-9a-f]{32})/b/(?P[^/.]+)/c/$') - self.assertEqual(get_regex_pattern(urls[1]), u'^a/(?P[0-9a-f]{32})/b/(?P[^/.]+)/c/(?P[^/.]+)/$') + self.assertEqual(get_regex_pattern(urls[0]), '^a/(?P[0-9a-f]{32})/b/(?P[^/.]+)/c/$') + self.assertEqual(get_regex_pattern(urls[1]), '^a/(?P[0-9a-f]{32})/b/(?P[^/.]+)/c/(?P[^/.]+)/$') class TestEmptyPrefix(TestCase): @@ -101,8 +101,8 @@ def test_empty_prefix(self): urls = self.router.urls urls = self.a_router.urls self.assertEqual(len(urls), 2) - self.assertEqual(get_regex_pattern(urls[0]), u'^(?P[0-9a-f]{32})/b/$') - self.assertEqual(get_regex_pattern(urls[1]), u'^(?P[0-9a-f]{32})/b/(?P[^/.]+)/$') + self.assertEqual(get_regex_pattern(urls[0]), '^(?P[0-9a-f]{32})/b/$') + self.assertEqual(get_regex_pattern(urls[1]), '^(?P[0-9a-f]{32})/b/(?P[^/.]+)/$') class TestBadLookupValue(TestCase): @@ -134,12 +134,12 @@ class TestRouterSettingInheritance(TestCase): """ def _assertHasTrailingSlash(self, router): - self.assertEqual(router.trailing_slash, u'/', "router does not have trailing slash when it should") + self.assertEqual(router.trailing_slash, '/', "router does not have trailing slash when it should") self.assertTrue(pattern_from_url(router.urls[0]).endswith('/$'), "router created url without trailing slash when it should have") def _assertDoesNotHaveTrailingSlash(self, router): - self.assertEqual(router.trailing_slash, u'', "router has trailing slash when it should not") + self.assertEqual(router.trailing_slash, '', "router has trailing slash when it should not") self.assertFalse(pattern_from_url(router.urls[0]).endswith('/$'), "router created url with trailing slash when it should not have") @@ -198,6 +198,6 @@ def test_inherits_nonstandard_trailing_slash(self): a_router = NestedSimpleRouter(router, 'a', lookup='a') a_router.register('b', BViewSet) - self.assertEqual(a_router.trailing_slash, u'/?', "router does not have trailing slash when it should") + self.assertEqual(a_router.trailing_slash, '/?', "router does not have trailing slash when it should") self.assertTrue(pattern_from_url(a_router.urls[0]).endswith('/?$'), "router created url without trailing slash when it should have") From 00372e70ce45bb9c83a1fe0e68ccb5b6180058b6 Mon Sep 17 00:00:00 2001 From: pyup-bot Date: Sat, 27 Apr 2024 23:19:52 -0300 Subject: [PATCH 09/10] Update pytest from 7.4.2 to 8.2.0 --- requirements-tox.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-tox.txt b/requirements-tox.txt index 1d18f524..7d18d19d 100644 --- a/requirements-tox.txt +++ b/requirements-tox.txt @@ -1,5 +1,5 @@ # Test requirements -pytest==7.4.2 +pytest==8.2.0 pytest-cov==5.0.0 pytest-django==4.8.0 flake8==6.1.0 From b1ab349108ec26e0a64fa2051fe7839345278608 Mon Sep 17 00:00:00 2001 From: Marti Raudsepp Date: Thu, 9 May 2024 12:30:46 +0300 Subject: [PATCH 10/10] Remove obsolete comments --- rest_framework_nested/relations.py | 2 +- tests/description.py | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/rest_framework_nested/relations.py b/rest_framework_nested/relations.py index 245785dd..3d684f00 100644 --- a/rest_framework_nested/relations.py +++ b/rest_framework_nested/relations.py @@ -4,7 +4,7 @@ These fields allow you to specify the style that should be used to represent model relationships with hyperlinks. """ -from functools import reduce # import reduce from functools for compatibility with python 3 +from functools import reduce import rest_framework.relations from rest_framework.relations import ObjectDoesNotExist, ObjectValueError, ObjectTypeError diff --git a/tests/description.py b/tests/description.py index 62c979dd..4265e496 100644 --- a/tests/description.py +++ b/tests/description.py @@ -1,9 +1,3 @@ -# Apparently there is a python 2.6 issue where docstrings of imported view classes -# do not retain their encoding information even if a module has a proper -# encoding declaration at the top of its source file. Therefore for tests -# to catch unicode related errors, a mock view has to be declared in a separate -# module. - from rest_framework.views import APIView