Разработка на основе тестов API-интерфейса RESTful для Django

Разработка на основе тестов API-интерфейса RESTful для Django

В этом посте рассматривается процесс разработки RESTful API на основе CRUD с Django иDjango REST Framework, который используется для быстрого создания RESTful API на основе моделей Django.

Это приложение использует:

  • Python v3.6.0

  • Джанго v1.11.0

  • Django REST Framework v3.6.2

  • Postgres v9.6.1

  • Psycopg2 v2.7.1

Free Bonus:Click here to download a copy of the "REST API Examples" Guide и получите практическое введение в принципы API Python + REST с практическими примерами.

NOTE: Ознакомьтесь с третьим курсомReal Python, чтобы получить более подробное руководство по Django REST Framework.

Цели

К концу этого урока вы сможете…

  1. Обсудите преимущества использования Django REST Framework для начальной загрузки разработки RESTful API

  2. Проверка наборов запросов модели с использованием сериализаторов

  3. Цените функцию Browsable API Django REST Framework для более чистой и хорошо документированной версии ваших API.

  4. Практика разработки через тестирование

Почему Django REST Framework?

Django REST Framework (REST Framework) предоставляет ряд мощных функций из коробки, которые хорошо сочетаются с идиоматическим Django, включая:

  1. Browsable API: документирует ваш API с помощью удобного для человека вывода HTML, обеспечивая красивый интерфейс в форме формы для отправки данных в ресурсы и выборки из них с использованием стандартных методов HTTP.

  2. Auth Support: REST Framework имеет широкую поддержку различных протоколов аутентификации, а также разрешений и политик регулирования, которые можно настроить для каждого просмотра.

  3. Serializers: Сериализаторы - это элегантный способ проверки наборов запросов / экземпляров модели и преобразования их в собственные типы данных Python, которые можно легко преобразовать в JSON и XML.

  4. Throttling: регулирование - это способ определить, авторизован ли запрос или нет, и его можно интегрировать с различными разрешениями. Обычно используется для ограничения скорости запросов API от одного пользователя.

Кроме того, документация легко читается и полна примеров. Если вы создаете RESTful API, где у вас есть взаимно-однозначные отношения между конечными точками API и вашими моделями, тогда REST Framework - это то, что вам нужно.

Настройка проекта Django

Создайте и активируйте virtualenv:

$ mkdir django-puppy-store
$ cd django-puppy-store
$ python3.6 -m venv env
$ source env/bin/activate

Установите Django и настройте новый проект:

(env)$ pip install django==1.11.0
(env)$ django-admin startproject puppy_store

Ваша текущая структура проекта должна выглядеть следующим образом:

└── puppy_store
    ├── manage.py
    └── puppy_store
        ├── __init__.py
        ├── settings.py
        ├── urls.py
        └── wsgi.py

Настройка приложения Django и REST Framework

Начнем с создания приложенияpuppies иinstalling REST Framework inside your virtualenv:

(env)$ cd puppy_store
(env)$ python manage.py startapp puppies
(env)$ pip install djangorestframework==3.6.2

Теперь нам нужно настроить наш проект Django для использования REST Framework.

Сначала добавьте приложениеpuppies иrest_framework в разделINSTALLED_APPS вpuppy_store/puppy_store/settings.py:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'puppies',
    'rest_framework'
]

Затем определите глобальныйsettings для REST Framework в одном словаре, опять же, в файлеsettings.py:

REST_FRAMEWORK = {
    # Use Django's standard `django.contrib.auth` permissions,
    # or allow read-only access for unauthenticated users.
    'DEFAULT_PERMISSION_CLASSES': [],
    'TEST_REQUEST_DEFAULT_FORMAT': 'json'
}

Это обеспечивает неограниченный доступ к API и устанавливает формат теста по умолчанию JSON для всех запросов.

NOTE: Неограниченный доступ подходит для локальной разработки, но в производственной среде вам может потребоваться ограничить доступ к определенным конечным точкам. Обязательно обновите это. Просмотритеdocs для получения дополнительной информации.

Ваша текущая структура проекта теперь должна выглядеть так:

└── puppy_store
    ├── manage.py
    ├── puppies
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── migrations
    │   │   └── __init__.py
    │   ├── models.py
    │   ├── tests.py
    │   └── views.py
    └── puppy_store
        ├── __init__.py
        ├── settings.py
        ├── urls.py
        └── wsgi.py

Настройка базы данных и модели

Давайте настроим базу данных Postgres и применим к ней все миграции.

NOTE: Не стесняйтесь заменить Postgres на реляционную базу данных по вашему выбору!

Когда в вашей системе будет работающий сервер Postgres, откройте интерактивную оболочку Postgres и создайте базу данных:

$ psql
# CREATE DATABASE puppy_store_drf;
CREATE DATABASE
# \q

Установитеpsycopg2, чтобы мы могли взаимодействовать с сервером Postgres через Python:

(env)$ pip install psycopg2==2.7.1

Обновите конфигурацию базы данных вsettings.py, добавив соответствующие имя пользователя и пароль:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'puppy_store_drf',
        'USER': '',
        'PASSWORD': '',
        'HOST': '127.0.0.1',
        'PORT': '5432'
    }
}

Затем определите модель щенка с некоторыми основными атрибутами вdjango-puppy-store/puppy_store/puppies/models.py:

from django.db import models


class Puppy(models.Model):
    """
    Puppy Model
    Defines the attributes of a puppy
    """
    name = models.CharField(max_length=255)
    age = models.IntegerField()
    breed = models.CharField(max_length=255)
    color = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def get_breed(self):
        return self.name + ' belongs to ' + self.breed + ' breed.'

    def __repr__(self):
        return self.name + ' is added.'

Теперьapply the migration:

(env)$ python manage.py makemigrations
(env)$ python manage.py migrate

Санитарная проверка

Снова перейдите вpsql и убедитесь, чтоpuppies_puppy создан:

$ psql
# \c puppy_store_drf
You are now connected to database "puppy_store_drf".
puppy_store_drf=# \dt
                      List of relations
 Schema |            Name            | Type  |     Owner
--------+----------------------------+-------+----------------
 public | auth_group                 | table | michael.herman
 public | auth_group_permissions     | table | michael.herman
 public | auth_permission            | table | michael.herman
 public | auth_user                  | table | michael.herman
 public | auth_user_groups           | table | michael.herman
 public | auth_user_user_permissions | table | michael.herman
 public | django_admin_log           | table | michael.herman
 public | django_content_type        | table | michael.herman
 public | django_migrations          | table | michael.herman
 public | django_session             | table | michael.herman
 public | puppies_puppy              | table | michael.herman
(11 rows)

NOTE: Вы можете запустить\d+ puppies_puppy, если хотите посмотреть фактические детали таблицы.

Прежде чем двигаться дальше, напишемquick unit test для модели Puppy.

Добавьте следующий код в новый файл с именемtest_models.py в новой папке с именем «tests» в «django-puppy-store / puppy_store / puppies»:

from django.test import TestCase
from ..models import Puppy


class PuppyTest(TestCase):
    """ Test module for Puppy model """

    def setUp(self):
        Puppy.objects.create(
            name='Casper', age=3, breed='Bull Dog', color='Black')
        Puppy.objects.create(
            name='Muffin', age=1, breed='Gradane', color='Brown')

    def test_puppy_breed(self):
        puppy_casper = Puppy.objects.get(name='Casper')
        puppy_muffin = Puppy.objects.get(name='Muffin')
        self.assertEqual(
            puppy_casper.get_breed(), "Casper belongs to Bull Dog breed.")
        self.assertEqual(
            puppy_muffin.get_breed(), "Muffin belongs to Gradane breed.")

В приведенном выше тесте мы добавили фиктивные записи в нашу таблицу puppy с помощью методаsetUp() изdjango.test.TestCase и подтвердили, что методget_breed() вернул правильную строку.

Добавьте файлinit.py в «тесты» и удалите файлtests.py из «django-puppy-store / puppy_store / puppies».

Давайте запустим наш первый тест:

(env)$ python manage.py test
Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.007s

OK
Destroying test database for alias 'default'...

Большой! Наш первый модульный тест прошел!

сериализаторы

Прежде чем перейти к созданию реального API, давайте определимserializer для нашей модели Puppy, которая проверяет модельquerysets и создает типы данных Pythonic для работы.

Добавьте следующий фрагмент вdjango-puppy-store/puppy_store/puppies/serializers.py:

from rest_framework import serializers
from .models import Puppy


class PuppySerializer(serializers.ModelSerializer):
    class Meta:
        model = Puppy
        fields = ('name', 'age', 'breed', 'color', 'created_at', 'updated_at')

В приведенном выше фрагменте мы определилиModelSerializer для нашей модели щенка, проверив все упомянутые поля. Короче говоря, если у вас есть взаимно-однозначные отношения между конечными точками API и вашими моделями - что, вероятно, и должно быть, если вы создаете RESTful API - тогда вы можете использоватьModelSerializer для создания сериализатора.

Имея нашу базу данных, мы можем приступить к созданию RESTful API…

RESTful Структура

В RESTful API конечные точки (URL) определяют структуру API и то, как конечные пользователи получают доступ к данным из нашего приложения с помощью методов HTTP - GET, POST, PUT, DELETE. Конечные точки должны быть логически организованы вокругcollections иelements, которые являются ресурсами.

В нашем случае у нас есть один единственный ресурс,puppies, поэтому мы будем использовать следующие URL-адреса -/puppies/ и/puppies/<id> для коллекций и элементов соответственно:

Конечная точка HTTP метод CRUD метод Результат

puppies

GET

READ

Получить всех щенков

puppies/:id

GET

READ

Завести одного щенка

puppies

POST

СОЗДАЙТЕ

Добавить одного щенка

puppies/:id

PUT

ОБНОВИТЬ

Обновить одного щенка

puppies/:id

УДАЛЯТЬ

УДАЛЯТЬ

Удалить одного щенка

Маршруты и тестирование (TDD)

Мы будем использовать подход, основанный на тестировании, а не тщательный подход, основанный на тестировании, при котором мы будем проходить через следующий процесс:

  • добавить модульный тест, достаточно кода, чтобы потерпеть неудачу

  • затем обновите код, чтобы он прошел тест.

Как только тест пройден, начните все заново с того же процесса для нового теста.

Начнем с создания нового файлаdjango-puppy-store/puppy_store/puppies/tests/test_views.py для хранения всех тестов для наших представлений и создания нового тестового клиента для нашего приложения:

import json
from rest_framework import status
from django.test import TestCase, Client
from django.urls import reverse
from ..models import Puppy
from ..serializers import PuppySerializer


# initialize the APIClient app
client = Client()

Прежде чем начинать со всеми маршрутами API, давайте сначала создадим скелет всех функций представления, которые возвращают пустые ответы, и сопоставим их с соответствующими URL-адресами в файлеdjango-puppy-store/puppy_store/puppies/views.py:

from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from .models import Puppy
from .serializers import PuppySerializer


@api_view(['GET', 'DELETE', 'PUT'])
def get_delete_update_puppy(request, pk):
    try:
        puppy = Puppy.objects.get(pk=pk)
    except Puppy.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    # get details of a single puppy
    if request.method == 'GET':
        return Response({})
    # delete a single puppy
    elif request.method == 'DELETE':
        return Response({})
    # update details of a single puppy
    elif request.method == 'PUT':
        return Response({})


@api_view(['GET', 'POST'])
def get_post_puppies(request):
    # get all puppies
    if request.method == 'GET':
        return Response({})
    # insert a new record for a puppy
    elif request.method == 'POST':
        return Response({})

Создайте соответствующие URL-адреса для соответствия представлениям вdjango-puppy-store/puppy_store/puppies/urls.py:

from django.conf.urls import url
from . import views


urlpatterns = [
    url(
        r'^api/v1/puppies/(?P[0-9]+)$',
        views.get_delete_update_puppy,
        name='get_delete_update_puppy'
    ),
    url(
        r'^api/v1/puppies/$',
        views.get_post_puppies,
        name='get_post_puppies'
    )
]

Также обновитеdjango-puppy-store/puppy_store/puppy_store/urls.py:

from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url(r'^', include('puppies.urls')),
    url(
        r'^api-auth/',
        include('rest_framework.urls', namespace='rest_framework')
    ),
    url(r'^admin/', admin.site.urls),
]

Браузерный API

Теперь, когда все маршруты подключены к функциям просмотра, давайте откроем интерфейс Browserable API REST Framework и проверим, все ли URL работают должным образом.

Сначала запустите сервер разработки:

(env)$ python manage.py runserver

Не забудьте закомментировать все атрибуты в разделеREST_FRAMEWORK нашего файлаsettings.py, чтобы обойти вход в систему. Теперь посетитеhttp://localhost:8000/api/v1/puppies

Вы увидите интерактивный HTML-макет для ответа API. Точно так же мы можем проверить другие URL и убедиться, что все URL работают отлично.

Давайте начнем с наших юнит-тестов для каждого маршрута.

Маршруты

ПОЛУЧИТЬ ВСЕ

Начните с теста, чтобы проверить извлеченные записи:

class GetAllPuppiesTest(TestCase):
    """ Test module for GET all puppies API """

    def setUp(self):
        Puppy.objects.create(
            name='Casper', age=3, breed='Bull Dog', color='Black')
        Puppy.objects.create(
            name='Muffin', age=1, breed='Gradane', color='Brown')
        Puppy.objects.create(
            name='Rambo', age=2, breed='Labrador', color='Black')
        Puppy.objects.create(
            name='Ricky', age=6, breed='Labrador', color='Brown')

    def test_get_all_puppies(self):
        # get API response
        response = client.get(reverse('get_post_puppies'))
        # get data from db
        puppies = Puppy.objects.all()
        serializer = PuppySerializer(puppies, many=True)
        self.assertEqual(response.data, serializer.data)
        self.assertEqual(response.status_code, status.HTTP_200_OK)

Запустите тест. Вы должны увидеть следующую ошибку:

self.assertEqual(response.data, serializer.data)
AssertionError: {} != [OrderedDict([('name', 'Casper'), ('age',[687 chars])])]

Обновите представление, чтобы пройти тест.

@api_view(['GET', 'POST'])
def get_post_puppies(request):
    # get all puppies
    if request.method == 'GET':
        puppies = Puppy.objects.all()
        serializer = PuppySerializer(puppies, many=True)
        return Response(serializer.data)
    # insert a new record for a puppy
    elif request.method == 'POST':
        return Response({})

Здесь мы получаем все записи для щенков и проверяем каждую, используяPuppySerializer.

Запустите тесты, чтобы убедиться, что они все прошли:

Ran 2 tests in 0.072s

OK

ПОЛУЧИТЬ Одноместный

Выбор одного щенка включает два теста:

  1. Получить действительного щенка - например, щенок существует

  2. Получите недействительного щенка - например, щенок не существует

Добавьте тесты:

class GetSinglePuppyTest(TestCase):
    """ Test module for GET single puppy API """

    def setUp(self):
        self.casper = Puppy.objects.create(
            name='Casper', age=3, breed='Bull Dog', color='Black')
        self.muffin = Puppy.objects.create(
            name='Muffin', age=1, breed='Gradane', color='Brown')
        self.rambo = Puppy.objects.create(
            name='Rambo', age=2, breed='Labrador', color='Black')
        self.ricky = Puppy.objects.create(
            name='Ricky', age=6, breed='Labrador', color='Brown')

    def test_get_valid_single_puppy(self):
        response = client.get(
            reverse('get_delete_update_puppy', kwargs={'pk': self.rambo.pk}))
        puppy = Puppy.objects.get(pk=self.rambo.pk)
        serializer = PuppySerializer(puppy)
        self.assertEqual(response.data, serializer.data)
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_get_invalid_single_puppy(self):
        response = client.get(
            reverse('get_delete_update_puppy', kwargs={'pk': 30}))
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

Запустите тесты. Вы должны увидеть следующую ошибку:

self.assertEqual(response.data, serializer.data)
AssertionError: {} != {'name': 'Rambo', 'age': 2, 'breed': 'Labr[109 chars]26Z'}

Обновить вид:

@api_view(['GET', 'UPDATE', 'DELETE'])
def get_delete_update_puppy(request, pk):
    try:
        puppy = Puppy.objects.get(pk=pk)
    except Puppy.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    # get details of a single puppy
    if request.method == 'GET':
        serializer = PuppySerializer(puppy)
        return Response(serializer.data)

В приведенном фрагменте мы получаем щенка с помощью идентификатора. Запустите тесты, чтобы убедиться, что они все прошли.

POST

Вставка новой записи также включает два случая:

  1. Вставка действительного щенка

  2. Вставка неверного щенка

Сначала напишите для него тесты:

class CreateNewPuppyTest(TestCase):
    """ Test module for inserting a new puppy """

    def setUp(self):
        self.valid_payload = {
            'name': 'Muffin',
            'age': 4,
            'breed': 'Pamerion',
            'color': 'White'
        }
        self.invalid_payload = {
            'name': '',
            'age': 4,
            'breed': 'Pamerion',
            'color': 'White'
        }

    def test_create_valid_puppy(self):
        response = client.post(
            reverse('get_post_puppies'),
            data=json.dumps(self.valid_payload),
            content_type='application/json'
        )
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)

    def test_create_invalid_puppy(self):
        response = client.post(
            reverse('get_post_puppies'),
            data=json.dumps(self.invalid_payload),
            content_type='application/json'
        )
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

Запустите тесты. Вы должны увидеть две ошибки:

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
AssertionError: 200 != 400

self.assertEqual(response.status_code, status.HTTP_201_CREATED)
AssertionError: 200 != 201

Опять же, обновите представление, чтобы пройти тесты:

@api_view(['GET', 'POST'])
def get_post_puppies(request):
    # get all puppies
    if request.method == 'GET':
        puppies = Puppy.objects.all()
        serializer = PuppySerializer(puppies, many=True)
        return Response(serializer.data)
    # insert a new record for a puppy
    if request.method == 'POST':
        data = {
            'name': request.data.get('name'),
            'age': int(request.data.get('age')),
            'breed': request.data.get('breed'),
            'color': request.data.get('color')
        }
        serializer = PuppySerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Здесь мы вставили новую запись путем сериализации и проверки данных запроса перед вставкой в ​​базу данных.

Запустите тесты снова, чтобы убедиться, что они прошли.

Вы также можете проверить это с помощью Browsable API. Снова запустите сервер разработки и перейдите кhttp://localhost:8000/api/v1/puppies/. Затем в форме POST отправьте следующее какapplication/json:

{
    "name": "Muffin",
    "age": 4,
    "breed": "Pamerion",
    "color": "White"
}

Будьте уверены, что GET ALL и Get Single работают также.

PUT

Начните с теста, чтобы обновить запись. Подобно добавлению записи, нам снова нужно проверить как действительные, так и недействительные обновления:

class UpdateSinglePuppyTest(TestCase):
    """ Test module for updating an existing puppy record """

    def setUp(self):
        self.casper = Puppy.objects.create(
            name='Casper', age=3, breed='Bull Dog', color='Black')
        self.muffin = Puppy.objects.create(
            name='Muffy', age=1, breed='Gradane', color='Brown')
        self.valid_payload = {
            'name': 'Muffy',
            'age': 2,
            'breed': 'Labrador',
            'color': 'Black'
        }
        self.invalid_payload = {
            'name': '',
            'age': 4,
            'breed': 'Pamerion',
            'color': 'White'
        }

    def test_valid_update_puppy(self):
        response = client.put(
            reverse('get_delete_update_puppy', kwargs={'pk': self.muffin.pk}),
            data=json.dumps(self.valid_payload),
            content_type='application/json'
        )
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

    def test_invalid_update_puppy(self):
        response = client.put(
            reverse('get_delete_update_puppy', kwargs={'pk': self.muffin.pk}),
            data=json.dumps(self.invalid_payload),
            content_type='application/json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

Запустите тесты.

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
AssertionError: 405 != 400

self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
AssertionError: 405 != 204

Обновить вид:

@api_view(['GET', 'DELETE', 'PUT'])
def get_delete_update_puppy(request, pk):
    try:
        puppy = Puppy.objects.get(pk=pk)
    except Puppy.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    # get details of a single puppy
    if request.method == 'GET':
        serializer = PuppySerializer(puppy)
        return Response(serializer.data)

    # update details of a single puppy
    if request.method == 'PUT':
        serializer = PuppySerializer(puppy, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_204_NO_CONTENT)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    # delete a single puppy
    elif request.method == 'DELETE':
        return Response({})

В приведенном выше фрагменте, аналогичном вставке, мы сериализуем и проверяем данные запроса, а затем отвечаем соответствующим образом.

Запустите тесты снова, чтобы убедиться, что все тесты пройдены.

УДАЛЯТЬ

Для удаления отдельной записи требуется идентификатор:

class DeleteSinglePuppyTest(TestCase):
    """ Test module for deleting an existing puppy record """

    def setUp(self):
        self.casper = Puppy.objects.create(
            name='Casper', age=3, breed='Bull Dog', color='Black')
        self.muffin = Puppy.objects.create(
            name='Muffy', age=1, breed='Gradane', color='Brown')

    def test_valid_delete_puppy(self):
        response = client.delete(
            reverse('get_delete_update_puppy', kwargs={'pk': self.muffin.pk}))
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)

    def test_invalid_delete_puppy(self):
        response = client.delete(
            reverse('get_delete_update_puppy', kwargs={'pk': 30}))
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

Запустите тесты. Тебе следует увидеть:

self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
AssertionError: 200 != 204

Обновить вид:

@api_view(['GET', 'DELETE', 'PUT'])
def get_delete_update_puppy(request, pk):
    try:
        puppy = Puppy.objects.get(pk=pk)
    except Puppy.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    # get details of a single puppy
    if request.method == 'GET':
        serializer = PuppySerializer(puppy)
        return Response(serializer.data)

    # update details of a single puppy
    if request.method == 'PUT':
        serializer = PuppySerializer(puppy, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_204_NO_CONTENT)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    # delete a single puppy
    if request.method == 'DELETE':
        puppy.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

Запустите тесты снова. Убедитесь, что все они прошли. Обязательно проверьте функциональность UPDATE и DELETE в Browsable API!

Заключение и последующие шаги

В этом руководстве мы рассмотрели процесс создания RESTful API с использованием Django REST Framework с подходом, основанным на тестировании.

Free Bonus:Click here to download a copy of the "REST in a Nutshell" Guide с практическим введением в принципы и примеры REST API.

Что дальше? Чтобы сделать наш RESTful API надежным и безопасным, мы можем реализовать разрешения и регулирование для производственной среды, чтобы разрешить ограниченный доступ на основе учетных данных аутентификации и ограничения скорости, чтобы избежать любого рода DDoS-атак. Кроме того, не забудьте запретить доступ к API Browsable в производственной среде.

Не стесняйтесь делиться своими комментариями, вопросами или советами в комментариях ниже. Полный код можно найти в репозиторииdjango-puppy-store.

Related