Введение
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
Со времени первой публикации прошло уже много времени и появились новые инструменты, среди них: