Giter Club home page Giter Club logo

dynamic-rest's Introduction

Django Dynamic REST

Circle CI PyPi

Dynamic API extensions for Django REST Framework

See http://dynamic-rest.readthedocs.org for full documentation.

Table of Contents

Overview

Dynamic REST (or DREST) extends the popular Django REST Framework (or DRF) with API features that empower simple RESTful APIs with the flexibility of a graph query language.

DREST classes can be used as a drop-in replacement for DRF classes, which offer the following features on top of the standard DRF kit:

  • Linked relationships
  • Sideloaded relationships
  • Embedded relationships
  • Inclusions
  • Exclusions
  • Filtering
  • Sorting
  • Directory panel for your Browsable API
  • Optimizations

DREST was originally written to complement Ember__, but it can be used to provide fast and flexible CRUD operations to any consumer that supports JSON over HTTP.

Maintainers

Contributors

Requirements

  • Python (3.6, 3.7, 3.8)
  • Django (2.2, 3.1, 3.2)
  • Django REST Framework (3.11, 3.12, 3.13)

Installation

  1. Install using pip:
    pip install dynamic-rest

(or add dynamic-rest to requirements.txt or setup.py)

  1. Add rest_framework and dynamic_rest to INSTALLED_APPS in settings.py:
    INSTALLED_APPS = (
        ...
        'rest_framework',
        'dynamic_rest'
    )
  1. If you want to use the Directory panel, replace DRF's browsable API renderer with DREST's in your settings:
REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        'dynamic_rest.renderers.DynamicBrowsableAPIRenderer',
    ],
}

Demo

This repository comes with a tests package that also serves as a demo application. This application is hosted at https://dynamic-rest.herokuapp.com but can also be run locally:

  1. Clone this repository:
    git clone [email protected]:AltSchool/dynamic-rest.git
    cd dynamic-rest
  1. From within the repository root, start the demo server:
    make serve
  1. Visit localhost:9002 in your browser.

  2. To load sample fixture data, run make fixtures and restart the server.

Features

To understand the DREST API features, let us consider a demo model with a corresponding viewset, serializer, and route. This will look very familiar to anybody who has worked with DRF:

# The related LocationSerializer and GroupSerializer are omitted for brevity

# The Model
class User(models.Model):
    name = models.TextField()
    location = models.ForeignKey('Location')
    groups = models.ManyToManyField('Group')

# The Serializer
class UserSerializer(DynamicModelSerializer):
    class Meta:
        model = User
        name = 'user'
        fields = ("id", "name", "location", "groups")

    location = DynamicRelationField('LocationSerializer')
    groups = DynamicRelationField('GroupSerializer', many=True)

# The ViewSet
class UserViewSet(DynamicModelViewSet):
    serializer_class = UserSerializer
    queryset = User.objects.all()

# The Router
router = DynamicRouter()
router.register('/users', UserViewSet)

Linked relationships

One of the key features of the DREST serializer layer is the ability to represent relationships in different ways, depending on the request context (external requirements) and the code context (internal requirements).

By default, a "has-one" (or "belongs-to") relationship will be represented as the value of the related object's ID. A "has-many" relationship will be represented as a list of all related object IDs.

When a relationship is represented in this way, DREST automatically includes relationship links for any has-many relationships in the API response that represents the object:

-->
    GET /users/1/
<--
    200 OK
    {
        "user": {
            "id": 1,
            "name": "John",
            "location": 1,
            "groups": [1, 2],
            "links": {
                "groups": "/users/1/groups"
            }
        }
    }

An API consumer can navigate to these relationship endpoints in order to obtain information about the related records. DREST will automatically create the relationship endpoints -- no additional code is required:

-->
    GET /users/1/groups
<--
    200 OK
    {
        "groups": [{
            "id": 1,
            "name": "Family",
            "location": 2,
        }, {
            "id": 2,
            "name": "Work",
            "location": 3,
        }]
    }

Sideloaded relationships

Using linked relationships provides your API consumers with a "lazy-loading" mechanism for traversing through a graph of data. The consumer can first load the primary resource and then load related resources later.

In some situations, it can be more efficient to load relationships eagerly, in such a way that both the primary records and their related data are loaded simultaneously. In Django, this can be accomplished by using prefetch_related or select_related.

In DREST, the requirement to eagerly load (or "sideload") relationships can be expressed with the include[] query parameter.

For example, in order to fetch a user and sideload their groups:

-->
    GET /users/1/?include[]=groups.*
<--
    200 OK
    {
        "user": {
            "id": 1,
            "name": "John",
            "location": 1,
            "groups": [1, 2]
        },
        "groups": [{
            "id": 1,
            "name": "Family",
            "location": 2
        }, {
            "id": 2,
            "name": "Work",
            "location": 3
        }]
    }

The "user" portion of the response looks nearly identical to the first example; the user is returned top-level, and the groups are represented by their IDs. However, instead of including a link to the groups endpoint, the group data is present within the respones itself, under a top-level "groups" key.

Note that each group itself contains relationships to "location", which are linked in this case.

With DREST, it is possible to sideload as many relationships as you'd like, as deep as you'd like.

For example, to obtain the user with groups, locations, and groups' locations all sideloaded in the same response:

-->
    GET /users/1/?include[]=groups.location.*&include[]=location.*
<--
    200 OK
    {
        "user": {
            "id": 1,
            "name": "John",
            "location": 1,
            "groups": [1, 2]
        },
        "groups": [{
            "id": 1,
            "name": "Family",
            "location": 2,
        }, {
            "id": 2,
            "name": "Work",
            "location": 3,
        }],
        "locations": [{
            "id": 1,
            "name": "New York"
        }, {
            "id": 2,
            "name": "New Jersey"
        }, {
            "id": 3,
            "name": "California"
        }]
    }

Embedded relationships

If you want your relationships loaded eagerly but don't want them sideloaded in the top-level, you can instruct your serializer to embed relationships instead.

In that case, the demo serializer above would look like this:

# The Serializer
class UserSerializer(DynamicModelSerializer):
    class Meta:
        model = User
        name = 'user'
        fields = ("id", "name", "location", "groups")

    location = DynamicRelationField('LocationSerializer', embed=True)
    groups = DynamicRelationField('GroupSerializer', embed=True, many=True)

... and the call above would return a response with relationships embedded in place of the usual ID representation:

-->
    GET /users/1/?include[]=groups.*
<--
    200 OK
    {
        "user": {
            "id": 1,
            "name": "John",
            "location": 1,
            "groups": [{
                "id": 1,
                "name": "Family",
                "location": 2
            }, {
                "id": 2,
                "name": "Work",
                "location": 3
            }]
        }
    }

In DREST, sideloading is the default because it can produce much smaller payloads in circumstances where related objects are referenced more than once in the response.

For example, if you requested a list of 10 users along with their groups, and those users all happened to be in the same groups, the embedded variant would represent each group 10 times. The sideloaded variant would only represent a particular group once, regardless of the number of times that group is referenced.

Inclusions

You can use the include[] feature not only to sideload relationships, but also to load basic fields that are marked "deferred".

In DREST, any field or relationship can be marked deferred, which indicates to the framework that the field should only be returned when requested by include[]. This could be a good option for fields with large values that are not always relevant in a general context.

For example, a user might have a "personal_statement" field that we would want to defer. At the serializer layer, that would look like this:

# The Serializer
class UserSerializer(DynamicModelSerializer):
    class Meta:
        model = User
        name = 'user'
        fields = ("id", "name", "location", "groups", "personal_statement")
        deferred_fields = ("personal_statement", )

    location = DynamicRelationField('LocationSerializer')
    groups = DynamicRelationField('GroupSerializer', many=True)

This field will only be returned if requested:

-->
    GET /users/1/?include[]=personal_statement
<--
    200 OK
    {
        "user": {
            "id": 1,
            "name": "John",
            "location": 1,
            "groups": [1, 2],
            "personal_statement": "Hello, my name is John and I like........",
            "links": {
                "groups": "/users/1/groups"
            }
        }
    }

Note that include[]=personal_statement does not have a . following the field name as in the previous examples for embedding and sideloading relationships. This allows us to differentiate between cases where we have a deferred relationship and want to include the relationship IDs as opposed to including and also sideloading the relationship.

For example, if the user had a deferred "events" relationship, passing include[]=events would return an "events" field populated by event IDs, passing include[]=events. would sideload or embed the events themselves, and by default, only a link to the events would be returned. This can be useful for large has-many relationships.

Exclusions

Just as deferred fields can be included on demand with the include[] feature, fields that are not deferred can be excluded with the exclude[] feature. Like include[], exclude[] also supports multiple values and dot notation to allow you to exclude fields on sideloaded relationships.

For example, if we want to fetch a user with his groups, but ignore the groups' location and user's location, we could make a request like this:

-->
    GET /users/1/?include[]=groups.*&exclude[]=groups.location&exclude[]=location
<--
    200 OK
    {
        "user": {
            "id": 1,
            "name": "John",
            "groups": [1, 2],
            "links": {
                "location": "/users/1/location"
            }
        },
        "groups": [{
            "id": 1,
            "name": "Family",
            "links": {
                "location": "/groups/1/location"
            }
        }, {
            "id": 2,
            "name": "Work",
            "links": {
                "location": "/groups/2/location"
            }
        }]
    }

exclude[] supports the wildcard value: *, which means "don't return anything". Why is that useful? include[] overrides exclude[], so exclude[]=* can be combined with include[] to return only a single value or set of values from a resource.

For example, to obtain only the user's name:

-->
    GET /users/1/?exclude[]=*&include[]=name
<--
    200 OK
    {
        "user": {
            "name": "John",
            "links": {
                "location": "/users/1/location",
                "groups": "/users/1/groups"
            }
        }
    }

Note that links will always be returned for relationships that are deferred.

Filtering

Tired of writing custom filters for all of your fields? DREST has your back with the filter{} feature.

You can filter a user by his name (exact match):

-->
    GET /users/?filter{name}=John
<--
    200 OK
    ...

... or a partial match:

-->
   GET /users/?filter{name.icontains}=jo
<--
    200 OK
    ...

... or one of several names:

-->
    GET /users/?filter{name.in}=John&filter{name.in}=Joe
<--
    200 OK

... or a relationship ID:

-->
    GET /users/?filter{groups}=1
<--
    200 OK

... or lack thereof:

-->
    GET /users/?filter{-groups}=1
<--
    200 OK

... or a relationship field:

-->
    GET /users/?filter{groups.name}=Home
<--
    200 OK

... or multiple criteria:

-->
    GET /users/?filter{groups.name}=Home&filter{name}=John
<--
    200 OK

... or combine it with include[] to filter the sideloaded data (get all the users and only sideload certain groups):

-->
    GET /users/?include[]=groups.*&filter{groups|name.icontains}=h
<--
    200 OK

The sky is the limit! DREST supports just about every basic filtering scenario and operator that you can use in Django:

  • in
  • icontains
  • istartswith
  • range
  • lt
  • gt ...

See the full list here.

Ordering

You can use the sort[] feature to order your response by one or more fields. Dot notation is supported for sorting by nested properties:

-->
    GET /users/?sort[]=name&sort[]=groups.name
<--
    200 OK
    ...

For descending order, simply add a - sign. To sort by name in descending order for example

-->
    GET /users/?sort[]=-name
<--
    200 OK
    ...

Directory panel

We love the DRF browsable API, but wish that it included a directory that would let you see your entire list of endpoints at a glance from any page. DREST adds that in:

Directory panel

Optimizations

Supporting nested sideloading and filtering is expensive and can lead to very poor query performance if implemented naively. DREST uses Django's Prefetch object to prevent N+1 query situations and guarantee that your API is performant. We also optimize the serializer layer to ensure that the conversion of model objects into JSON is as fast as possible.

How fast is it? Here are some benchmarks that compare DREST response time to DRF response time. DREST out-performs DRF on every benchmark:

Linear benchmark: rendering a flat list Linear Benchmark

Quadratic benchmark: rendering a list of lists Quadratic Benchmark

Cubic benchmark: rendering a list of lists of lists Cubic Benchmark

Settings

DREST is configurable, and all settings should be nested under a single block in your settings.py file. Here are our defaults:

DYNAMIC_REST = {
    # DEBUG: enable/disable internal debugging
    'DEBUG': False,

    # ENABLE_BROWSABLE_API: enable/disable the browsable API.
    # It can be useful to disable it in production.
    'ENABLE_BROWSABLE_API': True,

    # ENABLE_LINKS: enable/disable relationship links
    'ENABLE_LINKS': True,

    # ENABLE_SERIALIZER_CACHE: enable/disable caching of related serializers
    'ENABLE_SERIALIZER_CACHE': True,

    # ENABLE_SERIALIZER_OPTIMIZATIONS: enable/disable representation speedups
    'ENABLE_SERIALIZER_OPTIMIZATIONS': True,

    # DEFER_MANY_RELATIONS: automatically defer many-relations, unless
    # `deferred=False` is explicitly set on the field.
    'DEFER_MANY_RELATIONS': False,

    # MAX_PAGE_SIZE: global setting for max page size.
    # Can be overriden at the viewset level.
    'MAX_PAGE_SIZE': None,

    # PAGE_QUERY_PARAM: global setting for the pagination query parameter.
    # Can be overriden at the viewset level.
    'PAGE_QUERY_PARAM': 'page',

    # PAGE_SIZE: global setting for page size.
    # Can be overriden at the viewset level.
    'PAGE_SIZE': None,

    # PAGE_SIZE_QUERY_PARAM: global setting for the page size query parameter.
    # Can be overriden at the viewset level.
    'PAGE_SIZE_QUERY_PARAM': 'per_page',

    # ADDITIONAL_PRIMARY_RESOURCE_PREFIX: String to prefix additional
    # instances of the primary resource when sideloading.
    'ADDITIONAL_PRIMARY_RESOURCE_PREFIX': '+',

    # Enables host-relative links.  Only compatible with resources registered
    # through the dynamic router.  If a resource doesn't have a canonical
    # path registered, links will default back to being resource-relative urls
    'ENABLE_HOST_RELATIVE_LINKS': True
}

Compatibility

We actively support the following:

  • Python: 3.6, 3.7, 3.8
  • Django: 2.2, 3.1, 3.2
  • Django Rest Framework: 3.11, 3.12

Note: Some combinations are not supported. For up-to-date information on actively supported/tested combinations, see the tox.ini file.

Contributing

See Contributing.

License

See License.

dynamic-rest's People

Contributors

aleontiev avatar alexmadjar avatar amandeep-r avatar apo5tol avatar atzannes avatar ben-prezola avatar benbardin avatar chdastolfo avatar d2rk avatar deviatefish avatar dpaluch-rp avatar erikdbrown avatar ernestofgonzalez avatar gitter-badger avatar imposeren avatar ivlevdenis avatar jackatomenapps avatar joshvillbrandt avatar jtszalay avatar jvineet avatar markine avatar notsoluckycharm avatar patdujour avatar ryochiji avatar sganeriwal avatar shanx avatar simkimsia avatar suavesav avatar thomaswrenn avatar vmihalachi 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  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

dynamic-rest's Issues

Include wildcard does not include deferred fields

My assumption, which remains unaltered after reading the docs, would be that any deferred fields on a serializer would be included if the query had a parameter like include[]=*. Does my assumption jive with the intended behavior?

I attempted a search for any obvious bug in the code base but gave up after awhile. I did however land on the following workaround, which for anyone interested is code you should add to your ViewSet class. The basic idea behind this workaround is to intercept any wildcards and expand them to explicit field includes/excludes as that all seems to work great. Note, my datastructures are trees not graphs so I didn't bother to guard against the infinite recursion that will happen if you try this code with some serializers that reference each other...

`

def get_request_fields( self ):
    if hasattr( self, '_exp_request_fields' ):
        return self._exp_request_fields
    request_fields = self._expand_wildcards( super( DataViewSet, self ).get_request_fields( ), self.serializer_class )
    self._exp_request_fields = request_fields
    return request_fields

def _expand_wildcards( self, req_fields, serializer_cls ):
    if '*' in req_fields:
        for field in serializer_cls.Meta.fields:
            if ( field not in serializer_cls._declared_fields or
                    not isinstance( serializer_cls._declared_fields[field], DynamicRelationField ) ):
                req_fields[field] = req_fields.get( field, True ) and req_fields['*']
            else:
                embed = req_fields.setdefault( field, { } )
                if isinstance( embed, dict ):
                    embed['*'] = embed.get( '*', True ) and req_fields['*']
        del req_fields['*']
    for rf, requested in req_fields.items( ):
        if isinstance( requested, dict ):
            self._expand_wildcards( requested, serializer_cls._declared_fields[rf].serializer_class )
    return req_fields

`

Possibility to restrict ViewSet actions?

First of all, this package seems very promising and maybe I´m just misunderstanding how it works...but:

Is there a possibility to restrict which ViewSet actions that the DynamicViewSet class exposes? Basically I would just like to make use of the include feature on one of my current ViewSet actions (e.g. retrieve). As I understand I would need your Serializer, Viewset, and Router to make this work? And that would make my current ViewSet (DRF) a complete CRUD ViewSet which is not what I want since it currently only holds the retrieve action.

In my example I´m fetching a User object and I would like to expand (include) the relationship that the user has to its set of permissions and groups...just that, nothing more (at the moment at least).

no Directory Panel

I installed the demo site but the directory panel isn't showing up. Confirmed that the following is in settings.py per the documentation.

REST_FRAMEWORK = {
    'PAGE_SIZE': 50,
    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework.renderers.JSONRenderer',
        'dynamic_rest.renderers.DynamicBrowsableAPIRenderer'
    )
}

DynamicRelationField ignores queryset param

The code below always returns all the units associated with the curriculum being serialized, and ignores the filter.

class CurriculumSerializer(DynamicModelSerializer):
    units = DynamicRelationField('UnitSerializer', many=True, queryset=Unit.objects.filter(id=1))

    class Meta:
        model = Curriculum
        name = 'curriculum'
        fields = ('id', 'units')


class UnitSerializer(DynamicModelSerializer):
    class Meta:
        model = Unit
        name = 'unit'
        fields = ('id',)

When offloading the queryset to a function with an embedded print or ipdb, such as

def get_unit_queryset(field):
    print 'IS THIS EVER HAPPENING?"
    return Unit.objects.filter(id=1)

the result is the same and the print statement is never executed. The function is never called.

Warnings on old versions of django / drf

Hello, currently I work on support for django 2.0 and drf 3.7.
I update tests and circle config.

I had several warnings for unsupported versions od django and django-rest-framework.
Maybe support for unsupported django and rest framework versions should be droped for future development and maintance simplification.

Ps.
I think Django 1.8, 1.11 and 2.0 should be supported now.

Sideloaded object not have all included fields

Hello I have a problem with sideloading, when I nest Serializer and include the same object multiple time, but with another fields not all fields are returned.

Location (pk 2) is included in Car (field: short_name) and Part (field: name) but in sideloaded object sometimes short_name is returned and sometimes name never both.

Example:

# models.py

class Location(models.Model):
    name = models.CharField(max_length=60)
    short_name = models.CharField(max_length=30)
    description = models.TextField()

class Car(models.Model):
    name = models.CharField(max_length=60)
    location = models.ForeignKey(Location)


class Part(models.Model):
    car = models.ForeignKey(Car)
    name = models.CharField(max_length=60)
    location = models.ForeignKey(Location)
# serializers.py

class LocationSerializer(DynamicModelSerializer):
    name = serializers.CharField(deferred=True)
    short_name = serializers.CharField(deferred=True)
    description = serializers.CharField(deferred=True)
    location = serializers.DynamicRelationField('LocationSerializer', many=True, deferred=True)


    class Meta:
        model = Location 

class PartSerializer(DynamicModelSerializer):
    name = serializers.CharField(deferred=True)
    description = serializers.CharField(deferred=True)
    location = serializers.DynamicRelationField('LocationSerializer', many=True, deferred=True)

    class Meta:
        model = Car

class CarSerializer(DynamicModelSerializer):
    parts = serializers.DynamicRelationField('PartSerializer', many=True, deferred=True)
    name = serializers.CharField(deferred=True)
    description = serializers.CharField(deferred=True)

    class Meta:
        model = Car
# viewsets.py

class CarViewset(DynamicModelViewset):
    serializer_class = CarSerializer

For query:

localhost:8080/api/cars/1/?include[]=name&include[]=location.short_name&include[]=parts.name&include[]=parts.location.name

I radomly get one of responses:

{
    "cars": [
        {
            "id": 1,
            "name": "Porshe",
            "location": 2
        }
    ],
    "locations": [
        {
            "id": 1
            "name": "China",
        },
        {
            "id": 2
            "short_name": "US",
        }
    ],
    "parts": [
      {
        "id": 2,
        "name": "wheel",
        "location": 2
      },
      {
        "id": 2,
        "name": "tire",
        "location": 1
      }
    ]
}

or

{
    "cars": [
        {
            "id": 1,
            "name": "Porshe",
            "location": 2
        }
    ],
    "locations": [
        {
            "id": 1
            "name": "China",
        },
        {
            "id": 2
            "name": "United States",
        }
    ],
    "parts": [
      {
        "id": 2,
        "name": "wheel",
        "location": 2
      },
      {
        "id": 2,
        "name": "tire",
        "location": 1
      }
    ]
}

But I think correct response is like bellow

{
    "cars": [
        {
            "id": 1,
            "name": "Porshe",
            "location": 2
        }
    ],
    "locations": [
        {
            "id": 1
            "name": "China",
        },
        {
            "id": 2
            "name": "United States",
            "short_name": "Us",
        }
    ],
    "parts": [
      {
        "id": 2,
        "name": "wheel",
        "location": 2
      },
      {
        "id": 2,
        "name": "tire",
        "location": 1
      }
    ]
}

I propose this way for fix that: ( wrapped in # SIDELOADING FIX # comments)

# processors.py

"""This module contains response processors."""

from django.utils import six
from dynamic_rest.processors import SideloadingProcessor as BaseSideloadingProcessor
from rest_framework.utils.serializer_helpers import ReturnDict

from dynamic_rest.conf import settings

class SideloadingProcessor(BaseSideloadingProcessor):
    """A processor that sideloads serializer data.

    Sideloaded records are returned under top-level
    response keys and produces responses that are
    typically smaller than their nested equivalent.
    """
    def process(self, obj, parent=None, parent_key=None, depth=0):
        """Recursively process the data for sideloading.

        Converts the nested representation into a sideloaded representation.
        """
        if isinstance(obj, list):
            for key, o in enumerate(obj):
                # traverse into lists of objects
                self.process(o, parent=obj, parent_key=key, depth=depth)
        elif isinstance(obj, dict):
            dynamic = self.is_dynamic(obj)
            returned = isinstance(obj, ReturnDict)
            if dynamic or returned:
                # recursively check all fields
                for key, o in six.iteritems(obj):
                    if isinstance(o, list) or isinstance(o, dict):
                        # lists or dicts indicate a relation
                        self.process(
                            o,
                            parent=obj,
                            parent_key=key,
                            depth=depth +
                            1
                        )

                if not dynamic or getattr(obj, 'embed', False):
                    return

                serializer = obj.serializer
                name = serializer.get_plural_name()
                instance = getattr(obj, 'instance', serializer.instance)
                instance_pk = instance.pk if instance else None
                pk = getattr(obj, 'pk_value', instance_pk) or instance_pk

                # For polymorphic relations, `pk` can be a dict, so use the
                # string representation (dict isn't hashable).
                pk_key = repr(pk)

                # sideloading
                seen = True
                # if this object has not yet been seen
                if pk_key not in self.seen[name]:
                    seen = False
                    self.seen[name].add(pk_key)

                # prevent sideloading the primary objects
                if depth == 0:
                    return

                # TODO: spec out the exact behavior for secondary instances of
                # the primary resource

                # if the primary resource is embedded, add it to a prefixed key
                if name == self.plural_name:
                    name = '%s%s' % (
                        settings.ADDITIONAL_PRIMARY_RESOURCE_PREFIX,
                        name
                    )

                if not seen:
                    # allocate a top-level key in the data for this resource
                    # type
                    if name not in self.data:
                        self.data[name] = []

                    # move the object into a new top-level bucket
                    # and mark it as seen
                    self.data[name].append(obj)

                # SIDELOADING FIX #
                if seen:
                    ob = [o for o in self.data[name] if o.instance.pk == pk][0]
                    ob.update(obj)
                    self.data[name] = [ob if o.instance.pk == pk else o for o in self.data[name]]
                # SIDELOADING FIX #

                # replace the object with a reference
                if parent is not None and parent_key is not None:
                    parent[parent_key] = pk

how to filter if filter as OR across two or more fields for same model?

Let’s say I want to run a filter like this

User.objects.filter(Q(username="admin") | Q(first_name="test"))

I understand drest does not support this in the end point.

How can I modify so as to allow something like this in the endpoint? I am okay to make a PR for this. I just want to get some guidance so I can conform to drest architecture

DRF renderers ignored

https://github.com/AltSchool/dynamic-rest/blob/master/dynamic_rest/viewsets.py#L78 overrides the REST_FRAMEWORK['DEFAULT_RENDERER_CLASSES'] setting. Why? Also, why is there a DYNAMIC_REST setting to disable the browsable API?
I think the easier solution would be using the DRF settings. To enable the dynamic browsable API, just add it to these settings. Like:

    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework.renderers.JSONRenderer',
        'dynamic_rest.renderers.DynamicBrowsableAPIRenderer',
        'myproject.renderers.MyRenderer',
    ),

circle CI broken

circle CI fails on Dependencies > pyenv local 2.7.10 3.3.3 3.4.3 3.5.0 with pyenv: version '3.3.3' not installed (same goes for 3.5.0 if you fix that). Has there been some change on Circle CI?

This can be fixed by installing the versions before using them. Also, I would suggest testing the newest versions of Py2 and Py3.

This circle.yml works, though I think we could omit some of these versions to speed up the build:

dependencies:
    override:
        - pip install -r requirements.txt
        - python setup.py develop
        - pyenv install -s 2.7.13
        - pyenv install -s 3.3.3
        - pyenv install -s 3.4.3
        - pyenv install -s 3.5.0
        - pyenv install -s 3.6.1
        - pyenv local 2.7.13 3.3.3 3.4.3 3.5.0 3.6.1

test:
    override:
        - ENABLE_INTEGRATION_TESTS=True py.test tests/integration
        - tox

DynamicRelationField not detected

I am using dynamic-rest. According to the documentation, I have this model with a dynamically generated foreign key:

    class PscSerializer(DynamicModelSerializer):
    
        class Meta:
            model = PSC
            name = 'psc'
            fields = ('id', 'description', 'created', 'modified', 'pscno', 'productno', 'serviceno', 'luns')
    
        luns = DynamicRelationField('LunSerializer', many=True)

Which produces:

Exception Type: AttributeError at /api/v1/pscs/
Exception Value: luns is not a valid field for <class 'pscs.models.PSC'>

Sure, luns is not a field in the PSC model, but a dynamic field. What is going on? What am I doing wrong here?

I am using:

Django==2.0.4
djangorestframework==3.7.7
dynamic-rest==1.8.0

And:

» python --version
Python 3.6.4

Taggit Prefetching

I'm using the Taggit library (https://github.com/alex/django-taggit) and have run into an issue when exposing tags on one of my serializers:

The field looks like this :

tags = DynamicRelationField(TagSerializers,
                many=True,
                read_only=True)

It blows up with this error from the taggit object manager: https://github.com/alex/django-taggit/blob/master/taggit/managers.py#L100

I've tried adding requires=['tags.*', '*'], but I get the same error.

  1. Why does DREST set the prefect queryset?
  2. Is there a way to not set the prefect queryset?

Thanks!

Any patreon for me to contribute to? ❤️ this project

I love this project. ❤️

I may start using this open source library in commercial projects. When I do receive revenue, I like to give back via some kind of patreon or some kind of direct contribution.

I am not talking about huge amounts. Maybe a $5 or $10 every once in a while.

Thank you for doing dynamic-rest

Conflict with filters

I am having problems with how drf, dynamic-rest and django_filters are playing together.

I have:

Python 3.6.4
Django==2.0.4
djangorestframework==3.7.7
dynamic-rest==1.8.1
django-filter==1.1.0

I have this defined:

    INSTALLED_APPS = (
        ...
        'rest_framework',            # utilities for rest apis
        'dynamic_rest',
        'django_filters',            # for filtering rest endpoints
        ...
    }

And this:

    REST_FRAMEWORK = {
        'DEFAULT_FILTER_BACKENDS': (
            'django_filters.rest_framework.DjangoFilterBackend',
        ),
    }

This does not work:

class PscViewSet(DynamicModelViewSet):
    queryset = PSC.objects.all()
    serializer_class = PscSerializer
    permission_classes = (AllowAny,)
    filter_fields = ('owner',)

No filtering is possible, and the Filters button is not shown in the browsable API.

Doing this:

class PscViewSet(DynamicModelViewSet):
    queryset = PSC.objects.all()
    serializer_class = PscSerializer
    permission_classes = (AllowAny,)
    filter_backends = (filters.DjangoFilterBackend,)   # This added
    filter_fields = ('owner',)

allows for filtering, but no Filters button is shown in the browsable API.

Doing this:

class PscViewSet(viewsets.ModelViewSet):
    queryset = PSC.objects.all()
    serializer_class = PscSerializer
    permission_classes = (AllowAny,)
    filter_fields = ('owner',)

allows for filtering and shows a Filters button in the browsable API.

Infinite recursion loop when using DynamicRelationField with embed=True

I have two serializer classes: FolderSerializer and FileSerializer, see below. I want to include the full representation of any parent folder by default in subfolders, but in doing so I get an infinite recursion loop in filters.py going from line 339 -> line 405 -> line 495 -> line 339 etc. I do not have a loop in my object hierarchy, even when visiting the detail page of a folder without parent (=None) I get this issue. Setting a depth on the Meta class does not help, when changing to the reverse relation (including subfolders in parents) I get a stack overflow error in python resulting in 'Abort trap: 6'.

class FolderSerializer(BaseSerializer):
    files = DynamicRelationField("FileSerializer", many=True, deferred=True)
    parentFolder = DynamicRelationField("FolderSerializer", embed=True)

    class Meta:
        model = Folder
        fields = ('id', 'parentFolder', 'name', 'files')
        read_only_fields = ('id', 'parentFolder', 'files')

class FileSerializer(BaseSerializer):
    class Meta:
        model = File
        fields = ('id',)     // Just for testing, only include id

class Folder(models.Model):
    id = models.IntegerField(primary_key=True)
    owner = models.ForeignKey("User", related_name="folders")
    parentFolder = models.ForeignKey('Folder', related_name='subfolders', blank=True, null=True)
    name = models.CharField(max_length=40, default="Unnamed folder")

Atomic sideloading

Hi!

I am using dynamic-rest to support an application that requires atomic data consistency when performing API requests with sideloading. TL;DR: I think I need to be using select_related instead of prefetch_related when sideloading, but it doesn't seem like this is an option. Thoughts?

My research so for: Per your sideloading documentation, Django supports loading relationships using prefetch_related and select_related. From the Django docs I see that select_related produces a single SQL statement with JOINs, but is limited to foreign keys and one-to-one relationships while prefetch_related uses many SQL statements with joins on the Python side and because of this can also handle many-to-many and many-to-one relationships.

A cursory glance at the dynamic-rest code suggests that prefetch_related is being used to support sideloading. The upside to this is that you can support a wider selection of foreign key types. However, the downside is that using multiple queries to support sideloading means that the state of the database may have changed between performing the two queries.

So, I think I need to be using select_related instead of prefetch_related when sideloading, but it doesn't seem like this is an option. Am I on the write track in that this currently not supported by Dynamic REST? Do you have plans to support this? Would you be open to me adding this feature as a configurable option?

Alternatively, maybe I can wrap GET requests in a transaction? (Would this work the way I want it to work?) I know that Django has the ATOMIC_REQUESTS option, but as the docs state this can come with a significant performance impact.

(PS: Thanks in advanced for your time! We've really enjoyed using Dynamic REST to get our API up and running in a flash.)

Filtering on annotated fields

I annotate the count of a relation in my queryset ...

    queryset =  Location.objects.all.annotate(cats_count=Count('cats'))

... and add the field to the serializer ...

    cats_count = serializers.IntegerField(read_only=True)

This works fine, I can filter on locations and cats. But I can not filter on cats_count, like &filter{casts_count.gt}=0 (I want do do this dynamically, so I can include or exclude zero-results). But this raises an AttributeError: cats_count is not a valid field for <class 'models.Location'> because generate_query_key only checks for model fields, not serializer fields https://github.com/AltSchool/dynamic-rest/blob/master/dynamic_rest/filters.py#L116

Any idea how I to approach this?

Support for POST/PUT/PATCH/DELETE on nested relationships?

First of all, love this project so far!

From what I can determine from looking at https://github.com/AltSchool/dynamic-rest/blob/master/dynamic_rest/routers.py, it doesn't appear as though there's any support for managing nested relationships. Is this correct? If so, is there any intention on supporting this?

We are currently using drf-extensions to support nested routes, however I'd love to be able to rely solely on drf and dynamic-rest.

ordering ignores get_serializer_class method

https://github.com/AltSchool/dynamic-rest/blob/master/dynamic_rest/filters.py#L656
This gets the serializer_class attribute from the view. I prefer using the get_serializer_class method instead of the serializer_class attribute (for some complicated but valid reason ^^).
DRF first tries to use the method before falling back to the attribute, Dynamic REST should behave the same:
https://github.com/encode/django-rest-framework/blob/master/rest_framework/filters.py#L222

I would suggest using DRF's get_default_valid_fields method which (unlike get_valid_fields) seems to return serializer fields.

I could easily fix this, though I'm not sure about how to write a test for it (or if that is even necessary).

No support for multiple filters against the same field

Multiple values for the same filter do not seem to be supported; eg. if my query looks like this: ?filter{-option}=value1&filter{-option}=value2 to exclude two different values from the results, only the first is respected.

Dependency problem when installing dynamic-rest 1.8.0 with django 2.0.7

I am not sure what is going on, since dynamic-rest 1.8.0 is supposed to be compatible with django 2.0.7:

» poetry add dynamic-rest==1.8.0

Updating dependencies
Resolving dependencies... (0.2s)
                                                                                     
[SolverProblemError]                                                  
Because dynamic-rest (1.8.0) depends on Django (>=1.8,<2.0)                        
 and poetry-demo depends on Django (^2.0.7), dynamic-rest is forbidden.            
So, because poetry-demo depends on dynamic-rest (=1.8.0), version solving failed.  
                                                                                     
add [-D|--dev] [--git GIT] [--path PATH] [-E|--extras EXTRAS] [--optional] [--python PYTHON] [--platform PLATFORM] [--allow-prereleases] [--dry-run] [--] <name> (<name>)...

I am on python 3.6.5:

» python --version
Python 3.6.5

Allow only specific methods with DynamicModelViewSet

Sometimes views allow certain methods and not others. You can whitelist the ones you want in DRF with, for instance:

class MyView(
        mixins.UpdateModelMixin,
        viewsets.GenericViewSet):

instead of using ModelViewSet which incorporates all actions.

How would you replicate this with a DynamicViewSet?

one-to-many relations aren't deferred for PUT/PATCH

List of (one-to-many) related ids is returned for PUT/PATCH even though DynamicRelationField is deferred both in settings and in serializer definition. This is a big problem if you imagine e.g. a User having thousands of Transactions, then when you want to (partially) update User (e.g. change his/her name), you get back the complete list of his/her Transaction ids.

I've created a minimal project reproducing this issue here. You can see that the relation field is deffered both in settings and in serializer. When you create a Parent record with some Child records and then try to PATCH the Parent record, related Child ids are included in PATCH response. Should I try to investigate further or is this for some reason the desired behavior?

AltSchool closing and support for dynamic-rest

First of all, awesome work on dynamic-rest. This extension definitely provides some essential functionalities the django rest framework should offer natively.
We've been thinking about using dynamic-rest, but recently discovered this article What does this mean in terms of maintaining this library going forward? It seems like the support for the extension is already slowing down(?) #195 I just want to hear your honest opinion on its future so we can make sensible decisions.

ordering direction

Hi,
I know this isn't the right place for Q&A, but I haven't found any documented place to ask questions, so I'm asking here. Is there a way to dynamically sort records in descending order, e.g. something like /clients?sort[]=name.desc&page=2&per_page=5 (this doesn't work obviously). I could sort the records on the client side, but that doesn't cover the pagination case.

Too restrictive versioning in install_requires.txt

Nice project. I tried it right about now because of the DynamicRelationField.

When I added dynamic-rest to my project via pip install dynamic-rest, it downgraded Django to 1.9 and REST Framework to 3.3.0. The latest versions are 1.9.7 and 3.3.3 respectively. Django has had both bug- and vulnerability patches in 1.9 release, so it would be nice to get the latest patch.

As I'm guessing this is because of these lines in install_requires.txt:

Django>=1.7,<=1.9
djangorestframework>=3.1.0,<=3.3.0

Could you for instance update this to:

Django>=1.7,<1.10
djangorestframework>=3.1.0,<3.4.0

Thanks.

Unable to sort and filter JSON fields

I'm looking to expand the functionality to handle sorting and filtering for JSON field data.

Would like this to work using PostgresSQL database's built-in jsonb object which allows indexing. Would involve augmenting one of the models to include nested JSON. Here are the options I came up with:

  1. Treat the JSON field like a text field
  2. Convert the tests to Postgres
  3. Test two configurations, one with sqlite, one with postgres
  4. We could maintain a fork of the project

https://docs.djangoproject.com/en/2.1/ref/contrib/postgres/fields/

Can you guys update pypi version?

I saw the latest version is v1.5.7, howerver, the latest version I can install from pip command is v1.5.0.
I would appreciate if you update pypi version, thanks!

relationship endpoints don't work for deferred fields

Nested relationship endpoints like /events/5/users/ don't work when the DynamicRelationField is deferred, since deferred fields are removed from serializer.fields here and DynamicModelViewSet.list_related() raises ValidationError if the field isn't found in serializer.fields here. The deferred fields are included in serializer.get_all_fields(), but I have no idea if it's safe to use that instead of serializer.fields in DynamicModelViewSet.list_related().

Primary resource (not sideloaded) not have all included fields

This issue is in connection with #176 issue
Unfortunately, there is another case not covered by previous issue PR:

# models.py

class Region(models.Model):
    name = models.CharField(max_length=60)
    short_name = models.CharField(max_length=30)
    parent = models.ForeignKey('Region')

localhost:8080/api/regions/?include[]=name&include[]=parent&include[]=parent.short_name

Output is:

{
    "+regions": [
        {
            "id": 3,
            "short_name": "LBL",
        }
    ],
    "regions": [
        {
            "id": 1
            "name": "Opolskie",
            "parent": null
        },
        {
            "id": 2
            "name": "Opole",
            "parent": 1
        },
       {
           "id": 4,
           "name": Lublin,
           "parent": 3
       }
    ]
}

But should be:

{
    "+regions": [
        {
            "id": 3,
            "short_name": "LBL",
        }
    ],
    "regions": [
        {
            "id": 1
            "name": "Opolskie",
            "short_name": "OPO", 
            "parent": null
        },
        {
            "id": 2
            "name": "Opole",
            "parent": 1
        },
       {
           "id": 4,
           "name": Lublin,
           "parent": 3
       }
    ]
}

At this moment I don't have idea how to modify processors.py to cover this case.

Sorting parameter not works with deferred fields

If I declare one field as deferred I cannot use it as sort parameter,

For example:

class AnimalSerializer(DynamicModelSerializer):

    class Meta:
        model = Animal
        fields =  "__all__"
        deferred_fields = ('boughtOn',)

When I try to call myapi.com/animal/?include[]=boughtOn&sort[]=boughtOn I get the follow message:

	"Invalid filter field: [u'boughtOn']"

I debug the dynamic-rest and it only validates the sort fields with not deferred fields in the serializer.

return structure of DynamicSortingFilter.get_valid_fields does not match djangorestframework's OrderingFilter.get_valid_fields

hi,

I found a bug if make a search with a field that does not exists in the serializer : /?filter{fieldnotinserializer}=val.

ValueError: too many values to unpack

it seem that the signature of DynamicSortingFilter.get_valid_fields does not match his parent class's.
the original result from django-rest-framework is a list of 2n tuple, but you return a list of 3n tuple.

it seem that this broke the OrderingFilter.get_template_context method since the commit aeb57913c9f2a7ad1ffc1db825c3e6f44b4818ee of restframowork which do no longer pass the result of get_valid_fields but iterate over it instead.

this happen by browsing the django-rest-framework interface

version used :

  • djangorestframework==3.4.0
  • dynamic-rest==1.5.0

Non-existent foreign key value in POST/PATCH/PUT should throw a 400 (validation error) and not a 404 (not found)

I have models that look something like this:

class MyModelA(django.db.models.Model):
    name = models.CharField(unique=True, max_length=20)


class MyModelB(ExtraDataModel):
    name = models.CharField(unique=True, max_length=20)
    my_model_a = models.ForeignKey(MyModelA, on_delete=models.CASCADE)

And serializers that set up foreign key fields using DynamicRelationField. If I make a POST to /my_model_b/ with a my_model_a id corresponding to a non-existent MyModelA, then I get a 404 response code back. This seems weird, because the endpoint that I am communicating with, /my_model_b/, definitely exists. I would instead classify this as a validation error.

I've found where the NotFound exception is being thrown by catching the exception in the DynamicModelViewSet create method:

Traceback (most recent call last):
  File "/Users/jvillbrandt/repos/asrs/app/urls.py", line 77, in create
    response = super(ViewSet, self).create(request, *args, **kwargs)
  File "/Users/jvillbrandt/repos/asrs/env/lib/python2.7/site-packages/dynamic_rest-1.6.7-py2.7.egg/dynamic_rest/viewsets.py", line 533, in create
    request, *args, **kwargs)
  File "/Users/jvillbrandt/repos/asrs/env/lib/python2.7/site-packages/djangorestframework-3.6.2-py2.7.egg/rest_framework/mixins.py", line 20, in create
    serializer.is_valid(raise_exception=True)
  File "/Users/jvillbrandt/repos/asrs/env/lib/python2.7/site-packages/djangorestframework-3.6.2-py2.7.egg/rest_framework/serializers.py", line 236, in is_valid
    self._validated_data = self.run_validation(self.initial_data)
  File "/Users/jvillbrandt/repos/asrs/env/lib/python2.7/site-packages/djangorestframework-3.6.2-py2.7.egg/rest_framework/serializers.py", line 431, in run_validation
    value = self.to_internal_value(data)
  File "/Users/jvillbrandt/repos/asrs/env/lib/python2.7/site-packages/dynamic_rest-1.6.7-py2.7.egg/dynamic_rest/serializers.py", line 564, in to_internal_value
    value = super(WithDynamicSerializerMixin, self).to_internal_value(data)
  File "/Users/jvillbrandt/repos/asrs/env/lib/python2.7/site-packages/djangorestframework-3.6.2-py2.7.egg/rest_framework/serializers.py", line 461, in to_internal_value
    validated_value = field.run_validation(primitive_value)
  File "/Users/jvillbrandt/repos/asrs/env/lib/python2.7/site-packages/djangorestframework-3.6.2-py2.7.egg/rest_framework/fields.py", line 524, in run_validation
    value = self.to_internal_value(data)
  File "/Users/jvillbrandt/repos/asrs/env/lib/python2.7/site-packages/dynamic_rest-1.6.7-py2.7.egg/dynamic_rest/fields/fields.py", line 338, in to_internal_value
    return self.to_internal_value_single(data, self.serializer)
  File "/Users/jvillbrandt/repos/asrs/env/lib/python2.7/site-packages/dynamic_rest-1.6.7-py2.7.egg/dynamic_rest/fields/fields.py", line 322, in to_internal_value_single
    (related_model.__name__, data)
NotFound: 'MyModelA object with ID=0 not found

Is the 404 behavior expected in this scenario? Maybe I am doing something wrong? Otherwise, could you advise me to the best method to get this behavior to throw a 400 instead? In addition, I'd like the response body to include the name of the field that caused the error just like any other validation error.

PS: Any idea why there is a ' character in the front of the error message?

Serializer only working if Dynamically Related Models' fields are all Deferred

Hello,

I have a model with a related many-to-many model. The related model contains several nullable foreign key fields. Because I am working with Datatables, which rely on nested JSON data, I am using embedded data.

However, The base model will only serialize individual instances if the many-to-many model (i.e. the junction table) has all of its fields set to deferred=True. If I do not set the related model's fields to deferred, then I receive the following error:

'DynamicListSerializer' object has no attribute 'debug'

This happens even if I do not include[] the related many-to-many model. It happens even if I specifically exclude it as well (which seems to tell me that a query is being run, even though I thought the field was being deferred).

Here is the full text of the error report:

Environment:


Request Method: GET
Request URL: http://localhost:8000/api/cases/157/

Django Version: 1.11
Python Version: 3.4.3
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'peasantlegaldb',
 'coverage',
 'rest_framework',
 'webpack_loader',
 'dynamic_rest',
 'rest_framework_serializer_extensions',
 'django_js_reverse',
 'django_filters',
 'peasantlegaldb.templatetags',
 'widget_tweaks']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']



Traceback:

File "/home/vagrant/.virtualenvs/medievaldb/lib/python3.4/site-packages/dynamic_rest/fields/fields.py" in to_representation
  296.             return serializer.to_representation(related)

File "/home/vagrant/.virtualenvs/medievaldb/lib/python3.4/site-packages/dynamic_rest/serializers.py" in to_representation
  48.         return [self.child.to_representation(item) for item in iterable]

File "/home/vagrant/.virtualenvs/medievaldb/lib/python3.4/site-packages/dynamic_rest/serializers.py" in <listcomp>
  48.         return [self.child.to_representation(item) for item in iterable]

File "/home/vagrant/.virtualenvs/medievaldb/lib/python3.4/site-packages/dynamic_rest/serializers.py" in to_representation
  535.                 representation = self._faster_to_representation(instance)

File "/home/vagrant/.virtualenvs/medievaldb/lib/python3.4/site-packages/dynamic_rest/serializers.py" in _faster_to_representation
  518.                 ret[field.field_name] = field.to_representation(attribute)

File "/home/vagrant/.virtualenvs/medievaldb/lib/python3.4/site-packages/dynamic_rest/fields/fields.py" in to_representation
  289.                 related = getattr(instance, source)

During handling of the above exception ('Person' object has no attribute 'chevage'), another exception occurred:

File "/home/vagrant/.virtualenvs/medievaldb/lib/python3.4/site-packages/django/core/handlers/exception.py" in inner
  41.             response = get_response(request)

File "/home/vagrant/.virtualenvs/medievaldb/lib/python3.4/site-packages/django/core/handlers/base.py" in _get_response
  217.                 response = self.process_exception_by_middleware(e, request)

File "/home/vagrant/.virtualenvs/medievaldb/lib/python3.4/site-packages/django/core/handlers/base.py" in _get_response
  215.                 response = response.render()

File "/home/vagrant/.virtualenvs/medievaldb/lib/python3.4/site-packages/django/template/response.py" in render
  107.             self.content = self.rendered_content

File "/home/vagrant/.virtualenvs/medievaldb/lib/python3.4/site-packages/rest_framework/response.py" in rendered_content
  72.         ret = renderer.render(self.data, accepted_media_type, context)

File "/home/vagrant/.virtualenvs/medievaldb/lib/python3.4/site-packages/rest_framework/renderers.py" in render
  703.         context = self.get_context(data, accepted_media_type, renderer_context)

File "/home/vagrant/.virtualenvs/medievaldb/lib/python3.4/site-packages/dynamic_rest/renderers.py" in get_context
  16.             context

File "/home/vagrant/.virtualenvs/medievaldb/lib/python3.4/site-packages/rest_framework/renderers.py" in get_context
  637.         raw_data_put_form = self.get_raw_data_form(data, view, 'PUT', request)

File "/home/vagrant/.virtualenvs/medievaldb/lib/python3.4/site-packages/rest_framework/renderers.py" in get_raw_data_form
  559.                 content = renderer.render(serializer.data, accepted, context)

File "/home/vagrant/.virtualenvs/medievaldb/lib/python3.4/site-packages/dynamic_rest/serializers.py" in data
  607.             data = super(WithDynamicSerializerMixin, self).data

File "/home/vagrant/.virtualenvs/medievaldb/lib/python3.4/site-packages/rest_framework/serializers.py" in data
  531.         ret = super(Serializer, self).data

File "/home/vagrant/.virtualenvs/medievaldb/lib/python3.4/site-packages/rest_framework/serializers.py" in data
  262.                 self._data = self.to_representation(self.instance)

File "/home/vagrant/.virtualenvs/medievaldb/lib/python3.4/site-packages/dynamic_rest/serializers.py" in to_representation
  535.                 representation = self._faster_to_representation(instance)

File "/home/vagrant/.virtualenvs/medievaldb/lib/python3.4/site-packages/dynamic_rest/serializers.py" in _faster_to_representation
  518.                 ret[field.field_name] = field.to_representation(attribute)

File "/home/vagrant/.virtualenvs/medievaldb/lib/python3.4/site-packages/dynamic_rest/fields/fields.py" in to_representation
  299.             if serializer.debug:

Exception Type: AttributeError at /api/cases/157/
Exception Value: 'DynamicListSerializer' object has no attribute 'debug'

I can copy-paste my models and serializers too, if that would help.

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.