Пишем backend для мобильного приложения за несколько минут

    Здравствуйте! Моя основная область деятельности — разработка мобильных приложений (iOS, Android). И большая часть приложений, использует взаимодействие с другими пользователями, хранение данных и другие задачи требующие наличие единого сервера. Поэтому для большей части приложений приходится писать свой велосипедbackend. А так как я, в основном являюсь мобильным разработчиком, то написание этого сервиса всегда становится небольшой проблемой — приходится задействовать веб-разработчика или искать подходящий BaaS сервис, даже если надо написать всего пару запросов.
    Поэтому было принято решение, попробовать найти инструмент, позволяющий в короткие сроки написать небольшой веб-сервис, который можно было бы использовать в мобильном приложении.

    За исходные данные были приняты: знание что такое HTTP, REST, JSON и начальный уровень разработки на Python (Django).
    И вот на днях, на глаза попался небольшой проект, на котором можно было провести полевые испытания. Суть проекта — приложение для одного мероприятия. Нужно отображать спикеров и их доклады.
    После непродолжительных поисков, за основу были взяты следующие инструменты: Python (как основной язык разработки), Django (как базовая платформа) и фреймворк Tastypie (представленный как фреймворк для создания API веб-сервисов). Итак, приступим.
    Создаем шаблон приложения Django:
    python django-admin.py startproject EventApp
    

    В настройках файла settings.py прописываем необходимые настройки для базы данных, локализацию, время. Устанавливаем пакет tastypie:
    pip install django-tastypie
    

    Обратите внимание, что требуется Python2.6+ и Django 1.5+. Из-за незнания этого факта пришлось потратить немного больше времени, т.к. фреймворк отказывался работать. Кроме того, нужно установить аналогичным образом пакет python-mimeparse.
    Далее, в файле settings.py, прописываем:
    INSTALLED_APPS += ['tastypie']
    

    или добавляем уже в существующий список приложение ‘tastypie’.
    Теперь пропишем модели нашей предметной области:
    # -*- coding: utf-8 -*-
    from django.db import models
    
    
    class Speaker(models.Model):
        name = models.CharField(max_length=32)
        company = models.CharField(max_length=32)
        photo = models.ImageField(upload_to='photos', blank=True, null=True)
    
        def __unicode__(self):
            return self.name + ' (' + self.company + ')'
    
    
    class Event(models.Model):
        title = models.CharField(max_length=64)
        speaker = models.ForeignKey(Speaker, blank=False, null=False)
        start_time = models.TimeField()
        end_time = models.TimeField()
    
        def __unicode__(self):
            return self.title + ' (' + self.speaker.name + ')'
    

    Мы написали модель докладчика (Speaker) и модель выступления (Event). У каждого выступления обязательно есть докладчик. Теперь, сделаем так, чтобы мы могли полноценно работать с нашими моделями как с ресурсами через REST протокол.
    Создаем в нашем приложении пакет api и файлом resources.py (или можно его создать в основном пакете).
    from tastypie.resources import ModelResource
    from EventApp.models import Speaker, Event
    
    
    class SpeakerResource(ModelResource):
        class Meta:
            queryset = Speaker.objects.all()
            resource_name = 'speaker'
    
    
    class EventResource(ModelResource):
        speaker = fields.ForeignKey(SpeakerResources, 'speaker', blank=True, null=True)
        class Meta:
            queryset = Event.objects.all()
            resource_name = 'event'
    

    В этом файле мы создали классы, так называемых ресурсов, основных объектов в нашем REST сервисе. Это как раз те ресурсы, к которым мы будем обращаться. Каждый класс содержит ссылку на ту модель, которую он представляет. Поле queryset возвращает нам набор объектов получаемых из базы при обращении к даному ресурсу. Поле resource_name необязательно, и позволяет нам указать дополнительно наименование ресурса, по которому он будет доступен нам.
    Еще один момент, в классе EventResources мы указали отдельное поле speaker, которое указывает что ресурс события ссылается на ресурс спикера.
    Теперь осталось только прописать в файле urls.py обращения к нашему сервису. Это делает очень просто.
    from django.conf.urls.defaults import *
    from tastypie.api import Api
    from api.resources import EventResource, SpeakerResource
    
    v1_api = Api(api_name='v1')
    v1_api.register(SpeakerResource())
    v1_api.register(EventResource())
    
    urlpatterns = patterns('',
        (r'^api/', include(v1_api.urls)),
    )
    
    

    Теперь запускаем наш проект
    python manage.py runserver
    

    Теперь, если сервер успешно запустился, открыв в браузере страницу по адресу http://localhost:8000/api/entry/?format=json, увидим там что фреймворк видит все наши ресурсы и отобразил нам схему нашего сервиса:
    {
      "events": 
      {
        "list_endpoint": "/api/v1/events/"
        , "schema": "/api/v1/events/schema/"
      }
      ,"speakers": 
      {
        "list_endpoint": "/api/v1/speakers/"
        ,"schema": "/api/v1/speakers/schema/"
      }
    }
    

    Параметр format принудительно указывает в каком формате мы хотим получить данные, но вместо него можно в запросе указать заголовок Content-type: application/json. Кроме JSON поддерживаются xml, yaml, bplist.
    По адресу schema можно посмотреть описание структуры модели (поля, типы и описание), а по адресу list_endpoint можно уже получить наши ресурсы, которые мы предварительно записали в базу.
    Теперь, открыв адрес http://localhost:8000/api/v1/events/?format=json мы увидим там что-то вроде этого:
    {
      "meta": 
      {
        "limit": 20
        ,"next": null
        ,"offset": 0
        ,"previous": null
        ,"total_count": 4
      }
      ,"objects": [
      {
        "id": 3
        ,"speaker": "/api/v1/speakers/2/"
        ,"start_time": "08:39:25"
        ,"end_time": "18:39:29"
        ,"title": "Ранее что нибудь"
        ,"description": "описание"
      }
      ]
    }
    

    Как видим — ничего сложного. В разделе meta выводится основная информация о ресурсе: количество записей, размер выдачи и тд. Одновременно мы можем обратиться к конкретному событию, обратившись к ресурсу по его id — http://localhost:8000/api/v1/events/1/
    Можем создать запись выполнив POST запрос и передав в него объект в JSON формате, обновить запись PUT запросом и удалить с помощью DELETE.
    Так же, в tastypie у класса ModelResource есть большой набор переопределяемых полей и методов, с помощью которых мы можем полностью изменить структуру выдаваемых данных. Например, мы хотим вместо ссылки на спикера, сразу получать его имя, чтобы не делать лишний запрос. В классе EventResource переопределяем метод dehydrate:
        def dehydrate(self, bundle):
            try:
                speaker = Speaker.objects.filter(id=bundle.obj.speaker.id)
                bundle.data['speaker_name'] = speaker[0].name
            except Speaker.DoesNotExist:
                pass
            return bundle
    

    В нем, мы находим спикера в базе и подставляем его в объект bundle, который представляет из себя словарь который отдается ресурсом. Теперь, ответ на запрос будет выглядеть так (напишу только основную часть):
    {
      "id": 3
      ,"speaker": "/api/v1/speakers/2/"
      ,"speaker_name": "Василий"
      ,"start_time": "08:39:25"
      ,"end_time": "18:39:29"
      ,"title": "Ранее что нибудь"
      ,"description": "описание"
    }
    

    Что нам и требовалось! Кроме того, запрос к ресурсу можно сделать с параметрами для фильтрации.
    Например, нам надо выбрать все события для одного докладчика. Логичен был бы следующий запрос:
    http://localhost:8000/api/v1/events/?speaker=1 который вернет нам события, спикерами которых является спикер с id = 1. Необходимо только прописать в meta класс ресурса еще одно поле:
            filtering = {
                'speaker': ALL_WITH_RELATIONS
            }
    


    Заключение

    Отлично! Теперь мы уже можем обращаться к серверу, получать от него данные. А ведь именно это нам и требовалось.
    Если статья вызовет интерес, то можно продолжить рассказ о том как сюда добавить валидацию, авторизацию и прикрутить симпатичную админку в такие кратчайшие сроки.
    Метки:
    Поделиться публикацией
    Похожие публикации
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 23
    • 0
      Сразу вопрос как незнакомому с Django — как оно поведет себя на нагрузке? Сколько одновременно чтений (чтений с фильром), добавления записей выдержит (например удобно поменять на T1 Micro/M1 Small Amazon Instance). Хотя бы порядок 10 в секунду, 100, 1к? Вот так из коробки, как в примере.
    • 0
      Вы ничего не рассказали об аутентификации, а ведь в tastypie есть для этого специальные методы. ApiKeyAuthentication например.
      • 0
        Да, это тема следующей статьи. Т.к. не хотелось в одной писать сразу обо всем.
      • +3
        Несколько минут?
        • 0
          Я на написание этого примера потратил около 8 минут. Это конечно уже с учетом что все знаешь и никаких подводных камней не вылезет
        • 0
          на HN недавно была ссылка, может вас заинтересует
          • 0
            Спасибо! На первый взгляд очень удобная штука, будем смотреть и разбираться.
          • +1
            Speaker.objects.filter(id=bundle.obj.speaker.id)
            bundle.data['speaker_name'] = speaker[0].name
            except Speaker.DoesNotExist

            Здесь будет IndexError.
            Speaker.objects.get() возвращает Speker.DoesNotExist
            • 0
              Спасибо ) Чтобы успеть в «несколько минут» забыл проверить все
            • +1
              Если будет интересно есть еще django rest framework/. В виде плюшки можно делать тестовые запросы прямо из браузера.
              • 0
                Я бы основным плюсом django rest framework отметил то, что он намного проще tastypie, что делает его более доступным для модификаций под себя.
                • 0
                  Да. Сидел на тейстипае пару лет.
                  Перешел на DRF — очень доволен.
              • 0
                На Flask это было бы еще быстрее
                • 0
                  при чтении такого слезы наворачиваются от того, что я хочу стать серверным разработчиком. С другой стороны если я хочу быть серверным разработчиком я должен уметь писать сервера как за 5 минут так и на 100к в коннект/сек.
                  • +1
                    Вы тоже рассказывайте ©
                  • 0
                    приходится задействовать веб-разработчика или искать подходящий BaaS сервис
                    Большинство BaaS сервисов имеют эту базовую фичу (key-value store, когда можно создать свою иерархию данных) — например у нас в QuickBlox это называется Custom Objects, поддерживаются ACL, данные сразу становятся доступными через API и админку. У Parse, StackMob и прочих mBaaS это тоже есть.

                    Довелось ли автору опробовать эти готовые бекенды? Было бы интересно узнать нюансы, при которых написание с нуля остается предпочтительным.
                    • 0
                      Да, мы работали и сейчас работаем с Parse. Он нас устраивает на 99%, но не все заказчики готовы держать свои данные на чужих серверах, к сожалению.
                      • 0
                        А если бы Parse были согласны ставить свой движок на сервер к вам или заказчику, при условии подписания SLA? Мы это делаем с enterprise клиентами и это сразу снимает волнения за владение пользовательскими данными и за «что будет, если вашу команду переедет автобус, а сервера съест Ктулху».
                        • 0
                          А Parse тоже предоставляет возможности такие?
                          • 0
                            Инфа не 100%, но насколько я знаю, нет. После покупки Фейсбуком тем более в этом для них меньше смысла.

                            Также тут часто присутствует принципиальная позиция — я общался с CEO нескольких подобных сервисов, и они говорят, что верят в cloud и что всё должно быть в одном месте. Может еще боятся, что украдут движок, но об этом не принято говорить. Мы считаем, что это глупо, нам не жалко отдавать движок клиентам, т.к. через через каждый месяц у нас будет еще лучшая версия, и клиентам выгодно оставаться нашими клиентами и получать апдейты.
                    • 0
                      tastypie хорош, но слишком много проблем возникает когда надо сделать что-то нестандартное. Еще беда всех api в том что нет единой спецификации для REST API и каждый реализует по своему. Конкретно у tastypie мне не нравится работа со связями, вариант с отдачей `resource_uri` мне не очень нравится, когда начинаешь работать с api на стороне клиента хочется иметь как минимум id объекта.
                      Развиваются ребята тоже очень странно, в начале года было очень много проблем с каждой версией + они продают поддержку что вроде как и не плохо, но количество открытых/закрытых тасков и пулл реквестов на гитхабе напрягает очень, тот же django-rest-framework на год очень хорошо развился на глазах.

                      Для сравнения

                      • 0
                        P.S Перепутал местами колонки

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.