def api_accepts(fields):
def decorator(func):<EOL><INDENT>@wraps(func)<EOL>def wrapped_func(request, *args, **kwargs):<EOL><INDENT>if request.method not in ['<STR_LIT:GET>', '<STR_LIT:POST>']:<EOL><INDENT>return func(request, *args, **kwargs)<EOL><DEDENT>form_class = type('<STR_LIT>', (forms.Form,), fields.copy())<EOL>form = form_class(getattr(request, request.method))<EOL>if not form.is_valid():<EOL><INDENT>if settings.DEBUG:<EOL><INDENT>return JsonResponseBadRequest(<EOL>'<STR_LIT>' % dict(form.errors)<EOL>)<EOL><DEDENT>else:<EOL><INDENT>logger.warn(<EOL>'<STR_LIT>',<EOL>request.path,<EOL>dict(form.errors)<EOL>)<EOL>return func(request, *args, **kwargs)<EOL><DEDENT><DEDENT>for (field_name, field_instance) in fields.items():<EOL><INDENT>if isinstance(field_instance, models.Model):<EOL><INDENT>field_type = type(field_instance)<EOL>field_id = '<STR_LIT>' % field_name<EOL>if field_id not in request.REQUEST:<EOL><INDENT>return JsonResponseBadRequest(<EOL>'<STR_LIT>' % field_name<EOL>)<EOL><DEDENT>field_pk = int(request.REQUEST[field_id])<EOL>try:<EOL><INDENT>field_value = field_type.objects.get(pk=field_pk)<EOL><DEDENT>except field_type.DoesNotExist:<EOL><INDENT>return JsonResponseNotFound(<EOL>'<STR_LIT>' % (<EOL>field_type, field_pk<EOL>)<EOL>)<EOL><DEDENT>form.cleaned_data[field_name] = field_value<EOL><DEDENT><DEDENT>validated_request = ValidatedRequest(request, form)<EOL>return func(validated_request, *args, **kwargs)<EOL><DEDENT>return wrapped_func<EOL><DEDENT>return decorator<EOL>
Define the accept schema of an API (GET or POST).

'fields' is a dict of Django form fields keyed by field name that specifies
the form-urlencoded fields that the API accepts*.

The view function is then called with GET/POST data that has been cleaned
by the Django form.

In debug and test modes, failure to validate the fields will result in a
400 Bad Request response.
In production mode, failure to validate will just log a
warning, unless overwritten by a 'strict' setting.

For example:

@api_accepts({
    'x': forms.IntegerField(min_value=0),
    'y': forms.IntegerField(min_value=0),
})
def add(request, *args, **kwargs):
    x = request.POST['x']
    y = request.POST['y']

    # x and y are integers already.
    return HttpResponse('%d' % (x + y))


*: 'fields' can also include Django models as {'key': Model()}. If present,
api_accepts will look for the field keyed by '<key>-id'
and pick the object that has that primary key. For example, if the entry is
{'course': Course()}, it will search for the key course_id='course-id' in
the request object, and find the object Course.objects.get(pk=course_id)