Целью этого урока является создание API для создания и списка подписчиков. В этом уроке мы осмотрим данные, создаем модели, сериализаторы и представления, время разницы между созданием и объемом создания и узнать, как фильтровать запрос на запрос. Известные библиотеки мы будем использовать включать Джанго и Django Rest Framework Отказ
- Установка библиотек
- Создание проекта Django и приложения
- Структура проекта
- Понимание данных
- Создание моделей
- Создание сериализаторов
- Создание просмотров
- Создание маршрутов (URL)
- Добавление команды для тестовых данных
- Создание насыпного сериализатора
- Запрошеная фильтрация
Установка Django и Django Ress Framework
PIP3 Установить Django DjangorestFramework
Создание проекта Django и приложение Django
Создайте рабочий каталог для проекта Django
Создайте проект Django
Создайте приложение Django для подписчиков
Подсоедините подписчики и приложение 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”