Рубрики
Без рубрики

Эффективное развитие API с Python3 и Django Rest Framework

Целью этого урока является создание API для создания и списка подписчиков. В этом руководстве мы будем … с меткой Python, учебник, Django, API.

Целью этого урока является создание API для создания и списка подписчиков. В этом уроке мы осмотрим данные, создаем модели, сериализаторы и представления, время разницы между созданием и объемом создания и узнать, как фильтровать запрос на запрос. Известные библиотеки мы будем использовать включать Джанго и Django Rest Framework Отказ

  • Установка библиотек
  • Создание проекта Django и приложения
  • Структура проекта
  • Понимание данных
  • Создание моделей
  • Создание сериализаторов
  • Создание просмотров
  • Создание маршрутов (URL)
  • Добавление команды для тестовых данных
  • Создание насыпного сериализатора
  • Запрошеная фильтрация

Установка Django и Django Ress Framework

PIP3 Установить Django DjangorestFramework

Создание проекта Django и приложение Django

  1. Создайте рабочий каталог для проекта Django

  2. Создайте проект Django

  3. Создайте приложение Django для подписчиков

  4. Подсоедините подписчики и приложение REST_FRAMEWORK к настройкам API в API/Settings.py.

    Добавьте следующее в нижней части настроек для результатов списка построек

Структура проекта

./django_api_tutorial
│   manage.py
│   README.md
│
├───api
│       asgi.py
│       settings.py
│       urls.py
│       wsgi.py
│       __init__.py
│
├───data
│       fake_users.csv
│
└───subscribers
    │   admin.py
    │   apps.py
    │   models.py
    │   serializers.py
    │   tests.py
    │   urls.py
    │   views.py
    │   __init__.py
    │
    ├───management
    │   └───commands
    │           bulktestdata.py
    │           testdata.py
    │
    └───migrations
            __init__.py

Узнайте больше о структуре проекта Django здесь Отказ

Понимание данных

Вот первые 10 рядов поддельных данных для учебника. Эти данные были созданы с использованием Mockaroo Отказ

Водонациональный MPoAd0@cisco.com. Массачусетс Мужчина Преподаватель Мохаммед
Индианаполис bliddall1@odnoklassniki.ru. Индиана женский Liddall. Briana
Brococton jpattington2@telegraph.co.uk. Массачусетс Мужчина Паттингтон Jodie.
Ричмонд cworcs3@youku.com. Вирджиния женский Вурать Кари
Ньюарк spickford4@arstechnica.com. Нью-Джерси Мужчина Пикфорд Шнеф
Evansville. bmccolm5@comsenz.com. Индиана женский Маккольм Бетани
Лексингтон ewyriill6@opera.com. Кентукки женский Соревнование Elsi.
Колумбия Khartill7@webeden.co.uk. Южная Каролина женский Хартилл Кылинн
Джолиет lelleiot8@msn.com. Иллинойс Мужчина Эллиот Лонни
Сан Бернардино kkelso9@sbwire.com. Калифорния женский Kelso Kellyann.

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

first_name      VARCHAR(64)
last_name       VARCHAR(64)
email           TEXT
gender          VARCHAR(8)
city            VARCHAR(256)
state           VARCHAR(24)

Знание того, как мы хотим обрабатывать каждую из этих переменных, собирается позволить нам легко создавать модели и сериализаторы.

Создание моделей

Определения модели можно найти в Подписчики/Модели .py Отказ

from django.db import models

class Location(models.Model):
    city = models.CharField(null=False, max_length=256)
    state = models.CharField(null=False, max_length=64)

class Subscriber(models.Model):
    first_name = models.CharField(null=False, max_length=64)
    last_name = models.CharField(null=False, max_length=64)
    email = models.TextField()
    gender = models.CharField(null=False, max_length=8)
    location = models.ForeignKey(Location, related_name='subscriber_location', on_delete=models.DO_NOTHING)

Выше мы определили модели как для абонента, так и для местоположения. Модель подписчика – это много до одной связи с моделью расположения, поскольку у нас может быть много подписчиков в одном месте.

Создание сериализаторов

Теперь мы определим сериализаторы для моделей. Это позволяет нам легко преобразовывать объекты модели в объекты JSON для ответов API. Когда мы создаем сериализаторы, мы можем легко добавить много функциональности на API с помощью Django Rest Framework.

Сериализаторы определены в Подписчики/serializers.py Отказ Обратите внимание, что в подписчике, мы должны перезаписать метод создания. Это нормально, когда вы используете реляционные модели.

from rest_framework import serializers

class LocationSerializer(serializers.Serializer):
    city = serializers.CharField(required=True, max_length=256)
    state = serializers.CharField(required=True, max_length=64)

    class Meta:
        fields = ['city', 'state']

class SubscriberSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)
    created = serializers.DateTimeField(required=False)
    first_name = serializers.CharField(required=True, max_length=64)
    last_name = serializers.CharField(required=True, max_length=64)
    email = serializers.CharField(required=False)
    gender = serializers.CharField(required=True, max_length=8)
    location = LocationSerializer(required=True)

    class Meta:
        fields = ['first_name', 'last_name', 'email', 'gender', 'location']
        read_only_fields = ['id', 'created']

    def create(self, validated_data):
        # remove location from serialized data and add model object
        location = validated_data.pop('location')
        city = location.get('city', None)
        state = location.get('state', None)

        if not city and not state:
            raise serializers.ValidationError('No location input found')

        # call get or create to reuse location objects
        location_obj = Location.objects.get_or_create(city=city, state=state)[0]
        # add location back to validated data
        validated_data.update({'location': location_obj})

        # unpack validated_data to create a new Subscriber object
        return Subscriber.objects.create(**validated_data)

Теперь мы определили сериализаторы, чтобы мы могли легко сделать такие вещи, как создать, чтение, обновление, удаление и список объектов из моделей. Мы увидим это в действии, когда мы реализуем взгляды.

Создание просмотров

Представления определены в Подписчики/просмотр .py и содержат функциональность, которая будет доступна пользователям API. В этом руководстве мы сосредоточимся на умерении создания и списка подписчиков, но вместе с добавлением данных, которые мы также, вероятно, понадобится способ легко удалить данные. Я дам пример, как легко добавить операцию удаления в API, используя Mixins Mixins Mixins Django Rest.

from rest_framework import viewsets, mixins

from .models import Subscriber
from .serializers import SubscriberSerializer

class SubscriberView(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.CreateModelMixin):
    queryset = Subscriber.objects.all()
    serializer_class = SubscriberSerializer

Этот простой вид выше, обеспечивает универсальный интерфейс API со списком и создание функциональности для подписчиков. Чтобы легко добавить метод удаления и функциональность, оно будет выглядеть следующее:

from rest_framework import viewsets, mixins

from .models import Subscriber
from .serializers import SubscriberSerializer

class SubscriberView(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.CreateModelMixin,
                     mixins.DestroyModelMixin):
    queryset = Subscriber.objects.all()
    serializer_class = SubscriberSerializer

Обратите внимание, что единственное новое изменение было добавлением Микс. DestroyModelmixin в определении класса. Больше информации о миксах можно найти на рамках отдыха Django документы .

Создание маршрутов

Теперь, чтобы иметь возможность перейти к API, нам нужно добавить URL. URL-адреса определены в Подписчики/URLS.PY Отказ Мы также захотите сообщить базовую API, где найти маршруты из подписчиков, и это определено в API/URLS.PY Отказ

# api/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('tutorial/', include('subscribers.urls'))
]
# subscribers/urls.py
from rest_framework import routers

from .views import SubscriberView

router = routers.DefaultRouter(trailing_slash=False)
router.register(r'subscribers', SubscriberView, basename='subscribers')
urlpatterns = router.urls

В URL-адресах подписчиков мы используем маршрутизатор из Django Ress Framework, чтобы легко добавить все функциональные возможности, которые мы определили в представлениях. Маршрутизатор со просмотром включил следующие маршруты для API.

GET     /tutorial/subscribers       List view of subscribers
POST    /tutorial/subscribers       Create a new subscriber

Добавление команды для тестовых данных

Теперь мы добавим команду Django, чтобы легко позволить нам добавить тестовые данные на API. Команды определены в Подписчики/Management/Commands И первая команда названа testdata.py Отказ

import csv

from time import time
from django.core.management.base import BaseCommand, CommandError

from subscribers.serializers import SubscriberSerializer

class Command(BaseCommand):
    help = 'Adds the fake test data to the API'

    def handle(self, *args, **options):
        try:
            with open('data/fake_users.csv', 'r') as fin:
                csvreader = csv.reader(fin)
                headers = next(csvreader)
                data = [{'first_name': row[0],
                         'last_name': row[1],
                         'email': row[2],
                         'gender': row[3],
                         'location': {'city': row[4], 'state': row[5]}
                         } for row in csvreader
                        ]
                # time how fast it takes to add all records 1 by 1
                start = time()
                for item in data:
                    serializer = SubscriberSerializer(data=item)
                    if serializer.is_valid():
                        serializer.create(item)
                stop = time()
                print(f'{len(data)} items added in {stop-start} seconds')
        except FileExistsError:
            raise CommandError('No testdata found')

Эта команда добавит тестовые записи на API. Он также отслеживает, сколько записей и как быстро они были добавлены. Мы запустим команду и посмотрим, какая вывод.

python3 manage.py testdata

Выход: 6000 пунктов Добавлено в 31.6553955078125

Затем мы будем реализовать объемный сериализатор и добавить новую большую команду, чтобы увидеть, сможем ли мы ускорить создание записей.

Создание насыпного сериализатора

Вместо того, чтобы создавать объекты один за другим, мы создадим объемный сериализатор для создания многих одновременно.

class BulkSubscriberSerializer(serializers.Serializer):
    subscribers = SubscriberSerializer(many=True)

    class Meta:
        fields = ['subscribers']

    def create(self, validated_data):
        # store the Subscriber objects to be created in bulk
        create_objects_list = []
        # iterate over the validated_data and add Subscriber objects to a list to be created
        for data in validated_data:
            # notice the same functionality from the regular serializer
            location = data.pop('location')
            city = location.get('city', None)
            state = location.get('state', None)
            location_obj = Location.objects.get_or_create(city=city, state=state)[0]
            # combine data and {'location': location_obj} and unpack to the Subscriber model
            create_objects_list.append(Subscriber(**{**data, **{'location': location_obj}}))
        return Subscriber.objects.bulk_create(create_objects_list)

Мы также создадим новую команду под названием Bulktestdata это определено в Подписчики/Управление/Команды/Bulktestdata.py Отказ Это будет использовать объемный сериализатор для добавления записей и отслеживать, сколько времени требуется.

import csv

from time import time
from django.core.management.base import BaseCommand, CommandError

from subscribers.serializers import BulkSubscriberSerializer

class Command(BaseCommand):
    help = 'Adds the fake test data to the API'

    def handle(self, *args, **options):
        try:
            with open('data/fake_users.csv', 'r') as fin:
                csvreader = csv.reader(fin)
                headers = next(csvreader)
                data = [{'first_name': row[0],
                         'last_name': row[1],
                         'email': row[2],
                         'gender': row[3],
                         'location': {'city': row[4], 'state': row[5]}
                         } for row in csvreader
                        ]
                # time how fast it takes to add records in bulk
                start = time()
                bulk_serializer = BulkSubscriberSerializer(data={'subscribers': data})
                if bulk_serializer.is_valid():
                    bulk_serializer.create(data)
                stop = time()
                print(f'{len(data)} items added in {stop-start} seconds')
        except FileExistsError:
            raise CommandError('No testdata found')

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

python3 manage.py bulktestdata

Выход: 6000 пунктов Добавлено в 5.3229029178619385 секунды

Наконец, мы обновим представления для использования обычного или объемного серианизма на основе данных, отправленных на маршрут.

from rest_framework import viewsets, mixins
from rest_framework.response import Response

from .models import Subscriber
from .serializers import SubscriberSerializer, BulkSubscriberSerializer

class SubscriberView(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.CreateModelMixin,
                     mixins.DestroyModelMixin):
    queryset = Subscriber.objects.all()
    serializer_class = SubscriberSerializer

    def create(self, request, *args, **kwargs):
        # if the data is a dictionary, use parent create that relies on serializer_class
        if isinstance(request.data, dict):
            return super(SubscriberView, self).create(request, *args, **kwargs)
        # if the data is a list, send to the bulk serializer to handle creation
        elif isinstance(request.data, list):
            serializer = BulkSubscriberSerializer(data={'subscribers': request.data})
            if serializer.is_valid():
                serializer.create(request.data)
                return Response(serializer.data, status=201)
            else:
                return Response(serializer.errors, status=400)
        else:
            return Response('Invalid data received', status=400)

На данный момент API теперь может создать один со многими записями за раз и позволяет пользователям просматривать текущие подписчики. Вот фрагмент ответа от API для получения запроса http://127.0.0.1:8000/tutorial/subscribers Отказ

{
    "count": 6000,
    "next": "http://127.0.0.1:8000/tutorial/subscribers?page=2",
    "previous": null,
    "results": [
        {
            "id": 1,
            "created": "2020-10-13T15:51:50.850563Z",
            "first_name": "Mohammed",
            "last_name": "Poad",
            "email": "mpoad0@cisco.com",
            "gender": "Male",
            "location": {
                "city": "Watertown",
                "state": "Massachusetts"
            }
        },
        {
            "id": 2,
            "created": "2020-10-13T15:51:50.862560Z",
            "first_name": "Briana",
            "last_name": "Liddall",
            "email": "bliddall1@odnoklassniki.ru",
            "gender": "Female",
            "location": {
                "city": "Indianapolis",
                "state": "Indiana"
            }
        },
        ...
    ]
}

Запрошеная фильтрация

Мы можем легко перечислить все подписчики, но, если мы хотим только увидеть подписчиков из определенного состояния? В настоящее время, как пользователь, нам пришлось бы потянуть всех подписчиков из API и отфильтровать наши собственные результаты. Это где фильтрация на запрос от Django может помочь дать пользователям больше контроля. Пользователь может отправлять параметр запроса в запросе, и мы можем использовать его для фильтрации результатов. Новый вид будет выглядеть следующее.

from rest_framework import viewsets, mixins
from rest_framework.response import Response

from .models import Subscriber
from .serializers import SubscriberSerializer, BulkSubscriberSerializer

class SubscriberView(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.CreateModelMixin,
                     mixins.DestroyModelMixin):
    serializer_class = SubscriberSerializer

    def get_queryset(self):
        queryset = Subscriber.objects
        if 'state' in self.request.query_params:
            queryset = queryset.filter(location__state__icontains=self.request.query_params['state'])
        return queryset.order_by('created')


    def create(self, request, *args, **kwargs):
        # if the data is a dictionary, use parent create that relies on serializer class
        if isinstance(request.data, dict):
            return super(SubscriberView, self).create(request, *args, **kwargs)
        # if the data is a list, send to the bulk serializer to handle creation
        elif isinstance(request.data, list):
            serializer = BulkSubscriberSerializer(data={'subscribers': request.data})
            if serializer.is_valid():
                serializer.create(request.data)
                return Response(serializer.data, status=201)
            else:
                return Response(serializer.errors, status=400)
        else:
            return Response('Invalid data received', status=400)

Мы добавили get_queryset Метод и теперь можно отправить Государство в качестве параметра запроса в запросе Get. Например, если мы отправим запрос на получение http://127.0.0.1:8000/tutorial/subscribers?state. = Texas мы можем видеть, что у нас меньше всего результатов.

{
    "count": 629,
    "next": "http://127.0.0.1:8000/tutorial/subscribers?page=2&state=Texas",
    "previous": null,
    "results": [
        {
            "id": 13,
            "created": "2020-10-13T19:51:29.461522Z",
            "first_name": "Laure",
            "last_name": "Chitter",
            "email": "lchitterc@t-online.de",
            "gender": "Female",
            "location": {
                "city": "Corpus Christi",
                "state": "Texas"
            }
        },
        ...
    ]
}

Теперь у нас есть API, который может создать один ко многим абонентам на основе полезной нагрузки, список всех абонентов и абонентов список из определенного состояния. Надеюсь, вам понравилось учебное пособие, весь код можно найти здесь Отказ

Оригинал: “https://dev.to/tannerburns/efficient-api-development-with-python3-and-django-rest-framework-ec9”