From b39afec8eeed3cf505017a85512ae7e93135eb1f Mon Sep 17 00:00:00 2001 From: yelinz Date: Wed, 15 Nov 2023 16:06:30 +0100 Subject: [PATCH] feat: add visibiilities to relationships --- .github/workflows/tests.yml | 2 +- README.md | 30 ++++++++++++++++++++++--- generic_permissions/visibilities.py | 34 +++++++++++++++++++++++++++++ tests/migrations/0001_initial.py | 1 - tests/serializers.py | 5 +++++ tox.ini | 16 +++++++------- 6 files changed, 75 insertions(+), 13 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 76d6938..cf6f652 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - python: [3.6, 3.7, 3.8, 3.9.0-rc - 3.9] + python: [3.7, 3.8, 3.9, 3.10] steps: - uses: actions/checkout@v2 - name: Setup Python diff --git a/README.md b/README.md index e057ccf..9d89060 100644 --- a/README.md +++ b/README.md @@ -69,10 +69,34 @@ For the visibilities, extend your DRF `ViewSet` classes with the from rest_framework.viewsets import ModelViewSet from generic_permissions.visibilities import VisibilityViewMixin class MyModelViewset(VisibilityViewMixin, ModelViewSet): - serializer_class = ... + serializer_class = MyModelSerializers queryset = ... ``` +Make sure to use a subclassed `serializer_related_field` (such as the default `PrimariyKeyRelatedField`) +with the provided `VisibilityRelatedFieldMixin`. Otherwise relationships will leak hidden models. + +```python +# serializers.py +from rest_framework.viewsets import ModelSerializer +from generic_permissions.visibilities import VisibilityPrimaryKeyRelatedField +class MyModelSerializers(ModelSerializer): + serializer_related_field = VisibilityPrimaryKeyRelatedField +``` + +A few subclassed fields are provided: +- `VisibilityPrimaryKeyRelatedField` +- `VisibilityResourceRelatedField` +- `VisibilitySerializerMethodResourceRelatedField` + +If a different relation field variation is needed extend it with `VisibilityRelatedFieldMixin`: + +```python +from generic_permissions.visibilities import VisibilityRelatedFieldMixin +class CustomRelationField(VisibilityRelatedFieldMixin): + pass +``` + ### Permission subsystem Similarly, for the permissions system, add the `PermissionViewMixin` to your @@ -94,13 +118,13 @@ You may use only one of the two mixins, or both, depending on your needs. Last, for the validation system, you extend your **serializer** with a mixin: ```python # serializers.py -from rest_framework import serializers +from rest_framework.serializers import ModelSerializer from generic_permissions.serializers import PermissionSerializerMixin from generic_permissions.validation import ValidatorMixin from myapp import models -class MyModelSerializer(ValidatorMixin, serializers.ModelSerializer): +class MyModelSerializer(ValidatorMixin, ModelSerializer): # my field definitions... class Meta: model = models.MyModel diff --git a/generic_permissions/visibilities.py b/generic_permissions/visibilities.py index 3bc2faa..f891ea8 100644 --- a/generic_permissions/visibilities.py +++ b/generic_permissions/visibilities.py @@ -1,6 +1,12 @@ from functools import reduce from warnings import warn +from rest_framework.serializers import PrimaryKeyRelatedField +from rest_framework_json_api.relations import ( + ResourceRelatedField, + SerializerMethodResourceRelatedField, +) + from .config import DGAPConfigManager, VisibilitiesConfig """ @@ -44,6 +50,34 @@ def get_queryset(self): return queryset +class VisibilityRelatedFieldMixin: + def get_queryset(self): + queryset = super().get_queryset() + + for handler in VisibilitiesConfig.get_handlers(queryset.model): + queryset = handler( + queryset, self.get_parent_serializer()._context["request"] + ) + + return queryset + + +class VisibilityPrimaryKeyRelatedField( + PrimaryKeyRelatedField, VisibilityRelatedFieldMixin +): + pass + + +class VisibilityResourceRelatedField(ResourceRelatedField, VisibilityRelatedFieldMixin): + pass + + +class VisibilitySerializerMethodResourceRelatedField( + SerializerMethodResourceRelatedField, VisibilityRelatedFieldMixin +): + pass + + class BaseVisibility: # pragma: no cover def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/tests/migrations/0001_initial.py b/tests/migrations/0001_initial.py index f7b6f46..1b8be04 100644 --- a/tests/migrations/0001_initial.py +++ b/tests/migrations/0001_initial.py @@ -4,7 +4,6 @@ class Migration(migrations.Migration): - initial = True dependencies = [] diff --git a/tests/serializers.py b/tests/serializers.py index 52a5d07..0c25a10 100644 --- a/tests/serializers.py +++ b/tests/serializers.py @@ -1,17 +1,22 @@ from rest_framework import serializers from generic_permissions.validation import ValidatorMixin +from generic_permissions.visibilities import VisibilityPrimaryKeyRelatedField from . import models class TestModel1Serializer(ValidatorMixin, serializers.ModelSerializer): + serializer_related_field = VisibilityPrimaryKeyRelatedField + class Meta: model = models.Model1 fields = "__all__" class TestModel2Serializer(ValidatorMixin, serializers.ModelSerializer): + serializer_related_field = VisibilityPrimaryKeyRelatedField + class Meta: model = models.Model2 fields = "__all__" diff --git a/tox.ini b/tox.ini index 454e958..616510a 100644 --- a/tox.ini +++ b/tox.ini @@ -8,25 +8,25 @@ [tox] envlist = - {py36,py37,py38}-django22, - {py36,py37,py38}-django30, - {py36,py37,py38,py39}-django31, - {py38,py39}-latest, + {py37,py38,py39}-django31, + {py37,py38,py39}-django32, + {py38,py39,py3.10}-latest, flake8, black [latest] deps = https://github.com/django/django/archive/main.tar.gz https://github.com/encode/django-rest-framework/archive/master.tar.gz + https://github.com/django-json-api/django-rest-framework-json-api/archive/master.tar.gz [testenv] deps= - django22: django~=2.2.0 - django30: django~=3.0.0 django31: django~=3.1.0 - django22: djangorestframework~=3.11.0 - django30: djangorestframework~=3.11.0 + django32: django~=3.2.0 django31: djangorestframework~=3.11.0 + django32: djangorestframework~=3.14.0 + django31: djangorestframework-jsonapi~=6.1.0 + django32: djangorestframework-jsonapi~=6.1.0 latest: {[latest]deps} pytest pytest-cov