Adding api views¶
In order to sync data, a remote server must make it available for download. This can be achieved using the synctool.routing.Route class. This class takes care of:
- Creating the url pattern
- Serializing the returned data using the Django JSON serializer
- Returning an application/json response object
- Adding http basic authentication to the api
Serializing an application¶
The route.app function can be used to serialize a whole application. The returned data is the same as that of the python manage.py dumpdata command.
- route.app(path, label)¶
Parameters: - path – The path of the urlpattern to register.
- label – The label of the installed application to serialize.
Example:
from synctools.routing import Route
route = Route()
# Sync the ``django.contrib.sites`` app
route.app("sites", "sites")
# Sync an application call myblogapp
route.app("blogs", "myblogapp")
Once your urlpatterns are registered, you can open the url in a browser to inspect the data.
Serializing querysets¶
The route.queryset decorator can be used to register any function that returns one or more querysets.
- route.queryset(path)¶
Parameters: path – The path of the urlpattern to register.
Returning a single queryset¶
Example:
from myapp.models import Blog
@route.queryset("blogs")
def blogs():
return Blog.objects.all()
Returning multiple querysets¶
Multiple querysets can be returned as a list or tuple:
from myapp.models import Blog, Post
@route.queryset("blogs")
def blogs():
return [
Blog.objects.all(),
Post.objects.all(),
]
Filtering and slicing¶
Querysets can be filtered or sliced. This is useful when you only want to return a subset of a table.
Example:
@route.queryset("blogs")
def blogs():
return [
Blog.objects.all()[:100],
]
Accepting arguments¶
The route argument is a url regular expression. This means views can take arguments from the url.
Example:
@route.queryset("blog/(?P<slug>[^/]+)")
def blog(slug):
return Blog.objects.filter(slug=slug)
Modifying querysets¶
Querysets can be modified before returning them. This can be helpful if you want to exclude certain information in the output.
For example, if Blog had a User relation, we could return a Blog queryset but leave out the user information.
Example:
@route.queryset("blogs")
def pickle_blog():
queryset = Blog.objects.all()
for blog in queryset:
blog.user = None
return queryset
Note: This example assumes the user field is nullable.
Order is important¶
When syncing applications using the route.app function, model dependencies are automatically calculated and sorted. When using route.queryset, this is not the case. Therefore, you must pay attention to the order in which you return querysets.
For example, assume you had a Blog and Post model. The Post model has a foreign key to Blog:
@route.queryset("blogs")
def blogs():
# Good: Post depends on blog existing first. Since blog
# is serialized first, blog will be saved first.
return [
Blog.objects.all(),
Post.objects.all(),
]
@route.queryset("blogs")
def blogs():
# Bad: The sync client can fail with an IntegrityError because
# post points to blogs that haven't been saved locally yet.
return [
Post.objects.all(),
Blog.objects.all(),
]
Authentication¶
HTTP basic authentication is added to each view created using the Route class. By default, the credential is set to settings.SYNCTOOL_API_TOKEN. A custom token can be specified as an argument to the Route class.
Example:
route = Route(api_token="mytoken")
A sample call that grants access to this view would be:
$ curl https://myserver.com/sync/sites -u mytoken:
Note
If you want your queryset information and credentials to remain private, make sure to serve your API over SSL only.
Including urls¶
The routed url patterns can be included in your project in the same way as another other url pattern:
Example:
# myproject.urls
from myapp.views import route
urlpatterns += patterns("",
url("^sync/", include(route.urlpatterns)),
)