У цій статті ви навчитеся створювати простий REST API, використовуючи Django REST Framework. Весь код написаний за допомогою Python 3.6, Django 1.11 та DRF 3.6.
Передумови
Для побудови заклинань вам потрібні: Python 3.6, Django 1.11 і Django Rest Framework 3.6, також передбачається, що кожна команда запускається у virtualenv
. Якщо ви ще не знайомі з нею, це не проблема, вам слід використовувати sudo pip
, замість pip
. Якщо ви досі не встановили Python 3.6, тоді слід вилучати __str__
методи, оскільки вони використовують нові форматовані рядкові літерали.
$ pip install django djangorestframework
Створимо проект для цієї демонстрації.
Про що вам слід подбати?
Тепер розглянемо популярний підхід із бібліотекою. Наприклад, наш API буде містити книги та їхніх авторів, тоді пошук книги за конкретним автором буде виглядати: books/?author={author_id}
. Та більшість вважає, що логічніше використовувати вкладені структури у своїх API. Тоді цей самий запит виглядатиме: authors/{author_id}/books
.
Цей макет підтримується багатьма фронтенд інструментами, тому і виникає необхідність використання DRF.
Моделі
Перш за все, нам потрібні взаємопов'язані моделі, щоб загорнути наш API.
$ ./manage.py startapp shelf
Далі додаємо аплікацію і Rest Framework в settings.py
INSTALLED_APPS = [
...
'rest_framework',
'shelf',
]
Створюємо модель:
# shelf/models.py
from django.db import models
class Author(models.Model):
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
def __str__(self):
return f'{self.first_name} {self.last_name}'
class Book(models.Model):
title = models.CharField(max_length=60)
author = models.ForeignKey(Author)
def __str__(self):
return f'{self.title}'
Не забудьте застосувати
$ ./manage.py makemigrations && ./manage.py migrate
Серіалізатори
Перш за все, потрібно створити деякі серіалізатори для обробки обміну даними(за замовчуванням DRF використовує JSON, який можна замінити на XML чи YAML). Стверджують, що це все можливо реалізувати автоматично за допомогою ViewSet, проте створення API більш схоже на створення комбінаційної форми Form-View, але на іншому рівні.
Перейдемо до серіалізаторів:
# shelf/serializers.py
from rest_framework.serializers import ModelSerializer
from .models import Author, Book
class AuthorSerializer(ModelSerializer):
class Meta:
model = Author
fields = ('id', 'first_name', 'last_name')
class BookSerializer(ModelSerializer):
class Meta:
model = Book
fields = ('id', 'author', 'title')
Пам'ятайте, що мета-атрибути обов'язкові. Звісно, ви можете використати магічне значення __all__
, але рекомендовано вказувати конкретні поля. Це зробить API більш безпечним.
Набори представлень та маршрутизація
Тепер прийшов час писати базові представлення.
Застереження: зазвичай коли я пишу тільки backend api, то використовую views.py, а коли потрібно відокремити перегляди API від «нормальних» веб переглядів, можете додати цей код для прикладу в api.py. Але не забудьте відповідно оновити імпорт в кожному файлі.
# shelf/views.py
from rest_framework.viewsets import ModelViewSet
from .serializers import AuthorSerializer, BookSerializer
from .models import Author, Book
class AuthorViewSet(ModelViewSet):
serializer_class = AuthorSerializer
queryset = Author.objects.all()
class BookViewSet(ModelViewSet):
serializer_class = BookSerializer
queryset = Book.objects.all()
Фінальний штрих – створити базову маршрутизацію для API та під'єднати ViewSets:
# demo/api.py
from rest_framework.routers import DefaultRouter
from shelf.views import AuthorViewSet, BookViewSet
router = DefaultRouter()
router.register('authors', AuthorViewSet)
router.register('books', BookViewSet)
# demo/urls.py
from django.conf.urls import url, include
from django.contrib import admin
from .api import router
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/', include(router.urls))
]
Протестуємо поведінку API.
$ ./manage.py runserver
Переходимо на http://127.0.0.1:8000/api/ і заповнюємо.
Поки все гаразд, ми маємо базовий API, за допомогою якого можна створювати та редагувати дані.
Вкладені маршрутизатори
Нарешті прийшов час для основного питання цієї статті – як створити вкладену API?
Є декілька пакетів, що обробляють логіку вкладень. Ми використаємо DRF-Extensions, оскільки він найбільш функціональний.
$ pip install drf-extensions
Перш за все, потрібно додати міксін до нашого ViewSets
. Це забезпечить правильність обробки параметрів URL та фільтрування запитів:
# shelf/views.py
from rest_framework_extensions.mixins import NestedViewSetMixin
...
class AuthorViewSet(NestedViewSetMixin, ModelViewSet):
serializer_class = AuthorSerializer
queryset = Author.objects.all()
class BookViewSet(NestedViewSetMixin, ModelViewSet):
serializer_class = BookSerializer
queryset = Book.objects.all()
Необхідно розширити DefaultRouter
:
# demo/api.py
from rest_framework_extensions.routers import NestedRouterMixin
...
class NestedDefaultRouter(NestedRouterMixin, DefaultRouter):
pass
Створений NestedDefaultRouter дозволяє створювати підмаршутизатори для реєстрації вкладених кінцевих точок. Це так само просто як реєстрація звичайних маршутизаторів. Потрібно додати лише два додаткові параметри для того, щоб з'єднати все разом.
# demo/api.py
...
router = NestedDefaultRouter()
authors_router = router.register('authors', AuthorViewSet)
authors_router.register(
'books', BookViewSet,
base_name='author-books',
parents_query_lookups=['author'])
...
І пам'ятайте про деякі речі:
Base_name
повинне бути унікальним в API, це ім'я буде коренем для url імен, які використовуються функцієюreverse( )
.Parents_query_lookups
– це список зв'язків відносно батьківських моделей. Ці значення використовуються як імена для функціїfilter( )
. У нашому прикладі це буде автор у моделі книги:queryset = Book.objects.filter(author={value from url})
Після цих змін ми можемо отримати список всіх книг конкретного автора. Перезавантажте ваш сервер і перейдіть за посиланням url: http://127.0.0.1:8000/api/authors/2/books/
Подальші вкладення – це вже магія
Чим більше ви заглиблюєтеся вкладеннями, тим важче буде отримати parents_query_lookups
. Щоб проілюструвати проблему додамо до книг короткий опис.
# shelf/models.py
class Edition(models.Model):
book = models.ForeignKey(Book)
year = models.PositiveSmallIntegerField()
def __str__(self):
return f'{self.book} edition {self.year}'
# shelf/serializers.py
from .models import Edition
...
class EditionSerializer(ModelSerializer):
class Meta:
model = Edition
fields = ('id', 'book', 'year')
# shelf/views.py
from .serializers import EditionSerializer
from .models import Edition
...
class EditionViewSet(NestedViewSetMixin, ModelViewSet):
serializer_class = EditionSerializer
queryset = Edition.objects.all()
# demo/api.py
from shelf.views import EditionViewSet
...
authors_router.register(
'books', BookViewSet,
base_name='author-books',
parents_query_lookups=['author']
).register('editions',
EditionViewSet,
base_name='author-book-edition',
parents_query_lookups=['book__author', 'book']
)# shelf/models.py
class Edition(models.Model):
book = models.ForeignKey(Book)
year = models.PositiveSmallIntegerField()
def __str__(self):
return f'{self.book} edition {self.year}'
# shelf/serializers.py
from .models import Edition
...
class EditionSerializer(ModelSerializer):
class Meta:
model = Edition
fields = ('id', 'book', 'year')
# shelf/views.py
from .serializers import EditionSerializer
from .models import Edition
...
class EditionViewSet(NestedViewSetMixin, ModelViewSet):
serializer_class = EditionSerializer
queryset = Edition.objects.all()
# demo/api.py
from shelf.views import EditionViewSet
...
authors_router.register(
'books', BookViewSet,
base_name='author-books',
parents_query_lookups=['author']
).register('editions',
EditionViewSet,
base_name='author-book-edition',
parents_query_lookups=['book__author', 'book']
)
Так прикріпили інший register()
одразу ж після першого. Якщо вам потрібно додати більше кінцевих точок на цей рівень, то слід робити той самий трюк, що з authors_router
. Крім цього, будь ласка, зверніть увагу як тепер виглядає parents_query_lookups
. Можна побачити зміни застосувавши фільтр:
queryset = Edition.objects.filter(book__author={first value from url}, book={second value from url})
Тепер ви можете додати записи в БД й перевірити чи все гаразд. Не забудьте про міграції.
$ ./manage.py makemigrations && ./manage.py migrate
$ ./manage.py runserver
Відкрийте відповідний url і додайте зміни (примітка нище).
Примітка: Django REST Framework не буде фільтрувати набори запитів для вбудованого API браузера. Це означає, що вам дозволено, для прикладу, вибрати будь-якого автора, навіть якщо ви знаходитеся на певному списку книг одного з авторів.
Ще немає коментарів