Вкладені API за допомогою Django REST Framework

8 хв. читання
07 листопада 2017

У цій статті ви навчитеся створювати простий 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 за допомогою Django REST Framework

Поки все гаразд, ми маємо базовий 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/

Вкладені API за допомогою Django REST Framework

Подальші вкладення – це вже магія

Чим більше ви заглиблюєтеся вкладеннями, тим важче буде отримати 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 і додайте зміни (примітка нище). Вкладені API за допомогою Django REST Framework

Примітка: Django REST Framework не буде фільтрувати набори запитів для вбудованого API браузера. Це означає, що вам дозволено, для прикладу, вибрати будь-якого автора, навіть якщо ви знаходитеся на певному списку книг одного з авторів.

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.6K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація