Package: django-restup
Django RestUpDO NOT USE IN PRODUCTION!!!This package is in very early alpha and has no tests! It is currently more of
a proof of concept.PhilosophyDjango RestUp takes time-tested ideas from other Django REST API packages such
as Django REST Framework, Django Tastypie, and Restless.It expands upon these ideas by applying a bit more flexibility/extensibility and
explicitness. There is a trade-off. RestUp requires a bit more configuration than
other packages/libraries.The main difference between RestUp and other packages is how RestUp determines API
data structure. RestUp requires the developer to explicitly declare the structure of
their data on a per-resource basis.This explicitness comes with some major benefits:An extra layer of security and sanity checking for your data.Makes it easy to see the structure of your data and make changes to that
structure without side effects.Expose only the data you want to expose, and in the manner that you want
to expose it.Allows for deep authentication and authorization on a per-resource,
per-object, and even per-field basis.Getting StartedSchema DefinitionAt the core of a RestUpModelResourceis it’s schema definition. You
define a schema as a native python dictionary object. This schema is used to
populate the JSON response forGETrequests. It is also used to help
deserializePOSTed data into native python data types so it can be used to
create a new object or modify an existing one.Example:Assume we have a Django model with the following structure:# In ~/yourapp/models.py
from django.db import models


class Book(models.Model):

    title = models.CharField(
        max_lenfth=200,
        blank=False
    )

    isbn = models.CharField(
        max_length=100,
        blank=False
    )

    favorites = models.PositiveIntegerField(
        blank=True,
        default=0
    )

    secret_field = models.CharField(
        max_length=20,
        blank=False
    )Let’s define a RestUpModelResourceclass to expose this resource:# In ~/yourapp/api.py
from restup import ModelResource
from .models import Book


class BookResource(ModelResource):

    model = Book

    schema = {
        'title': {
            'attribute': 'title'
        },
        'isbn': {
            'attribute': 'isbn'
        },
        'favorites': {
            'attribute': 'favorites'
        },
        'secret_field': {
            'attribute': 'secret_field'
        }
    }You’ll also need to tell Django where to find the urls for this resource:# In ~/yourproject/urls.py

# ~~? (other imports)
from django.conf.urls import include
from yourapp.api import BookResource

urlpatterns = [
    # ...?
    url(
        r'^api/books/', include(BookResource.urls())
    )
]That’s all you need to define in order to make anyGET, POST, PUT, DELETErequests to the endpoint.PermissionsHowever, right now, we’re letting just anyone mess with our data! There’s
no security at all! We only want logged in users to be able to manipulate
the data. We’ll allow anyone toGETit though:# In ~/yourapp/api.py
from restup import ModelResource
from .models import Book


class BookResource(ModelResource):

    model = Book

    schema = {
        'title': {
            'attribute': 'title'
        },
        'isbn': {
            'attribute': 'isbn'
        },
        'favorites': {
            'attribute': 'favorites'
        },
        'secret_field': {
            'attribute': 'secret_field'
        }
    }

    def is_authenticated(self, request):  # Add this method override
        if request.method in self.SAFE_METHODS:
            return True
        return request.user.is_authenticated()You can put anything you want in theis_authenticatedmethod as long as it
returns true for authenticated requests and false for unauthorized requests. You’ll
notice that you have access to the request object. This is a normal Django request
object. You can do anything with it that you could do in a normal Django view class.
Theis_authenticatedmethod is the second permission hook to be called in any
request. It is called right after the allowed methods check and right before the
request is routed to the correct action handler. Speaking of the Allowed methods
check, we don’t want anyone to be able to delete our models. Let’s stop them
from doing that:# In ~/yourapp/api.py
from restup import ModelResource
from .models import Book


class BookResource(ModelResource):

    model = Book

    schema = {
        'title': {
            'attribute': 'title'
        },
        'isbn': {
            'attribute': 'isbn'
        },
        'favorites': {
            'attribute': 'favorites'
        },
        'secret_field': {
            'attribute': 'secret_field'
        }
    }

    def is_authenticated(self, request):
        if request.method in self.SAFE_METHODS:
            return True
        return request.user.is_authenticated()

    def can_delete(self, obj, request):  # Override this method
        return FalseThere we go! Now allDELETErequests to any of theBookResourceendpoints
will return a403 ForbiddenHTTP response. What about thesecret_fieldfield? Surely we don’t want everyone to see that? But, we need to populate it with
data from the client. This is where RestUp becomes something special:# In ~/yourapp/api.py
from restup import ModelResource
from .models import Book


class BookResource(ModelResource):

    model = Book

    schema = {
        'title': {
            'attribute': 'title'
        },
        'isbn': {
            'attribute': 'isbn'
        },
        'favorites': {
            'attribute': 'favorites'
        },
        'secret_field': {
            'attribute': 'secret_field',
            'readable': False  # Add this line
        }
    }

    def is_authenticated(self, request):
        if request.method in self.SAFE_METHODS:
            return True
        return request.user.is_authenticated()

    def can_delete(self, obj, request):
        return FalseAll we need to do is add a'readable'key to our field declaration inside
our schema and set it’s value toFalse. This will ensure that this data is
not sent out to any requesting client. However, we can still applyPOSTed
data to this field.Object level permissionsWe only want staff users to be able to create and update anyBookobjects.
Let’s make sure no one else can:# In ~/yourapp/api.py
from restup import ModelResource
from .models import Book


class BookResource(ModelResource):

    model = Book

    schema = {
        'title': {
            'attribute': 'title'
        },
        'isbn': {
            'attribute': 'isbn'
        },
        'favorites': {
            'attribute': 'favorites'
        },
        'secret_field': {
            'attribute': 'secret_field',
            'readable': False
        }
    }

    def is_authenticated(self, request):
        if request.method in self.SAFE_METHODS:
            return True
        return request.user.is_authenticated()

    def can_create(self, request):  # Override this method
        return request.user.is_staff

    def can_delete(self, obj, request):
        return False

    def can_update(self, obj, request):  # Override this method
        return request.user.is_staffThere we go! Now only staff members can create and updateBookresources!We now have a fairly robust RESTful resource. Our resource allows us to
create, update, list, and getBookobjects. We also make sure non staff users
can’t do anything butGETthe resource from either it’s list or detail endpoints.FilteringWe want users to be able to filterBookobjects. We’ll allow them to
filter the results based on the favorites field:# In ~/yourapp/api.py
from restup import ModelResource
from .models import Book


class BookResource(ModelResource):

    model = Book

    schema = {
        'title': {
            'attribute': 'title'
        },
        'isbn': {
            'attribute': 'isbn'
        },
        'favorites': {
            'attribute': 'favorites',
            'filters': (  # Add this key
                'gt', 'lt',
            )
        },
        'secret_field': {
            'attribute': 'secret_field',
            'readable': False
        }
    }

    def is_authenticated(self, request):
        if request.method in self.SAFE_METHODS:
            return True
        return request.user.is_authenticated()

    def can_create(self, request):
        return request.user.is_staff

    def can_delete(self, obj, request):
        return False

    def can_update(self, obj, request):
        return request.user.is_staffGreat! Now, if our users want to get a list of books with more than 10 favorites
they only need to send a request tohttp://mysite.com/api/books/?favorites__gt=10.
You can use any of the standard Django query filters definedin the Django docsValidationWe want to be sure that the client is only providing a positive integer to our
favorites field. We will create 2 validator functions to make sure the value
is correct:# In ~/yourapp/api.py
from restup import ModelResource
from .models import Book

# We will add two methods for illustrative purposes, but this check could
# easily be done with a single function.

def is_integer(value):  # Add this method
    return type(value) == int

def is_positive(value):  # Add this method
    return value > 0


class BookResource(ModelResource):

    model = Book

    schema = {
        'title': {
            'attribute': 'title'
        },
        'isbn': {
            'attribute': 'isbn'
        },
        'favorites': {
            'attribute': 'favorites',
            'filters': (
                'gt', 'lt',
            ),
            'validators': (  # Add this key
                is_integer,
                is_positive
            )
        },
        'secret_field': {
            'attribute': 'secret_field',
            'readable': False
        }
    }

    def is_authenticated(self, request):
        if request.method in self.SAFE_METHODS:
            return True
        return request.user.is_authenticated()

    def can_create(self, request):
        return request.user.is_staff

    def can_delete(self, obj, request):
        return False

    def can_update(self, obj, request):
        return request.user.is_staffValidator functions should take a single argument. This argument should be
thevalueof the key in the received data. The function should returnTrueif the value is valid andFalseotherwise. A check like the
one in our example isn’t srictly necessary as Django’s model backend
would throw an exception if we tried to save anything that wasn’t a positive
integer. However, that would return a500 Server errorresponse. That’s not
very helpful for the client. It would be much better to return a400 Bad Requestresponse to let the client know they entered something incorrectly.ConclusionWell, that’s a basic rundown of how it all works. If you want a more in-depth
understanding, please take a look at the source code. TheModelResourceclass is a great place to start.DocumentationAs the project develops, I plan on adding
more complete documentation including:An in-depth tutorialFull API referenceIn-depth explanation of data flowUpcoming required developmentTests!Robust handling for edge-cases.Upcoming FeaturesA list of some features on the docket:Support for custom per-field pre and post processing functions. These
will take a value returned by the database or from clientPOSTed data
and perform any necessary complex transformations.Support for custom per-field authorization functions for extremely granular
permissions control.Self documenting schema endpoints similar to those of Django REST Framework.Return resource URIs in JSON data.Custom URL namespaces.Features I won’t addXML/YAML/etc support. I don’t use these often and they aren’t very easy to
serialize. If someone else wants to add support, they are welcome to create
a pull request.Python 2 support. Sorry, but it’s time to move on.Django < 1.8 support. See above.ContributingThis project is in very early development. It should only be used on non-production
projects until it reachesV1.0.0.Any criticisms or ideas welcome. Just open up an issue.If you want to contribute to the source code, it is preferred that you open an issue
before submitting a pull request to discuss the changes or enhancements you want to
make. I will not discriminate against anyone for any reason.