Alternative approach to data validation and REST endpoints in Django and DRF


Keywords
declarative, django, rest, serializer, validation
License
MIT
Install
pip install django-alt==1.0.0b6

Documentation

logo


django-alt is an alternative approach to data validation and REST endpoint definition in Django and DRF.

pypi-version

Latest version is 0.74.

Installation

Requirements: this package depends on django and djangorestframework.

pip install django-alt

Motives

  • No standardized way to separate domain and data logic.
  • No standardized way to validate serialized data.
  • Standard validation techniques do not offer any mechanisms for separation of concerns. (e.g. validating object creation vs. update).
  • Cumbersome lifecycle hooks (e.g. code to execute upon object creation).
  • Allow more declarative expressions while making it easy to spice them up.

Solution

This package aims to help solving these problems. It allows a notion of a validator – an encapsulation of domain logic for a given model. Validators allow you to:

  • Handle before and after lifecycle events of model changes (creation, update, deletion).
  • Write arbitrary methods to clean and validate individual fields that are automatically called when needed.
  • Write arbitrary methods to validate interdependent fields.

This package also includes managers and endpoints – means to automate validated model object management as REST resources (or not). A plentiful of shorthand helpers make validating CRUD ops feel like a breeze. Validators also easily tie into existing infrastructure.

Be sure to also checkout recipes.md for quick–starting and more examples or indepth.md for a deeper dive.

Example: Todo list

endpoints.py
class TodoEndpoint(Endpoint):
    serializer = TodoSerializer
    config = {
        'get, patch, delete': {
            'queryset': lambda todo, **url: todo.objects.get(id=url['pk'])
        }
    }
validators.py
class TodoValidator(Validator):
    def clean(self, attrs):
        attrs['slug'] = slugify(attrs['name'])
        
    def clean_author(self, author):
        return author.capitalize()
        
    def field_author(self, author):
        invalid_if(not author.is_active, 'author', 'Sorry, you cannot post')
            
    def did_create(self, instance, validated_attrs):
        inform_subscribers(instance)
serializers.py
class TodoSerializer(ValidatedModelSerializer):
    class Meta:
        model = Todo
        fields = '__all__'
        validator_class = TodoValidator

Example: endpoint customization

endpoints.py
class TodoSpecialEndpoint(Endpoint):
    serializer = TodoSerializer
    config = {
        'get': {
            'queryset': lambda todo, **url: todo.objects.all(),
            'filters': {
                'hot': lambda: qs, hot: qs.filter(hot=hot),
                'confirmed': lambda: qs, confirmed: qs.filter(confirmed=confirmed)
            }
        }
    }
       
    @classmethod
    def can_get(cls):
        pre_permission = lambda request, **url: not request.user.is_anonymous
        post_permission = lambda request, queryset, attrs: not attrs.get('confirmed', False)
        return (pre_permission, post_permission)

urls.py

urlpatterns = [url(r'^todos/$', TodoSpecialEndpoint.as_view())]

This code would automatically create a behind-the-scenes view. This simple definition uses some powerful concepts:

  • /todos/ would return all Todo objects serialized as JSON.
  • /todos/?hot=true would return all Todo objects that have an attribute hot that is true.
  • /todos/?confirmed=true would return all Todo objects that have an attribute confirmed that is true.
  • /todos/?confirmed=true&hot=true you can probably guess...

Notes

While on the 0.x track, this project is subject to rapid development and some changes might break reverse–compatibility. Any contribution or ideas are welcome!

To see the hot upcoming features, switch to the dev-1.0 branch.

Author & License

Vilius Poška.
This project is freely licensed under the MIT license.