Введение
REST (сокр. англ. Representational State Transfer, "передача состояния представления") — подход к архитектуре сетевых протоколов, обеспечивающих доступ к информационным ресурсам. Был описан и популяризован в 2000 году Ройем Филдингом (Roy Fielding), одним из создателей протокола HTTP. Самой известной системой, построенной в значительной степени по архитектуре REST, является современная Всемирная паутина. источник
Каждая сущность, с которой необходимо работать через REST, должна иметь уникальный url. Например, фильм "Неудержимые" идентифицируется по /film/1/. В REST каждая сущность называется ресурсом. Для манипуляций с ресурсом используются стандартные методы HTTP: GET, POST, PUT, DELETE, которые в REST называются интерфейсы:
GET /film/1/ - запрос фильма с id равным 1. В случае корректного запроса в ответ приходит 200/OK и запрашиваемые данные, в случае ошибки 400/Bad Request;
POST /film/1/ - изменение фильма, новые данные передаются в теле запроса. В случае запроса на изменение в ответ приходит 200/OK, в случаи успешного создания ресурса - 201/Created, в случае ошибки - 400/Bad Request;
DELETE /film/1/ - удаление фильма. В случае корректного запроса в ответ приходит 200/Accepted, если запрашиваемый ресурс отсутствует - 404/Not Found, если существует несколько фильмов с id=1 то вернется 409/Conflict;
Подробнее с REST можно познакомится по приведенным ссылкам:
Установка и использование
Из множества доступных реализаций REST в django я решил остановится на django-piston. Почему именно django-piston? На то есть две причины, во-первых, я встречал много лестных отзывов в западной блогосфере по-поводу piston, во-вторых, проект в котором надо было прикрутить REST писался на ExtJS (уже Sencha), а Matt Dorn описал у себя в блоге наглядный пример как объединить ExtJS и Django через REST. Сам пост и выступление Matt Dorn на одной из конференций можно найти ниже, в блоке Дополнительное чтиво.
В качестве примера напишем простую систему учета новых фильмов в кинотеатре. Начнем-с.
Устанавливаем последнюю версию django-piston
hg clone http://bitbucket.org/jespern/django-piston
В INSTALLED_APPS добавляем 'piston'.
В корне проекта создадим директорию api (не забываем создать файл init.py). В этой же директории создадим файл handlers.py, в котором описываются интерфейсы работы с ресурсом, т.е. все манипуляции (CRUD) с нашей моделью Фильмов. Минимальное содержимое файла:
from piston.handler import BaseHandler
from films.models import Film
class FilmHandler(BaseHandler):
model = Film
fields = ('id', 'title', 'genre', 'release')
Для начала работы этого хватает, все остальное добавит базовый класс BaseHandler.
В директории api создадим еще один файл - urls.py, который очень похож на стандартный, только обработчиком назначается не вид, хандлер, определенный выше.
from django.conf.urls.defaults import *
from piston.resource import Resource
from api.handlers import FilmHandler
film_resource = Resource(FilmHandler)
urlpatterns = patterns('',
url(r'^film/(?P\d+)/$', film_resource),
url(r'^films/$', film_resource),
)
В urls.py проекта добавим ссылку на urls.py из api:
urlpatterns = patterns('',
...
(r'^api/', include('api.urls')),
)
Создадим приложение films с такой моделью:
# coding=utf-8
from django.db import models
class Film(models.Model):
title = models.CharField('Название', max_length=200)
genre = models.CharField('Жанр', max_length=100)
release = models.DateField('Дата релиза')
def __unicode__(self):
return self.title
Добавим новое приложение в INSTALLED_APPS и синхронизируем БД.
Добавим из консоли два тестовых фильма:
curl -i -X POST -d "title=The%20Expendables&genre=action&release=2010-01-28" http://127.0.0.1:8000/api/films/
HTTP/1.0 200 OK
Date: Tue, 07 Sep 2010 21:55:24 GMT
Server: WSGIServer/0.1 Python/2.6.5
Vary: Authorization
Content-Type: application/json; charset=utf-8
{
"genre": "action",
"release": "2010-01-28",
"id": 1,
"title": "The Expendables"
}
curl -i -X POST -d "title=The%20Karate%20Kid&genre=action&release=2010-01-28" http://127.0.0.1:8000/api/films/
HTTP/1.0 200 OK
Date: Tue, 07 Sep 2010 21:57:32 GMT
Server: WSGIServer/0.1 Python/2.6.5
Vary: Authorization
Content-Type: application/json; charset=utf-8
{
"genre": "action",
"release": "2010-01-28",
"id": 2,
"title": "The Karate Kid"
}
Все 200/OK, теперь запросим список фильмов:
curl -i -X GET http://127.0.0.1:8000/api/films/
HTTP/1.0 200 OK
Date: Tue, 07 Sep 2010 21:59:44 GMT
Server: WSGIServer/0.1 Python/2.6.5
Vary: Authorization
Content-Type: application/json; charset=utf-8
[
{
"genre": "action",
"release": "2010-01-28",
"id": 1,
"title": "The Expendables"
},
{
"genre": "action",
"release": "2010-01-28",
"id": 2,
"title": "The Karate Kid"
}
]
Удалим фильм с id = 2
curl -i -X DELETE http://127.0.0.1:8000/api/film/2/ HTTP/1.0 204 NO CONTENT Date: Tue, 07 Sep 2010 22:05:55 GMT Server: WSGIServer/0.1 Python/2.6.5 Vary: Authorization Content-Length: 0 Content-Type: text/plain
Как видите все операции по отображению, добавлению, удалению piston взял на себя. Но piston позволяет переопределить любой метод из CRUD. Для примера переопределим метод create (в файле handlers.py) и добавим возможность сохранения ссылки на imdb при создании фильма. Я использовал фейковую функцию, которая возвращает один и тот же url.
from piston.handler import BaseHandler
from films.models import Film
def get_imdb(title):
return 'http://www.imdb.com/title/tt1320253/'
class FilmHandler(BaseHandler):
model = Film
fields = ('id', 'title', 'imdb', 'genre', 'release')
def create(self, request, *args, **kwargs):
if not self.has_model():
return rc.NOT_IMPLEMENTED
attrs = self.flatten_dict(request.data)
try:
inst = self.queryset(request).get(**attrs)
return rc.DUPLICATE_ENTRY
except self.model.DoesNotExist:
inst = self.model(**attrs)
inst.imdb = get_imdb(inst.title)
inst.save()
return inst
except self.model.MultipleObjectsReturned:
return rc.DUPLICATE_ENTRY
Еще один пример работы через браузер с использованием jquery. Файл шаблона можете скачать тут, не забудьте заменить расширение на .html. В url.py проекта добавить такую строчку:
(r'^$', 'django.views.generic.simple.direct_to_template', {'template':'django_piston.html'}),
Скриншот полученной страницы:
На этом возможности django-piston не ограничиваются:
Накануне запуска поста обнаружил (в твитере у Александра Соловъёва) хороший инструмент для отладки REST API - htty
Со времени первой публикации прошло уже много времени и появились новые инструменты, среди них: