This package creates a minimal framework for creating AJAX endpoints of your own in Django without having to create all of the mappings, handling errors, building JSON, etc.
Additionally, this package allows you to create AJAX endpoints for manipulating Django models directly from JavaScript without much effort. This feature works in a similar manner to Django's ModelAdmin
feature in admin.py
.
- Make sure you have the decorator package installed.
- Install the
ajax/
directory as you would any other application in your Django project or put it in your Django project'ssys.path
somewhere. - Add
ajax
toINSTALLED_APPS
in yoursettings.py
. - In your Django project's
urls.py
add(r'^ajax/', include('ajax.urls'))
.
There are no models associated with this package so you don't need to worry about syncing your database or anything.
You can use this package to create ad-hoc AJAX endpoints or by exposing your Django applications's models via AJAX.
/ajax/{some_app_name}/{some_endpoint}.json
is treated as an ad-hoc AJAX endpoint and will be mapped tosome_app_name.endpoints.some_endpoint
. The functionsome_endpoint
must return adict
or anHttpResponse
of some sort. When adict
is returned, it is serialized as JSON and packaged up nicely for the client./ajax/{some_app_name}/{model}.json
will attempt to load up an instance ofModelEndpoint
for the given model and run an append operation to create a new record for the given model./ajax/{some_app_name}/{model}/{pk}/(update|delete|get).json
will attempt to load up an instance ofModelEndpoint
for the given model and run the given operation for the record specified bypk
.
All of your AJAX endpoints should be put into a file called endpoints.py
in your Django applications. AJAX will handle all of the rest of the magic.
The following is a simple example of an AJAX endpoint that just echo's back the POST. Keep in mind that ad-hoc AJAX endpoints basically work like regular Django views in that they get a request
object. All of the usual view decorators can be used here without issue (e.g. login_required
). The only thing to keep in mind is that views only get request
as an argument and must return a dict
or HttpResponse
.
from ajax.exceptions import AJAXError
def right_back_at_you(request):
if len(request.POST):
return request.POST
else:
raise AJAXError(500, 'Nothing to echo back.')
If you're surfacing a known error, it's best to use AJAXError
with a sane error code and a message. All other exceptions will be returned as a 500 with a generic error message.
From JavaScript you can easily access the endpoint using jQuery.
$.post('/ajax/my_app/right_back_at_you.json', {
name: "Joe Stump",
age: 31
});
You can also create endpoints from callable objects. The BaseEndpoint
class has two functions that are pretty helpful for encoding Django a QuerySet
or an instance of Model
.
BaseEndpoint._encode_data
takes a single argument,data
, which it assumes to be aQuerySet
(or something that looks and acts like one) and converts it into a vanilla list capable of being serialized usingsimplejson
. This uses Django's Python serializer and does some minor cleanup to make it a sane looking JSON payload.BaseEndpoint._encode_record
takes a DjangoModel
and encodes it into a normal Python dict. Additionally, it looks forForeignKey
's and hydrates them to include the full associated record.
The following code is a simple example of using the BaseEndpoint._encode_record
method. You could easily encode a bunch of users with BaseEndpoint._encode_data
by replacing a few lines of code in this example.
from django.contrib.auth.models import User
from ajax.endpoints import BaseEndpoint
from ajax.exceptions import AJAXError
class MyEndpoint(BaseEndpoint):
__call__(self, request):
try:
user = User.objects.get(pk=int(request.POST['user']))
except User.DoesNotExist:
return AJAXError(404, 'Invalid user.')
return self._encode_record(user)
my_endpoint = MyEndpoint()
AJAX also offers a class, called ModelEndpoint
, that takes a Django model and exposes ways to manipulate it via AJAX.
import ajax
from my_app.models import Category
class CategoryEndpoint(ajax.endpoints.ModelEndpoint):
pass
ajax.endpoint.register(Category, CategoryEndpoint)
You can then send a POST to:
/ajax/my_app/category.json
to create a newCategory
./ajax/my_app/category/{pk}/update.json
to update aCategory
.pk
must be present in the path forupdate
,delete
, andget
. NOTE: This package assumes that thepk
argument is an integer of some sort. If you've mangled yourpk
fields in weird ways, this likely will not work as expected./ajax/my_app/category/{pk}/get.json
to get theCategory
as specified aspk
./ajax/my_app/category/{pk}/delete.json
to delete theCategory
as specified bypk
.
It should be noted that the AJAX package takes a liberal approach when it comes to instances of ForeignKey
it finds in model declarations. If a model that has a ForeignKey
is fetched it will be expanded to the full record, recursively. For instance, if a ForeignKey
to User
is in a given model, you will get the whole associated User
record when a row is fetched.
In addition to expanding ForeignKey
while fetching, they are expanded when creating a record from the data in POST. If a ForeignKey
to User
is in a given model, and the field is called author
, ModelEndpoint
will detect that and automatically assume that request.POST['author']
is an appropriate pk
, instantiate it, and replace it with a full instance of the associated User
object.
There are a number of security features inherent in the framework along with ways to lock down your ad-hoc and model endpoints. You can use the decorator(s) outlined below as well as throwing appropriate AJAXError
exceptions from your ad-hoc endpoints. Of course, all exceptions raised and HttpResponse
objects returned are respected by default. For model endpoints you can, additionally, use can_create()
, can_update()
, can_delete()
, can_get()
, and authenticate()
to lock down various operations on the given model.
- All requests to an AJAX endpoint must be sent via
POST
, including a GET on a model'spk
. - The default
ModelEndpoint.authenticate()
method requires that a user is, at a minimum, logged in.
The AJAX package offers a familiar decorator in ajax.decorators
called login_required
. It works in the same way that the Django decorator does and handles throwing a proper AJAXError
if the user isn't logged in.
from ajax.decorators import login_required
@login_required
def my_ajax_endpoint(request):
return {
'Hello, %s!' % request.user.username
}
The ModelEndpoint
class offers a number of methods that you can override to add more advanced security over your model-based enpoints. You can override these in your model-based endpoints to control who is able to access each model and in what manner.
Returns True
if the user
can create a new record
using the given model.
Returns True
if the user
can update the given record
.
Returns True
if the user
can delete the given record
.
Returns True
if the user
can fetch the given record
.
This method is ran before any other security-related operations. This method allows you to control overall authentication-related access prior to any of the can_*
methods being ran and before any model operations are executed. It is ran regardless of the operation being attempted.
- In
ModelEndpoint
modify_extract_data()
to automatically recognizeForeignKey
fields and convert the value provided inrequest.POST
topk
for the givenForeignKey
. This will allow people to provide apk
inrequest.POST
without having to load the object itself. - Create a JavaScript client that extends jQuery and exposes shortcuts for interfacing with
django-ajax
. - Integrate Django's CSRF token support.