Coder Social home page Coder Social logo

rsinger86 / drf-flex-fields Goto Github PK

View Code? Open in Web Editor NEW
731.0 12.0 61.0 243 KB

Dynamically set fields and expand nested resources in Django REST Framework serializers.

License: MIT License

Python 100.00%
django-rest-framework field-expansion django

drf-flex-fields's People

Contributors

adr-007 avatar allanlewis avatar andruten avatar crocmagnon avatar dependabot[bot] avatar erielias avatar fladi avatar fvgoto avatar giovannicimolin avatar hemache avatar jaspersui avatar jsatt avatar lukasberka avatar michaelschem avatar mikeifts avatar nikeshyad avatar niyaznz avatar oliwarner avatar pablolmedorado avatar rjevski avatar rsinger86 avatar sentyaev avatar soerenbe avatar soroush-tabesh avatar tanoabeleyra avatar xjlin0 avatar zakjholt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

drf-flex-fields's Issues

Better notation for deferred fields?

Hello and hope you're well!

I wanted to raise a discussion on how deferred fields are currently defined and whether a less verbose approach could be supported?

At the moment my understanding is that expandable fields explicitly need to have their serializer (or field type) defined. This is fine for "true" expands (that warrant a separate serializer) but becomes unnecessarily verbose for fields on the same model - those only defined in fields and the ModelSerializer infers the actual field types at runtime from the model.

Given this serializer:

class MySerializer(serializers.ModelSerializer):
    class Meta:
        model = MyModel
        fields = ("id", "name", "description", "etc")

Let's say I wanted to have description and etc deferred - not rendered by default unless requested, currently I'd have to do this:

class MySerializer(serializers.ModelSerializer):
    class Meta:
        model = MyModel
        fields = ("id", "name")
        expandable_fields = {"description" serializers.CharField, "etc": serializers.CharField}

This requires explicitly listing out field classes for every field, a pretty tedious process.

In the codebase I'm currently working on we worked around this as follows:

class CustomFlexFieldsSerializerMixin(FlexFieldsSerializerMixin):
    """
    Overriding the FlexFieldsSerializerMixin to enable declaring of "default_fields"
    in the Serializer.Meta.
    This is a list of fields to be shown if no "fields" parameter is present.

    class Meta:
        default_fields = ["id", "name"]
    """

    def __init__(self, *args, **kwargs):
        """Set fields from Meta.default_fields if not provided in the parameters"""
        if (
            kwargs.get("context")
            and not kwargs["context"]["request"].query_params.getlist(FIELDS_PARAM)
            and not kwargs["context"]["request"].query_params.getlist(OMIT_PARAM)
        ):
            super().__init__(*args, **kwargs, fields=self._default_fields)
        else:
            super().__init__(*args, **kwargs)

    @property
    def _default_fields(self) -> dict:
        if hasattr(self, "Meta") and hasattr(self.Meta, "default_fields"):
            return self.Meta.default_fields
        return {}

Essentially the above approach sets the fields argument to Meta.default_fields (unless it's explicitly set within the context from the originating request) as if they were explicitly requested via the query string - this allows you to have deferrable fields with minimal changes to the serializer - just set default_fields and you're good to go.

We had a TODO in there to upstream this so I wanted to raise this discussion to see if there's a way we can merge our approaches so our custom override above is no longer required.

Support for expandable `SerializerMethodFields`

Hello, I have a few large SerializerMethodFields on my serializer, which return iterables that are not a FK to my model. Something like this:

Class FooSerializer(FlexFieldsModelSerializer):
    things = SerializerMethodField()

    def get_things(self, instance):
        return ThingSerializer(get_things_related_to_this_instance(instance), many=True).data

As far as I know I cannot directly leverage drf-flex-fields to make this field expandable, because expandable_fields are a statically defined dict that takes a serializer class or tuple.

I have hacked around this by making these fields omitted by default, unless declared in the expand argument:

def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)

    expandable_method_fields = ('things', 'more_things')

    if '~all' in self._flex_options['expand'] or '*' in self._flex_options['expand']:
        return

    for field in expandable_method_fields:
        if (
            field not in self._flex_options['expand']
            and field not in self._flex_options['omit']
        ):
            self._flex_options['omit'].append(field)

It works well enough for my purposes, but I thought I'd pitch this as a feature request, in case it is helpful or others have solved the problem differently. Cheers, and thanks for the library!

Attribute Error when using rest_flex_fields.filter_backends.FlexFieldsFilterBackend

Thanks for this great library, I really love this. I have come across an issue however, when I wanted to use rest_flex_fields.filter_backends.FlexFieldsFilterBackend. When I added it to the configuration of DRF, the following import error occured:

Traceback (most recent call last):
  File "manage.py", line 22, in <module>
    main()
  File "manage.py", line 18, in main
    execute_from_command_line(sys.argv)
  File "d:\workspace\my_project\api\.env\lib\site-packages\django\core\management\__init__.py", line 401, in execute_from_command_line
    utility.execute()
  File "d:\workspace\my_project\api\.env\lib\site-packages\django\core\management\__init__.py", line 377, in execute
    django.setup()
  File "d:\workspace\my_project\api\.env\lib\site-packages\django\__init__.py", line 24, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "d:\workspace\my_project\api\.env\lib\site-packages\django\apps\registry.py", line 114, in populate
    app_config.import_models()
  File "d:\workspace\my_project\api\.env\lib\site-packages\django\apps\config.py", line 211, in import_models
    self.models_module = import_module(models_module_name)
  File "d:\workspace\my_project\api\.env\lib\importlib\__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 728, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "d:\workspace\my_project\api\my_app\models.py", line 9, in <module>
    from utils.mixins import ResultsModelMixin, TrackFieldChangesMixin
  File "d:\workspace\my_project\api\utils\mixins\__init__.py", line 4, in <module>
    from .results import ResultsModelMixin, ResultsListViewMixin
  File "d:\workspace\my_project\api\utils\mixins\results\__init__.py", line 2, in <module>
    from .views import ResultsListViewMixin
  File "d:\workspace\my_project\api\utils\mixins\results\views.py", line 5, in <module>
    from rest_framework.generics import get_object_or_404
  File "d:\workspace\my_project\api\.env\lib\site-packages\rest_framework\generics.py", line 24, in <module>
    class GenericAPIView(views.APIView):
  File "d:\workspace\my_project\api\.env\lib\site-packages\rest_framework\generics.py", line 43, in GenericAPIView
    filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
  File "d:\workspace\my_project\api\.env\lib\site-packages\rest_framework\settings.py", line 220, in __getattr__
    val = perform_import(val, attr)
  File "d:\workspace\my_project\api\.env\lib\site-packages\rest_framework\settings.py", line 168, in perform_import
    return [import_from_string(item, setting_name) for item in val]
  File "d:\workspace\my_project\api\.env\lib\site-packages\rest_framework\settings.py", line 168, in <listcomp>
    return [import_from_string(item, setting_name) for item in val]
  File "d:\workspace\my_project\api\.env\lib\site-packages\rest_framework\settings.py", line 177, in import_from_string
    return import_string(val)
  File "d:\workspace\my_project\api\.env\lib\site-packages\django\utils\module_loading.py", line 17, in import_string
    module = import_module(module_path)
  File "d:\workspace\my_project\api\.env\lib\importlib\__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "d:\workspace\my_project\api\.env\lib\site-packages\rest_flex_fields\__init__.py", line 3, in <module>
    from .views import FlexFieldsModelViewSet
  File "d:\workspace\my_project\api\.env\lib\site-packages\rest_flex_fields\views.py", line 6, in <module>
    from rest_framework import viewsets
  File "d:\workspace\my_project\api\.env\lib\site-packages\rest_framework\viewsets.py", line 199, in <module>
    class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
AttributeError: module 'rest_framework.generics' has no attribute 'GenericAPIView'

Removing the imports from .views import FlexFieldsModelViewSet from __init__.py and from rest_framework.viewsets import GenericViewSet from filter_backends.py solved the issue for me. Probably the later could be placed behind the TYPE_CHECKING flag from typing, but I have no idea about the first one.

Env:

Django==3.0.7
django-filter==2.3.0
djangorestframework==3.11.0
drf-flex-fields==0.8.5

Expand not working as expected

I have the following models:

class UploadTemplate(models.Model):
    book = models.ForeignKey('Book', on_delete=models.SET_NULL, null=True)
    coupon_type = models.CharField(max_length=100)
class Book(models.Model):
    book_name = models.CharField(max_length=30)
    #....random other fields

and serializers as follows:

class TemplateSerializer(FlexFieldsModelSerializer):
    book = serializers.PrimaryKeyRelatedField(read_only=True)
    class Meta:
        model = UploadTemplate
        fields = ["coupon_type", "id", "book"]
        expandable_fields = {
            'book': ('BookSerializer', {
                'source': 'book'
            })
        }
class BookSerializer(FlexFieldsModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"

If i make a call to http://localhost:8000/v1/coupons/templates/6/?expand=book I'd expect book to expand to the nested serializer. Unfortunately it doesn't and I've drawn a blank debugging.

My viewset:

from rest_flex_fields.views import FlexFieldsMixin

class UploadTemplateViewset(viewsets.ModelViewSet, FlexFieldsMixin):
    queryset = models.UploadTemplate.objects.all()
    serializer_class = serializers.UploadTemplateSerializer
    filterset_fields = ('book', )
    permit_list_expands = ['book']

    def get_queryset(self):
        print(is_expanded(self.request, 'book'))
        return models.UploadTemplate.objects.all()

confirms that book is expanded. (i,e, it prints True in the console).

Am I doing something obviously dumb, and if not, where should I start with debugging the issue? I'm on django 2, python 3.6

Expand Argument Not Respected When Empty

I would expect that when instantiating a serializer and explicitly setting the expand argument, that would be respected no matter what. Instead, the behavior is that if there is an expand query param, that is respected over an empty list argument because empty lists are falsy. Here is the logic in question:

"expand": (
                self._get_permitted_expands_from_query_param(EXPAND_PARAM)
                if not expand
                else []
            ),

Maybe this is the intended behavior but it seems strange to me. Feature or bug?

URL querystring not parsed correctly in 0.7.0

It seems that since upgrading to 0.7.0, drf-flex-fields no longer parses expansions requested via URL querystring the same way.

I have been doing this:

class EphemeronViewSet(FlexFieldsModelViewSet):
    [...]
    def get_serializer(self, *args, **kwargs):
        if "expand" in self.request.query_params:  # add from querystring in URL
            kwargs["expand"] = self.request.query_params['expand']
        return super(EphemeronViewSet, self).get_serializer(*args, **kwargs)

which allows expanding fields like this:

curl '0.0.0.0:8000/api/ephemera/?expand=user'

But since upgrading to 0.7.0, the field no longer gets expanded in the output.

I think these changed lines are what resulted in the change in behavior. If I add a print(passed) just after that and compare previous behavior to 0.7.0:

Before:

{'expand': 'user', 'fields': [], 'omit': []}

After:

{'expand': ['u', 's', 'e', 'r'], 'fields': [], 'omit': []}

Perhaps these lines should be something more like [kwargs.pop("expand")] if "expand" in kwargs else [] instead of list(kwargs.pop("fields", []))?

(Or perhaps there's a better way to pass querystrings to drf-flex-fields than what I've been doing in my get_serializer() override as shown above? I have been doing it that way in order to expand fields in response to a POST request, if I recall recorrectly.)

Add settings for the query parameter names

Now, the query parameter expand and fields are fixed. Also, the magic value ~all as expand value is fixed. It would be nicer if these could be configured in the settings.

Reverse Relationships?

Hello,

I may be missing something, so I apologize if I am, but I am unable to get DRF-Flex-Fields to work properly with a reverse relationships . Is this not something that drf-flex-fields can do or am I messing up somehow?

Allow omit query_param and omit keyword

I would like to specify some fields to omit when I initialize the serializer and also allow the API request query_param to omit extrfa fields. Right now it's either or but merging the two would be nice. Also the same for fields, possibly.

For example:
This should return all the fields except password &email. Right now, it will only omit password.
FlexSerializer(data=request.data, omit=['password'])
GET /api/?omit=email

      def __init__(self, *args, **kwargs):
        expand = list(kwargs.pop("expand", []))
        fields = list(kwargs.pop("fields", []))
        omit = list(kwargs.pop("omit", []))

        super(FlexFieldsSerializerMixin, self).__init__(*args, **kwargs)

        self.expanded_fields = []
        self._flex_fields_applied = False

        self._flex_options = {
            "expand": (
                expand
                if len(expand) > 0
                else self._get_permitted_expands_from_query_param()
            ),
            "fields": (
                fields if len(fields) > 0 else self._get_query_param_value("fields")
            ),
            "omit": omit if len(omit) > 0 else self._get_query_param_value("omit"),
        }

Doesn't work out of the gate

Not sure what I'm doing wrong, but I'd really love to get this fixed after following the README pretty exact I think.

serializers.py

from rest_flex_fields import FlexFieldsModelSerializer

class CastleSerializer(FlexFieldsModelSerializer):
    class Meta:
        model = Castle
        fields = '__all__'

class RoomSerializer(FlexFieldsModelSerializer):
    castle = serializers.PrimaryKeyRelatedField(read_only=True)

    class Meta:
        model = Room
        fields = ('id', 'name', 'castle')

        expandable_fields = {
            "castle": CastleSerializer
        }

settings.py

# To make our nested serializers more like JSON API
REST_FLEX_FIELDS = {"EXPAND_PARAM": "include"}

API call: GET /api/v1/room/1/?include=castle
or
API call: GET /api/v1/rooms/?include=castle

both give:

{
    "id": 1,
    "name": "Throne Room",
    "castle": 1
}

OR

[
  {
    "id": 1,
    "name": "Throne Room",
    "castle": 1
  }
]

I expect it to be:

{
    "id": 1,
    "name": "Throne Room",
    "castle": {
        "id": 1,
        "name": "Daventry Castle"
    }
}

I also expect to be able to POST an update method without having to add the nested castle id and have it just validate and work. That looks like it's fixed in the latest.
API call: POST /api/v1/rooms-update/

{
    "id": 1,
    "name": "Throne Room Changed Text",
}

TO RETURN

{
    "id": 1,
    "name": "Throne Room Changed Text",
    "castle": {
        "id": 1,
        "name": "Daventry Castle"
    }
}

It seems none of this is currently working. What am I missing out there (probably obvious to everyone else) to get this library working?

Filter Backend for Query optimization using wrong check

In the filter backend for optimizing the queries, the backend is doing a check to confirm that the serializer being used is a valid FlexFields serializer.
https://github.com/rsinger86/drf-flex-fields/blob/master/rest_flex_fields/filter_backends.py#L19

However I think rather than checking for the FlexFieldsModelSerializer, the proper check should be for a instance of FlexFieldsSerailizerMixin, as this mixin is what actually contains the functionality needed by the filter backend.

There are several cases in my project where we are having to build our own ModelSerializers where it doesn't make sense or could break things to introduce an additional ModelSerializer to the MRO, especially just to add the functionality provided by simply adding FlexFieldsSerializerMixin.

Undocumented feature: HyperlinkedRelatedField works as expected

The FlexFieldsModelSerializer returns values for related fields by default:

GET /api/location/region/AKL/

{
    "url": "http://127.0.0.1:8000/api/location/region/AKL/",
    "code": "AKL",
    "name": "Auckland",
    "country": "NZL"
}

But by specifying country as a HyperlinkedRelatedField:

class RegionSerializer(FlexFieldsModelSerializer):
    country = serializers.HyperlinkedRelatedField(
        read_only=True,
        view_name='country-detail'
    )

We get:

{
    "url": "http://127.0.0.1:8000/api/location/region/AKL/",
    "code": "AKL",
    "name": "Auckland",
    "country": "http://127.0.0.1:8000/api/location/country/NZL/"
}

and ?expand=country works as expected. Nice.

Automatic Query Optimization

is it possible to optimize query automatically for list request? I am not really sure how to implement this, but maybe we can override get_queryset to append the query with prefetch_related using fields that are is_expanded?

Expandable Fields through SOA architecture

Hi, I created an expansion from your flex fields to allow the usage between APIs.

It is really usefull in a distributed system, where we usually have many weak foreign keys that points to a resource in a different service.

The usage remained the same, so its transparent to the API consumer. It even allows the nested expand/fields/omit through APIs

Do you have interest in a PR with this feature?

Ex:

    class Meta:
        model = FileImportInput
        fields = "__all__"
        expandable_fields = {
            "data_input": (
                APIResourceFlexDict,
                {
                    "url": settings.DATA_INPUT_RESOURCE,
                    "included_headers": ["Authorization"],
                },
            ),
        }

Django 3.2 support

I've run the test suite for this package on Django 3.2, and it only required minor changes to work (and still be Django 3.1 compatible).

Would you accept a PR to upgrade this package's support to Django 3.2 and fix some deprecation warnings? Let me know if so.

`~all` or `*` should work in `fields` too

Hey there! I was looking at graphql until I found you awesome library, thank you! I don't have to add another yet API ๐Ÿ—ก๏ธ

The question is:
We have two models with a lot of fields, e.g

class Order:
    field0 = ....
    field1 = ...
    ...
    field35 = ...
   restaurant=Restaurant

class Restaurant:
   field0 = ...
   field1 = ...
   ...
   field60 = ....

And I want to make a request that will get All fields from Order, expand Restaurant, but only field0\field1
How can I achieve it with drf-flex?

I tried to solve it these ways:

  1. GET /orders/?expand=restaurant - but it gives too much fields, I really want to only two from Restaurant because we have about 100 orders in a list and all of them will contain 60 additional fields
  2. GET /orders/?fields=field0,field1,...,field35,restaurant.field0,restaurant.field1&expand=restaurant - In this case I get that I want to, but I have to add all of fields to params, and I really don't want to do it on the frontend :)
  3. GET /orders/?expand=restaurant - give me too much fields too

What I really want to can be achieved with GET /orders/?fields=~all,restaurant.field0, restaurant.field2&expand=restaurant

But looks like ~all works only in expand and not on fields, right?

AttributeError 'map' object has no attribute 'split'

Hello,

I started getting this error after upgrading to Python 3 where fields is a generator and not a list.

Traceback:  

File "/app/.venv/lib/python3.7/site-packages/django/core/handlers/exception.py" in inner
  41.             response = get_response(request)

File "/app/.venv/lib/python3.7/site-packages/django/core/handlers/base.py" in _legacy_get_response
  249.             response = self._get_response(request)

File "/app/.venv/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
  187.                 response = self.process_exception_by_middleware(e, request)

File "/app/.venv/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
  185.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/app/.venv/lib/python3.7/site-packages/django/views/decorators/csrf.py" in wrapped_view
  58.         return view_func(*args, **kwargs)

File "/app/.venv/lib/python3.7/site-packages/rest_framework/viewsets.py" in view
  103.             return self.dispatch(request, *args, **kwargs)

File "/app/.venv/lib/python3.7/site-packages/rest_framework/views.py" in dispatch
  483.             response = self.handle_exception(exc)

File "/app/.venv/lib/python3.7/site-packages/rest_framework/views.py" in handle_exception
  443.             self.raise_uncaught_exception(exc)

File "/app/.venv/lib/python3.7/site-packages/rest_framework/views.py" in dispatch
  480.             response = handler(request, *args, **kwargs)

File "/app/.venv/lib/python3.7/site-packages/rest_framework/mixins.py" in list
  44.             serializer = self.get_serializer(page, many=True)

File "/app/.venv/lib/python3.7/site-packages/rest_framework/generics.py" in get_serializer
  112.         return serializer_class(*args, **kwargs)

File "/app/.venv/lib/python3.7/site-packages/rest_framework/serializers.py" in __new__
  124.             return cls.many_init(*args, **kwargs)

File "/app/.venv/lib/python3.7/site-packages/rest_framework/serializers.py" in many_init
  145.         child_serializer = cls(*args, **kwargs)

File "/app/.venv/lib/python3.7/site-packages/rest_flex_fields/serializers.py" in __init__
  34.         sparse_fields, next_sparse_fields = split_levels(fields)

File "/app/.venv/lib/python3.7/site-packages/rest_flex_fields/utils.py" in split_levels
  27.         fields = [a.strip() for a in fields.split(",") if a.strip()]

AttributeError at /api/v2/users/
'map' object has no attribute 'split'

Running on Python 3.7

Django==1.11.25
djangorestframework==3.8.2
drf-flex-fields==0.6.1

I can submit a PR to fix this issue if you don't mind

Omitted fields still appears to allow saving omitted field on the model

When passing omitted fields to the serializer in a views' get_serializer method, it still seems to be possible to save/update said fields, even though they're not returned in the Response.

For example:

def get_serializer(self, *args, **kwargs):
    serializer_class = self.get_serializer_class()
    kwargs.setdefault('context', self.get_serializer_context())
    kwargs.update({'omit': 'example_field'})
    return serializer_class(*args, **kwargs)

Will not return example_field in the response, however any value passed into a PUT or PATCH request for that field will still be saved on the model.

Fields don't seem to be omitted until to_representation is called, but the lateness of that call means that the serializer still contains these fields up until the response is prepared. Is this the expected behavior? It seems contrary to what I would expect.

OpenAPI support for flex fields

I am using drf-spectacular for schema generation; using the @extend_schema decorator to add docs to my views.

Say I have a serializer named PersonSerializer that inherits from FlexFieldsModelSerializer. If I pass it the fields argument, I expect it to show only those fields in the schema. Example code:

@extend_schema(
    responses={201: PersonSerializer(fields=["id", "username"])},
)
@api_view(["POST"])
def my_view(request):

But the schema picks up all the default fields from the PersonSerializer class.

I do not know if this is something that needs to be solved in this project or drf-spectacular but any help is greatly appreciated.

Deferred fields?

Documentation talks about deferring fields but I can't get that to work. Just so we're on the same page, I have a couple of statistic fields that I want to make available but I don't want included in every query. These are annotated and pretty expensive (hence the deferral).

So here, I want to be able to annotate and display the last visit (the end of the last booking a Visitor had). The annotation itself โ€”despite looking pretty gnarlyโ€” works.... But throwing expand=last_visit or expand=last_visit&fields=last_visit through on the querystring doesn't make the serialiser render this field.

class VisitorSerializer(FlexFieldsModelSerializer):
    class Meta:
        model = Visitor
        fields = ('first_name', 'second_name')
        expandable_fields = {
            'last_visit': (serializers.DateTimeField, {})
        }

class VisitorViewSet(viewsets.ModelViewSet):
    permission_classes = (IsAuthenticated,)
    queryset = Visitor.objects.all()
    serializer_class = VisitorSerializer

    permit_list_expands = ['last_visit']

    def get_queryset(self):
        queryset = super().get_queryset()

        if is_expanded(self.request, 'last_visit'):
            subq = Booking.objects.filter(visitor_bookings__visitor=OuterRef('pk')).values('timeframe')
            queryset = queryset.annotate(
                last_visit=RangeEndsWith(Subquery(subq))
            )

        return queryset

fields arguement in Serializer constructor not working from 0.8.0

This works perfectly fine in 0.7.5, but breaks from 0.8 ๐Ÿ˜Ÿ

from rest_flex_fields import FlexFieldsModelSerializer
from django.db import models


class MentorMentee(models.Model):
    mentee = models.ForeignKey('CustomUser', related_name="mentors", on_delete=models.CASCADE)
    skill = models.ForeignKey('Skill', on_delete=models.CASCADE)
    mentor = models.ForeignKey('CustomUser', related_name="mentees", on_delete=models.CASCADE)


class MentorReqSerializer(FlexFieldsModelSerializer):
    class Meta:
        model = MentorMentee
        fields = '__all__'


data = MentorReqSerializer(fields=('mentor', 'skill'), data={'mentor': 1, 'skill': 51518})
data.is_valid(raise_exception=True)

Raises

rest_framework.exceptions.ValidationError: {'mentee': [ErrorDetail(string='This field is required.', code='required')]}

Adding FlexFieldsFilterBackend immediately breaks the app

I am utilizing drf-flex-fields heavily and everything works fine until the moment i add:

REST_FRAMEWORK = {
    ...
    'DEFAULT_FILTER_BACKENDS': (
        'rest_flex_fields.filter_backends.FlexFieldsFilterBackend',
    ),
    ...
}

It crashes the app with the following traceback:

INFO 2020-03-03 21:38:35,864 autoreload 90410 4456287680 Watching for file changes with StatReloader
Performing system checks...

Exception in thread django-main-thread:
Traceback (most recent call last):
  File "/usr/local/Cellar/[email protected]/3.8.1/Frameworks/Python.framework/Versions/3.8/lib/python3.8/threading.py", line 932, in _bootstrap_inner
    self.run()
  File "/usr/local/Cellar/[email protected]/3.8.1/Frameworks/Python.framework/Versions/3.8/lib/python3.8/threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/django/utils/autoreload.py", line 53, in wrapper
    fn(*args, **kwargs)
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/django/core/management/commands/runserver.py", line 117, in inner_run
    self.check(display_num_errors=True)
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/django/core/management/base.py", line 392, in check
    all_issues = self._run_checks(
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/django/core/management/base.py", line 382, in _run_checks
    return checks.run_checks(**kwargs)
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/django/core/checks/registry.py", line 72, in run_checks
    new_errors = check(app_configs=app_configs)
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/django/core/checks/urls.py", line 13, in check_url_config
    return check_resolver(resolver)
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/django/core/checks/urls.py", line 23, in check_resolver
    return check_method()
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/django/urls/resolvers.py", line 407, in check
    for pattern in self.url_patterns:
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/django/utils/functional.py", line 48, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/django/urls/resolvers.py", line 588, in url_patterns
    patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/django/utils/functional.py", line 48, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/django/urls/resolvers.py", line 581, in urlconf_module
    return import_module(self.urlconf_name)
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 783, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/Users/alex.zagoro/projects/gagosian/noya/gagosian/urls.py", line 6, in <module>
    path('api/v1/', include('gagosian.api.v1.urls', namespace='api'))
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/django/urls/conf.py", line 34, in include
    urlconf_module = import_module(urlconf_module)
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 783, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/Users/alex.zagoro/projects/gagosian/noya/gagosian/api/v1/urls.py", line 4, in <module>
    from .contacts import urls as contacts_urls
  File "/Users/alex.zagoro/projects/gagosian/noya/gagosian/api/v1/contacts/urls.py", line 3, in <module>
    from .viewsets import (
  File "/Users/alex.zagoro/projects/gagosian/noya/gagosian/api/v1/contacts/viewsets.py", line 1, in <module>
    from rest_framework.viewsets import ModelViewSet
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/rest_framework/viewsets.py", line 27, in <module>
    from rest_framework import generics, mixins, views
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/rest_framework/generics.py", line 24, in <module>
    class GenericAPIView(views.APIView):
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/rest_framework/generics.py", line 43, in GenericAPIView
    filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/rest_framework/settings.py", line 220, in __getattr__
    val = perform_import(val, attr)
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/rest_framework/settings.py", line 168, in perform_import
    return [import_from_string(item, setting_name) for item in val]
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/rest_framework/settings.py", line 168, in <listcomp>
    return [import_from_string(item, setting_name) for item in val]
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/rest_framework/settings.py", line 177, in import_from_string
    return import_string(val)
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/django/utils/module_loading.py", line 17, in import_string
    module = import_module(module_path)
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/rest_flex_fields/__init__.py", line 3, in <module>
    from .views import FlexFieldsModelViewSet
  File "/Users/alex.zagoro/venv/noya-TS4FQGPC/lib/python3.8/site-packages/rest_flex_fields/views.py", line 21, in <module>
    class FlexFieldsModelViewSet(FlexFieldsMixin, viewsets.ModelViewSet):
AttributeError: partially initialized module 'rest_framework.viewsets' has no attribute 'ModelViewSet' (most likely due to a circular import)

Adding the backend directly to the view works fine.

dot-notation not working

Based on https://github.com/rsinger86/drf-flex-fields#dynamically-setting-fields,

Here are my serializers:

class AddressBookSerializer(FlexFieldsModelSerializer):
    class Meta:
        model = SellerAddressBook
        fields = ('id', 'address', 'city', 'state', )
class OrderSerializer(FlexFieldsModelSerializer):
    address = AddressBookSerializer(many=False)

    class Meta:
        model = Order
        fields = ('id', 'order_name', 'address',)

On Get, /orders/123/?fields=id,order_name,address.city

ACTUAL RESULT

{
  "id" : 123
  "order_name" : "Order Name",
  "address" : {
    "id" : "1",
    "address": "my add",
    "city": "my_city",
    "state": " my state"
}

EXPECTED RESULT

{
  "id" : 13322
  "order_name" : "Order Name",
  "address" : {
      "city": "my_city"
 }

Bug in serializer when passing params

Hi!
Thanks for the great library! I faced an issue with multiple nested fields expansion and was able to track it down to the following lines:

if name in nested_expand:
settings["expand"] = nested_expand[name]
if name in nested_fields:
settings["fields"] = nested_fields[name]
if name in nested_omit:
settings["omit"] = nested_omit[name]
if type(serializer_class) == str:
serializer_class = self._get_serializer_class_from_lazy_string(
serializer_class
)
return serializer_class(**settings)

TypeError: __init__() got an unexpected keyword argument 'expand'

Since I am using custom REST_FLEX_FIELDS params, this is throwing an error because the serializer expected parameters with different names since you only pop off the expected custom params

def __init__(self, *args, **kwargs):
expand = list(kwargs.pop(EXPAND_PARAM, []))
fields = list(kwargs.pop(FIELDS_PARAM, []))
omit = list(kwargs.pop(OMIT_PARAM, []))
super(FlexFieldsSerializerMixin, self).__init__(*args, **kwargs)

I believe all thats needed to fix this is to edit this section to utilize the custom params instead when passing

FilterBackend: Include not-expanded M2M fields in prefetch-related clause

Hi Robert;

First of all, thank you so much for such an amazing library! ๐Ÿ˜„

I'm testing the "FlexFieldsFilterBackend" and I think you overlooked something. Please let me know if I'm wrong.

When dealing with M2M fields, it's always desiderable to include them in prefetch_related clause, even if you're working only with pks. Otherwise, django would need to perform an extra query for every single record.

I noticed that you check if the field is expanded before including it in the prefetch_related clause. I think it should be included anyways.

and field.field_name in serializer.expanded_fields

Thanks in advance.

Expand feature in multi database

HI,
I have to use drf-flex-field in multi database architecture.
Currently I am looping through all the databases and fetching the result. but when I pass expand it breaks and raise error that Model doesn't exists. because it is selecting default database.

Is there any way to pass the database name to be using when expanding and serialising?

Missing tag for 0.9.4

v0.9.4 is published on PyPi but there isn't a corresponding tag (or source) in this repo.

Support arrays in query string for `expan`, `fields` etc

Currently flex-fields support passing lists of fields to expand/omit like this:
?expand=field_1,field_2,field_3

For our internal development there is a request from frontend team to support passing lists like this:

  1. ?expand=field_1,field_2
  2. ?expand=field_1&expand=field_2
  3. ?expand[]=field_1&expand[]=field_2

The reason is - different libs/frameworks has different internal implementation for handling query string arrays, and looks like all this 3 options used.

Currently we do this with overriding _parse_request_list_value method from FlexFieldsModelSerializer.
This is naive and simple implementation:

    def _parse_request_list_value(self, field):
        if not self._can_access_request:
            return []

        values = self.context["request"].query_params.getlist(field)
        if not values:
            values = self.context["request"].query_params.getlist('{}[]'.format(field))

        if values and len(values) == 1:
            return values[0].split(",")
        return values or []

@rsinger86 Do you interested in adding this to the drf-flex-fields itself? If so, I can create PR then.

`is_expanded` logic for nested fields is incorrect

  1. If I have nested fields such that a.b and b are both legal expands, is_epxanded will not distinguish between b at the top level or nested
  2. is_expanded will return true for all keys if ~all is in expanded, but ~all only expands top level expands

Proposed fix:

def is_expanded(expand, key):
    """Determine if the given key is expanded"""
    expand_fields = []

    # first split on commas to get each expand
    for full in expand.split(","):
        # than split on dots to get each component that is expanded
        parts = full.split(".")
        for i in range(len(parts)):
            # add each prefix, as each prefix is epxanded, ie
            # a.b.c will add a, a.b and a.b.c to the expand_fields list
            # we do this to differentiate a.b from b
            expand_fields.append(".".join(parts[: i + 1]))

    # ~all only expands top level fields
    if "." not in key and "~all" in expand_fields:
        return True

    return key in expand_fields

Test:

import pytest

from .utils import is_expanded

data = [
    ("a", "a", True),
    ("a", "b", False),
    ("a,b,c", "a", True),
    ("a,b,c", "b", True),
    ("a,b,c", "c", True),
    ("a,b,c", "d", False),
    ("a.b.c", "a", True),
    ("a.b.c", "a.b", True),
    ("a.b.c", "a.b.c", True),
    ("a.b.c", "b", False),
    ("a.b.c", "c", False),
    ("a.b.c", "d", False),
    ("a.b.c,d", "a", True),
    ("a.b.c,d", "d", True),
    ("~all", "a", True),
    ("~all", "a.b", False),
]


class TestIsExpanded:
    @pytest.mark.parametrize("expand,key,ret", data)
    def test_expanded(self, expand, key, ret):
        assert is_expanded(expand, key) is ret

Can't rename expanded field (because "source" keyword is excluded)

I'm trying to rename a field that is also a nested serializer. Normally, I'd use the source kwarg during serializer instantiation for this, but if I define the serializer in expandable_fields, it doesn't work. In the code below, if I set the field name (in the FooBarSerializer) to foo_bar_details, everything works. But if I set it to details and then include the source field, I get this error (this approach works fine if I instantiate the serializer normally, outside of expandable_fields):

  File "/Users/david/src/proj/venv/lib/python3.7/site-packages/rest_framework/serializers.py", line 1325, in build_unknown_field
    (field_name, model_class.__name__)
django.core.exceptions.ImproperlyConfigured: Field name `details` is not valid for model `FooBar`.
class FooBarDetails(django.db.models.Model):
    id = models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True)
    properties = models.CharField(max_length=256, null=True, blank=True)  
    class Meta:
        db_table = 'foo_bar_details'

class FooBar(django.db.models.Model):
    id = models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True)
    name = models.CharField(max_length=256, null=True, blank=True)  
    foo_bar_details = models.ForeignKey(FooBarDetails, on_delete=models.SET_NULL, null=True)
    class Meta:
        db_table = 'foo_bar'

class FooBarDetailsSerializer(FlexFieldsModelSerializer):
    class Meta:
        model = FooBarDetails
        fields = ('properties')

class FooBarSerializer(FlexFieldsModelSerializer):
    expandable_fields = {
        'details': (
            FooBarDetailsSerializer,
            {'required': False, 'many': False, 'read_only': True, 'source': 'foo_bar_details'},
        ),
    }
    class Meta:
        model = FooBar
        fields = (
            'id',
            'name',
            'details',
        )

I think I've traced this issue to here: https://github.com/rsinger86/drf-flex-fields/blob/master/rest_flex_fields/serializers.py#L102
Is there a reason that the source keyword is purposefully being excluded from the serializer instantiation?

OpenAPI support for query parameters

Would it be possible to have OpenAPI support for query parameters?

At the moment this is provided by the FlexFieldsFilterBackend however the filter backend also implements the (not thoroughly tested) query optimization features, which may not be desirable.

An easy fix would be to just separate the docs into its own (dummy) filter backend that doesn't actually do anything query-wise, so people who only want docs can include that one. It does feel a bit hacky to make an essentially fake filtering backend just for documentation so I'd like to get others' thoughts on this if there's a better way.

Create unique type names in FlexFieldsMixin

All fields that are expandable will get the same serializer names. It's not directly an issue with this library but this causes issues with (for example) schema generation where names -can- be based on serializer class names.

Perhaps include the original serializer name in the generated type name.

In views.py:50 change this:

-		return type('DynamicFieldsModelSerializer', (self.serializer_class,), {
+		return type('{}FlexFieldsSerializer'.format(self.serializer_class.__name__), (self.serializer_class,), {
			'expand': expand, 
			'include_fields': fields,
		})

FlexFieldsFilterBackend does not handle "GenericForeignKey"

When calling a ViewSet that has both the FlexFieldsFilterBackend and a serializer containing a field pointing to a GenericForeignKey, we get the error : AttributeError: 'GenericForeignKey' object has no attribute 'attname'

It can easily be reproduced with the following classes :
models

class TaggedItem(models.Model):
    tag = models.SlugField()
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')
    link = ForeignKey(SomeTaggableModel, null=True, on_delete=models.CASCADE)

serializers

class TaggedItemSerializer(FlexFieldsSerializerMixin, ModelSerializer):
    content_object = PrimaryKeyRelatedField(read_only=True) 
    class Meta:
        model = TaggedItem
        fields = (
            "id",
            "content_type",
            "object_id",
            "content_object"
        )

Please notice that the PrimaryKeyRelatedField is only here for simplicity, in real life it could be a GenericRelatedField from
rest-framework-generic-relations.

views

class TaggedItemViewSet(ModelViewSet):
    serializer_class = TaggedItemSerializer
    queryset = TaggedItem.objects.all()
    filter_backends = [FlexFieldsFilterBackend]

After digging a bit, I found that the field gets included in the queryset.only(...) and crashes probably because it is not a concrete field. A GenericForeignKey should, instead, be included in the queryset.prefetch_related(...) fields.

AppRegistryNotReady caused by __init__.py

The easy fix is to not import everything into __init__, but that changes the API. The bad solution is to tell people to not list the app in INSTALLED_APPS. The ugly solution is to change views.py to import DRF internals inside the methods where they are needed, not importing at the top.

python3 manage.py migrate

  ...
  File "/usr/lib/python3.8/site-packages/rest_flex_fields/__init__.py", line 3, in <module>
    from .views import FlexFieldsModelViewSet
  File "/usr/lib/python3.8/site-packages/rest_flex_fields/views.py", line 6, in <module>
    from rest_framework import viewsets
  File "/usr/lib/python3.8/site-packages/rest_framework/viewsets.py", line 27, in <module>
    from rest_framework import generics, mixins, views
  File "/usr/lib/python3.8/site-packages/rest_framework/generics.py", line 9, in <module>
    from rest_framework import mixins, views
  File "/usr/lib/python3.8/site-packages/rest_framework/views.py", line 17, in <module>
    from rest_framework.schemas import DefaultSchema
  File "/usr/lib/python3.8/site-packages/rest_framework/schemas/__init__.py", line 33, in <module>
    authentication_classes=api_settings.DEFAULT_AUTHENTICATION_CLASSES,
  File "/usr/lib/python3.8/site-packages/rest_framework/settings.py", line 220, in __getattr__
    val = perform_import(val, attr)
  File "/usr/lib/python3.8/site-packages/rest_framework/settings.py", line 168, in perform_import
    return [import_from_string(item, setting_name) for item in val]
  File "/usr/lib/python3.8/site-packages/rest_framework/settings.py", line 168, in <listcomp>
    return [import_from_string(item, setting_name) for item in val]
  File "/usr/lib/python3.8/site-packages/rest_framework/settings.py", line 177, in import_from_string
    return import_string(val)
  File "/usr/lib/python3.8/site-packages/django/utils/module_loading.py", line 17, in import_string
    module = import_module(module_path)
  File "/usr/lib64/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "/usr/lib/python3.8/site-packages/rest_framework_jwt/authentication.py", line 17, in <module>
    from rest_framework_jwt.blacklist.exceptions import (
  File "/usr/lib/python3.8/site-packages/rest_framework_jwt/blacklist/exceptions.py", line 8, in <module>
    class MissingToken(AuthenticationFailed):
  File "/usr/lib/python3.8/site-packages/rest_framework_jwt/blacklist/exceptions.py", line 10, in MissingToken
    msg = _('The token is missing.')
  File "/usr/lib/python3.8/site-packages/django/utils/translation/__init__.py", line 105, in ugettext
    return gettext(message)
  File "/usr/lib/python3.8/site-packages/django/utils/translation/__init__.py", line 92, in gettext
    return _trans.gettext(message)
  File "/usr/lib/python3.8/site-packages/django/utils/translation/trans_real.py", line 354, in gettext
    _default = _default or translation(settings.LANGUAGE_CODE)
  File "/usr/lib/python3.8/site-packages/django/utils/translation/trans_real.py", line 267, in translation
    _translations[language] = DjangoTranslation(language)
  File "/usr/lib/python3.8/site-packages/django/utils/translation/trans_real.py", line 154, in __init__
    self._add_installed_apps_translations()
  File "/usr/lib/python3.8/site-packages/django/utils/translation/trans_real.py", line 195, in _add_installed_apps_translations
    raise AppRegistryNotReady(
django.core.exceptions.AppRegistryNotReady: The translation infrastructure cannot be initialized before the apps registry is ready. Check that you don't make non-lazy gettext calls at import time.

Also occurs with drf_extended_viewset and beda-software/drf-writable-nested#93

How to pass context to child serializer?

I'm facing a RecursionError when querying a subset of fields that should not make recursion at all.

Here are some simplified models and serializers:

# disclaimer: I did not directly test this code, it's just an extract of mine.
# If you don't manage to reproduce the issue with this snippet, please let me know

class Client(models.Model):
    name = models.CharField(max_length=250)

class Project(models.Model):
    name = models.CharField(max_length=250)
    client = models.ForeignKey(Client, on_delete=models.PROTECT, related_name='projects')

class ProjectSerializer(FlexFieldsModelSerializer):
    expandable_fields = {
        'client_details': ('api.ClientSerializer', {'source': 'client', 'read_only': True}),
    }
    class Meta:
        model = Project
        fields = [
            'id',
            'name',
        ]

class ClientSerializer(FlexFieldsModelSerializer):
    expandable_fields = {
        'projects_details': (ProjectSerializer, {'source': 'projects', 'many': True, 'read_only': True}),
    }
    class Meta:
        model = Client
        fields = [
            'id',
            'name',
        ]

I queried my endpoint like this:

/api/client/5906?expand=projects_details&fields=id,name,projects_details.id

The expected result would be:

{
  "id": 5906,
  "name": "client name",
  "projects_details": [
    {
      "id": 2056
    },
    {
      "id": 3323
    }
  ]
}

Instead, I'm getting a RecursionError (see below). Did I miss something ? I understand that since I'm requesting to expand the projects and the projects themselves have a reference to the clients, but given the fields input, I feel like this should not fall in recursion.

RecursionError at /api/client/5906
maximum recursion depth exceeded

Request Method: GET
Request URL: http://localhost:81/api/client/5906?expand=projects_details&fields=id,name,projects_details.id
Django Version: 2.1.9
Python Executable: C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\Scripts\python.exe
Python Version: 3.7.3
Python Path: ****
Server time: Mon, 24 Jun 2019 17:47:09 +0200
Installed Applications:
['django.contrib.admindocs',
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'rest_framework',
 'django_filters',
 'corsheaders',
 'api',
 'custom_auth']
Installed Middleware:
['corsheaders.middleware.CorsMiddleware',
 'django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.auth.middleware.RemoteUserMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']


Traceback:

File "C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\lib\site-packages\django\core\handlers\exception.py" in inner
  34.             response = get_response(request)

File "C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\lib\site-packages\django\core\handlers\base.py" in _get_response
  126.                 response = self.process_exception_by_middleware(e, request)

File "C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\lib\site-packages\django\core\handlers\base.py" in _get_response
  124.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\lib\site-packages\django\views\decorators\csrf.py" in wrapped_view
  54.         return view_func(*args, **kwargs)

File "C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\lib\site-packages\django\views\generic\base.py" in view
  68.             return self.dispatch(request, *args, **kwargs)

File "C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\lib\site-packages\rest_framework\views.py" in dispatch
  495.             response = self.handle_exception(exc)

File "C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\lib\site-packages\rest_framework\views.py" in handle_exception
  455.             self.raise_uncaught_exception(exc)

File "C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\lib\site-packages\rest_framework\views.py" in dispatch
  492.             response = handler(request, *args, **kwargs)

File "C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\lib\site-packages\rest_framework\generics.py" in get
  284.         return self.retrieve(request, *args, **kwargs)

File "C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\lib\site-packages\rest_framework\mixins.py" in retrieve
  57.         serializer = self.get_serializer(instance)

File "C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\lib\site-packages\rest_framework\generics.py" in get_serializer
  112.         return serializer_class(*args, **kwargs)

File ".\api\serializers.py" in __init__
  230.         super().__init__(*args, **kwargs)

File "C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\lib\site-packages\rest_flex_fields\serializers.py" in __init__
  47.                 name, next_expand_fields, next_sparse_fields, next_omit_fields

File "C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\lib\site-packages\rest_flex_fields\serializers.py" in _make_expanded_field_serializer
  58.         serializer_settings = copy.deepcopy(field_options[1])

File "C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\lib\copy.py" in deepcopy
  150.         y = copier(x, memo)

File "C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\lib\copy.py" in _deepcopy_dict
  240.         y[deepcopy(key, memo)] = deepcopy(value, memo)

File "C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\lib\copy.py" in deepcopy
  150.         y = copier(x, memo)

File "C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\lib\copy.py" in _deepcopy_dict
  240.         y[deepcopy(key, memo)] = deepcopy(value, memo)

File "C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\lib\copy.py" in deepcopy
  180.                     y = _reconstruct(x, memo, *rv)

File "C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\lib\copy.py" in _reconstruct
  281.         if hasattr(y, '__setstate__'):

File "C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\lib\site-packages\rest_framework\request.py" in __getattr__
  412.             return getattr(self._request, attr)

File "C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\lib\site-packages\rest_framework\request.py" in __getattr__
  412.             return getattr(self._request, attr)

File "C:\Users\augendre\.virtualenvs\bizdev_kb_api-TgTXDkAC\lib\site-packages\rest_framework\request.py" in __getattr__
  412.             return getattr(self._request, attr)

[...]

Exception Type: RecursionError at /api/client/5906
Exception Value: maximum recursion depth exceeded
Request information:
USER: ****

GET:
expand = 'projects_details'
fields = 'id,name,projects_details.id'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.