<commit_before># Django REST - FlexFields
Flexible, dynamic fields and nested models for Django REST Framework serializers. Works with both Python 2 &3

# Overview

FlexFields (DRF-FF) for [Django REST Framework](https://django-rest-framework.org) is a package designed to provide a common baseline of functionality for dynamically setting fields and nested models within DRF serializers. To remove unneeded fields, you can dynamically set fields, including nested fields, via URL parameters ```(?fields=name,address.zip)``` or when configuring serializers. Additionally, you can dynamically expand fields from simple values to complex nested models, or treat fields as "deferred", and expand them on an as-needed basis.

This package is designed for simplicity and provides two classes - a viewset class and a serializer class - with minimal magic and entanglement with DRF's foundational classes. If you are familar with Django REST Framework, it shouldn't take you long to read over the code and see how it works.

There are similar packages, such as the powerful [Dynamic REST](https://github.com/AltSchool/dynamic-rest), which does what this package does and more, but you may not need all those bells and whistles. There is also the more basic [Dynamic Fields Mixin](https://github.com/dbrgn/drf-dynamic-fields), but it lacks functionality for field expansion and dot-notation field customiziation.

Table of Contents:

- [Installation](#installation)
- [Requirements](#requirements)
- [Basics](#basics)
- [Dynamic Field Expansion](#dynamic-field-expansion)
  * [Deferred Fields](#deferred-fields)
  * [Deep, Nested Expansion](#deep-nested-expansion)
  * [Configuration from Serializer Options](#configuration-from-serializer-options)
  * [Field Expansion on "List" Views](#field-expansion-on-list-views)
  * [Use "~all" to Expand All Available Fields](#use-all-to-expand-all-available-fields)
- [Dynamically Setting Fields](#dynamically-setting-fields)
  * [From URL Parameters](#from-url-parameters)
  * [From Serializer Options](#from-serializer-options)
- [Combining Dynamically-Set Fields and Field Expansion](#combining-dynamically-set-fields-and-field-expansion)
- [Serializer Introspection](#serializer-introspection)
- [Testing](#testing)
- [License](#license)

# Installation

```
pip install drf-flex-fields
```

# Requirements

* Python (2.7, 3.2, 3.3, 3.4, 3.5)
* Django (1.8, 1.9, 1.10)

# Basics

To use this package's functionality, your viewsets need to subclass ```FlexFieldsModelViewSet``` and your serializers need to subclass ```FlexFieldsModelSerializer```:

```
from rest_flex_fields import FlexFieldsModelViewSet, FlexFieldsModelSerializer

class PersonViewSet(FlexFieldsModelViewSet):
  queryset = models.Person.objects.all()
  serializer_class = PersonSerializer

class CountrySerializer(FlexFieldsModelSerializer):
  class Meta:
    model = Country
    fields = ('id', 'name', 'population')

class PersonSerializer(FlexFieldsModelSerializer):
  class Meta:
    model = Person
    fields = ('id', 'name', 'country', 'occupation')

  expandable_fields: {
      'country': (CountrySerializer, {source: 'country'})
  }
```

Now you can make requests like ```GET /person?expand=country&fields=id,name,country``` to dynamically manipulate which fields are included, as well as expand primitive fields into nested objects. You can also use dot notation to control both the ```fields``` and ```expand``` settings at arbitrary levels of depth in your serialized responses. Read on to learn the details and see more complex examples.

# Dynamic Field Expansion

To define an expandable field, add it to the ```expandable_fields``` within your serializer:
```
class CountrySerializer(FlexFieldsModelSerializer):
    class Meta:
        model = Company
        fields = ['name', 'population']


class PersonSerializer(FlexFieldsModelSerializer):
    country = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

    class Meta:
        model = Person
        fields = ['id', 'name', 'country', 'occupation']

    expandable_fields = {
        'country': (CountrySerializer, {'source': 'country', 'fields': ['name']})
    }
```

If the default serialized response is the following:
```
{
  "id" : 13322
  "name" : "<NAME>",
  "country" : 12,
  "occupation" : "Programmer",
}
```
When you do a ```GET /person/13322?expand=country```, the response will change to:

```
{
  "id" : 13322
  "name" : "<NAME>",
  "country" : {
    "name" : "United States"
  },
  "occupation" : "Programmer",
}
```
Notice how ```population``` was ommitted from the nested ```country``` object. This is because ```fields``` was set to ```['name']``` when passed to the embedded ```CountrySerializer```. You will learn more about this later on.

## Deferred Fields
Alternatively, you could treat ```country``` as a "deferred" field by not defining it among the default fields. To make a field deferred, only define it within the serializer's ```expandable_fields```.

## Deep, Nested Expansion
Let's say you add ```StateSerializer``` as serializer nested inside the country serializer above:

```
class StateSerializer(FlexFieldsModelSerializer):
    class Meta:
        model = State
        fields = ['name', 'population']


class CountrySerializer(FlexFieldsModelSerializer):
    class Meta:
        model = Company
        fields = ['name', 'population']

    expandable_fields = {
        'states': (StateSerializer, {'source': 'country', 'many': True})
    }

class PersonSerializer(FlexFieldsModelSerializer):
    country = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

    class Meta:
        model = Person
        fields = ['id', 'name', 'country', 'occupation']<commit_msg>docs(fix): readme.md code snippets<commit_after># Django REST - FlexFields
Flexible, dynamic fields and nested models for Django REST Framework serializers. Works with both Python 2 &3

# Overview

FlexFields (DRF-FF) for [Django REST Framework](https://django-rest-framework.org) is a package designed to provide a common baseline of functionality for dynamically setting fields and nested models within DRF serializers. To remove unneeded fields, you can dynamically set fields, including nested fields, via URL parameters ```(?fields=name,address.zip)``` or when configuring serializers. Additionally, you can dynamically expand fields from simple values to complex nested models, or treat fields as "deferred", and expand them on an as-needed basis.

This package is designed for simplicity and provides two classes - a viewset class and a serializer class - with minimal magic and entanglement with DRF's foundational classes. If you are familar with Django REST Framework, it shouldn't take you long to read over the code and see how it works.

There are similar packages, such as the powerful [Dynamic REST](https://github.com/AltSchool/dynamic-rest), which does what this package does and more, but you may not need all those bells and whistles. There is also the more basic [Dynamic Fields Mixin](https://github.com/dbrgn/drf-dynamic-fields), but it lacks functionality for field expansion and dot-notation field customiziation.

Table of Contents:

- [Installation](#installation)
- [Requirements](#requirements)
- [Basics](#basics)
- [Dynamic Field Expansion](#dynamic-field-expansion)
  * [Deferred Fields](#deferred-fields)
  * [Deep, Nested Expansion](#deep-nested-expansion)
  * [Configuration from Serializer Options](#configuration-from-serializer-options)
  * [Field Expansion on "List" Views](#field-expansion-on-list-views)
  * [Use "~all" to Expand All Available Fields](#use-all-to-expand-all-available-fields)
- [Dynamically Setting Fields](#dynamically-setting-fields)
  * [From URL Parameters](#from-url-parameters)
  * [From Serializer Options](#from-serializer-options)
- [Combining Dynamically-Set Fields and Field Expansion](#combining-dynamically-set-fields-and-field-expansion)
- [Serializer Introspection](#serializer-introspection)
- [Testing](#testing)
- [License](#license)

# Installation

```
pip install drf-flex-fields
```

# Requirements

* Python (2.7, 3.2, 3.3, 3.4, 3.5)
* Django (1.8, 1.9, 1.10)

# Basics

To use this package's functionality, your viewsets need to subclass ```FlexFieldsModelViewSet``` and your serializers need to subclass ```FlexFieldsModelSerializer```:

```
from rest_flex_fields import FlexFieldsModelViewSet, FlexFieldsModelSerializer

class PersonViewSet(FlexFieldsModelViewSet):
  queryset = models.Person.objects.all()
  serializer_class = PersonSerializer

class CountrySerializer(FlexFieldsModelSerializer):
  class Meta:
    model = Country
    fields = ('id', 'name', 'population')

class PersonSerializer(FlexFieldsModelSerializer):
  class Meta:
    model = Person
    fields = ('id', 'name', 'country', 'occupation')

  expandable_fields: {
      'country': (CountrySerializer, {source: 'country'})
  }
```

Now you can make requests like ```GET /person?expand=country&fields=id,name,country``` to dynamically manipulate which fields are included, as well as expand primitive fields into nested objects. You can also use dot notation to control both the ```fields``` and ```expand``` settings at arbitrary levels of depth in your serialized responses. Read on to learn the details and see more complex examples.

# Dynamic Field Expansion

To define an expandable field, add it to the ```expandable_fields``` within your serializer:
```
class CountrySerializer(FlexFieldsModelSerializer):
    class Meta:
        model = Country
        fields = ['name', 'population']


class PersonSerializer(FlexFieldsModelSerializer):
    country = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

    class Meta:
        model = Person
        fields = ['id', 'name', 'country', 'occupation']

    expandable_fields = {
        'country': (CountrySerializer, {'source': 'country', 'fields': ['name']})
    }
```

If the default serialized response is the following:
```
{
  "id" : 13322
  "name" : "<NAME>",
  "country" : 12,
  "occupation" : "Programmer",
}
```
When you do a ```GET /person/13322?expand=country```, the response will change to:

```
{
  "id" : 13322
  "name" : "<NAME>",
  "country" : {
    "name" : "United States"
  },
  "occupation" : "Programmer",
}
```
Notice how ```population``` was ommitted from the nested ```country``` object. This is because ```fields``` was set to ```['name']``` when passed to the embedded ```CountrySerializer```. You will learn more about this later on.

## Deferred Fields
Alternatively, you could treat ```country``` as a "deferred" field by not defining it among the default fields. To make a field deferred, only define it within the serializer's ```expandable_fields```.

## Deep, Nested Expansion
Let's say you add ```StateSerializer``` as serializer nested inside the country serializer above:

```
class StateSerializer(FlexFieldsModelSerializer):
    class Meta:
        model = State
        fields = ['name', 'population']


class CountrySerializer(FlexFieldsModelSerializer):
    class Meta:
        model = Country
        fields = ['name', 'population']

    expandable_fields = {
        'states': (StateSerializer, {'source': 'country', 'many': True})
    }

class PersonSerializer(FlexFieldsModelSerializer):
    country = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

    class Meta:
        model = Person
        fields = ['id', 'name', 'country', 'occupation']
{'fraction_non_alphanumeric': 0.11560996939221688, 'fraction_numerical': 0.006296458242238741, 'mean_word_length': 4.743847312908087, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 6, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '843683', 'n_tokens_mistral': 3310, 'n_tokens_neox': 3060, 'n_words': 1142}