From c0773d63119a19a87fe3986edf90b623d7aca0e7 Mon Sep 17 00:00:00 2001 From: Mike Schem Date: Mon, 24 Apr 2023 10:59:48 -0700 Subject: [PATCH 1/8] Add python 3.10 and 3.11 to test matrix --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index d7c4f5f..8ac13fb 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -10,7 +10,7 @@ jobs: strategy: max-parallel: 4 matrix: - python-version: [ 3.7, 3.8, 3.9 ] + python-version: [ 3.7, 3.8, 3.9, 3.10, 3.11 ] steps: - uses: actions/checkout@v3 From 5ac9821cb8a8c412081ccf7f79b1b0236e79a947 Mon Sep 17 00:00:00 2001 From: Mike Schem Date: Mon, 24 Apr 2023 11:00:36 -0700 Subject: [PATCH 2/8] Update test.yaml --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 8ac13fb..b2a9db0 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -10,7 +10,7 @@ jobs: strategy: max-parallel: 4 matrix: - python-version: [ 3.7, 3.8, 3.9, 3.10, 3.11 ] + python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11" ] steps: - uses: actions/checkout@v3 From 4c28febb24b9ddfbbfbd6c389e19a8db6832ae21 Mon Sep 17 00:00:00 2001 From: Mike Schem Date: Mon, 24 Apr 2023 11:02:51 -0700 Subject: [PATCH 3/8] Update requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 73853c2..b6b3949 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,5 +15,5 @@ pyflakes==2.1.1 pytz==2019.1 sqlparse==0.3.0 toml==0.10.0 -typed-ast==1.4.3 +typed-ast==1.5.4 typing-extensions==3.10.0.0 From ea7d028e834e55adad2e29774cf3ab6a7f9bee1f Mon Sep 17 00:00:00 2001 From: Mike Schem Date: Mon, 24 Apr 2023 11:15:12 -0700 Subject: [PATCH 4/8] Update requirements.txt --- requirements.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index b6b3949..2089838 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,12 +8,9 @@ djangorestframework==3.12.1 entrypoints==0.3 flake8==3.7.7 mccabe==0.6.1 -mypy==0.910 -mypy-extensions==0.4.3 pycodestyle==2.5.0 pyflakes==2.1.1 pytz==2019.1 sqlparse==0.3.0 toml==0.10.0 -typed-ast==1.5.4 typing-extensions==3.10.0.0 From 9b3201a9e45ccad34b6fbaac90efeaa1281e165d Mon Sep 17 00:00:00 2001 From: mschem Date: Tue, 25 Apr 2023 18:40:35 -0700 Subject: [PATCH 5/8] show nested expanders in swagger --- rest_flex_fields/filter_backends.py | 15 ++++++++++++++- rest_flex_fields/utils.py | 12 ++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/rest_flex_fields/filter_backends.py b/rest_flex_fields/filter_backends.py index 63e3402..8b12d15 100644 --- a/rest_flex_fields/filter_backends.py +++ b/rest_flex_fields/filter_backends.py @@ -1,3 +1,5 @@ +import itertools +from collections.abc import Sequence from functools import lru_cache from typing import Optional @@ -9,6 +11,7 @@ from rest_framework.request import Request from rest_framework.viewsets import GenericViewSet +from rest_flex_fields.utils import get_model_from_dot_path from rest_flex_fields import ( FIELDS_PARAM, EXPAND_PARAM, @@ -47,10 +50,20 @@ def _get_expandable_fields(serializer_class: FlexFieldsModelSerializer) -> list: expand_list = [] while expandable_fields: key, cls = expandable_fields.pop() - cls = cls[0] if hasattr(cls, '__iter__') else cls + cls = cls[0] if isinstance(cls, Sequence) else cls + try: + cls = get_model_from_dot_path(cls) if isinstance(cls, str) else cls + except (ValueError, AttributeError): + pass + + skip_next_level = key.rsplit('.', 1)[-1] in list(itertools.chain.from_iterable([i.split('.') for i in expand_list])) expand_list.append(key) + # Skip node, already visited + if skip_next_level: + continue + if hasattr(cls, "Meta") and issubclass(cls, FlexFieldsSerializerMixin) and hasattr(cls.Meta, "expandable_fields"): next_layer = getattr(cls.Meta, 'expandable_fields') expandable_fields.extend([(f"{key}.{k}", cls) for k, cls in list(next_layer.items())]) diff --git a/rest_flex_fields/utils.py b/rest_flex_fields/utils.py index 3f2c49a..d7e75ae 100644 --- a/rest_flex_fields/utils.py +++ b/rest_flex_fields/utils.py @@ -1,4 +1,7 @@ +import logging +import importlib from collections.abc import Iterable +from django.db.models import Model from rest_flex_fields import EXPAND_PARAM, FIELDS_PARAM, OMIT_PARAM, WILDCARD_VALUES @@ -72,3 +75,12 @@ def split_levels(fields): first_level_fields = list(set(first_level_fields)) return first_level_fields, next_level_fields + + +def get_model_from_dot_path(dot_path: str) -> Model | None: + """Given a dot path such as 'testapp.models.Person', return the model class.""" + module_path, attribute_name = dot_path.rsplit('.', 1) + module = importlib.import_module(module_path) + model = getattr(module, attribute_name) + + return model \ No newline at end of file From 1d5b38ed0363528a1cee21c834a3d6fe4984a824 Mon Sep 17 00:00:00 2001 From: mschem Date: Tue, 25 Apr 2023 18:44:23 -0700 Subject: [PATCH 6/8] run all python versions --- .github/workflows/test.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index b2a9db0..91097dc 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -9,6 +9,7 @@ jobs: runs-on: ubuntu-latest strategy: max-parallel: 4 + fail-fast: false matrix: python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11" ] From 33978ebb1739d2e4ef2329d37e42529941bd9e89 Mon Sep 17 00:00:00 2001 From: mschem Date: Tue, 25 Apr 2023 18:47:02 -0700 Subject: [PATCH 7/8] python < 3.8 compatible --- .github/workflows/test.yaml | 2 +- rest_flex_fields/utils.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 91097dc..260c068 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -8,7 +8,7 @@ jobs: Test: runs-on: ubuntu-latest strategy: - max-parallel: 4 + max-parallel: 5 fail-fast: false matrix: python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11" ] diff --git a/rest_flex_fields/utils.py b/rest_flex_fields/utils.py index d7e75ae..c4af147 100644 --- a/rest_flex_fields/utils.py +++ b/rest_flex_fields/utils.py @@ -2,6 +2,7 @@ import importlib from collections.abc import Iterable from django.db.models import Model +from typing import Optional from rest_flex_fields import EXPAND_PARAM, FIELDS_PARAM, OMIT_PARAM, WILDCARD_VALUES @@ -77,7 +78,7 @@ def split_levels(fields): return first_level_fields, next_level_fields -def get_model_from_dot_path(dot_path: str) -> Model | None: +def get_model_from_dot_path(dot_path: str) -> Optional[Model]: """Given a dot path such as 'testapp.models.Person', return the model class.""" module_path, attribute_name = dot_path.rsplit('.', 1) module = importlib.import_module(module_path) From c36923253997fd98deb1f0c07216993f280263df Mon Sep 17 00:00:00 2001 From: mschem Date: Wed, 26 Apr 2023 10:27:14 -0700 Subject: [PATCH 8/8] add prefetch ignores --- rest_flex_fields/filter_backends.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/rest_flex_fields/filter_backends.py b/rest_flex_fields/filter_backends.py index 8b12d15..ca4fd93 100644 --- a/rest_flex_fields/filter_backends.py +++ b/rest_flex_fields/filter_backends.py @@ -240,13 +240,19 @@ def filter_queryset( queryset = queryset.prefetch_related(*( model_field.name for model_field in nested_model_fields if - (model_field.is_relation and not model_field.many_to_one) or - (model_field.is_relation and model_field.many_to_one and not model_field.concrete) # Include GenericForeignKey) + ( + (model_field.is_relation and not model_field.many_to_one) or + (model_field.is_relation and model_field.many_to_one and not model_field.concrete) # Include GenericForeignKey) + ) and + (model_field.name not in self._get_prefetches_ignores()) ) ) return queryset + def _get_prefetches_ignores(self): + return [] + @staticmethod @lru_cache() def _get_field(field_name: str, model: models.Model) -> Optional[models.Field]: