{"id":417281,"date":"2024-06-30T01:28:19","date_gmt":"2024-06-30T01:28:19","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=417281"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=417281","title":{"rendered":"<span>\u0420\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u043f\u043e \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 Django REST Framework, Swagger \u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u043a\u043b\u0438\u0435\u043d\u0442\u0430 TypeScript \u0434\u043b\u044f API<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/4d3\/a17\/c11\/4d3a17c118fb870255ae685378f68e49.png\" width=\"780\" height=\"439\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/4d3\/a17\/c11\/4d3a17c118fb870255ae685378f68e49.png\"\/><figcaption><\/figcaption><\/figure>\n<p> <strong>\u0426\u0435\u043b\u0438:<\/strong><\/p>\n<ul>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0442\u044c API \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Django REST Framework;<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e Swagger;<\/p>\n<\/li>\n<li>\n<p>\u0421\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f API \u043a\u043e\u0434 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043d\u0430 TypeScript;<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 ReactJS, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 \u043d\u0430 TypeScript \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 API.<\/p>\n<\/li>\n<\/ul>\n<p><strong>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434:<\/strong><\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/github.com\/appliku\/drfswagger_tutorial\">\u0420\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0441 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u043c Django<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/appliku\/react-swagger-api-demo\">\u0420\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0441 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u043c React<\/a> <\/p>\n<\/li>\n<\/ul>\n<p><strong>\u0422\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f:<\/strong><\/p>\n<p>\u0412 \u044d\u0442\u043e\u043c \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u0435 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u044f\u0442\u0441\u044f \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0435 \u043f\u0430\u043a\u0435\u0442\u044b \u0432 \u0441\u043b\u0443\u0447\u0430\u0435, \u0435\u0441\u043b\u0438 \u0432\u044b \u043d\u0435 \u0431\u0443\u0434\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0448\u0430\u0431\u043b\u043e\u043d \u043f\u0440\u043e\u0435\u043a\u0442\u0430 Djangitos:<\/p>\n<pre><code>Django==3.2.7 djangorestframework==3.12.4 drf-yasg==1.20.0 django-filter==2.4.0 django-cors-headers==3.8.0<\/code><\/pre>\n<p><strong>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/strong><\/p>\n<p>\u0421\u043a\u0430\u0447\u0430\u0439\u0442\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u0448\u0430\u0431\u043b\u043e\u043d \u043f\u0440\u043e\u0435\u043a\u0442\u0430 Djangitos, \u043f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u0443\u0439\u0442\u0435 \u043f\u0430\u043f\u043a\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438 \u0441\u043a\u043e\u043f\u0438\u0440\u0443\u0439\u0442\u0435 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 .<em>env<\/em>-\u0444\u0430\u0439\u043b.\u00a0<\/p>\n<pre><code>curl -sSL https:\/\/appliku.com\/djangitos.zip > djangitos.zip unzip djangitos.zip   mv djangitos-master drfswagger_tutorial cd drfswagger_tutorial cp start.env .env<\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u043f\u0440\u043e\u0435\u043a\u0442:<\/p>\n<pre><code>docker-compose up<\/code><\/pre>\n<p>\u041f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u0435 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438:\u00a0<\/p>\n<pre><code>docker-compose run web python manage.py migrate<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0430\u043a\u043a\u0430\u0443\u043d\u0442 \u0441\u0443\u043f\u0435\u0440\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f:<\/p>\n<pre><code>docker-compose run web python manage.py makesuperuser<\/code><\/pre>\n<p>\u0412 \u0432\u044b\u0432\u043e\u0434\u0435 \u043f\u043e\u0441\u043b\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0431\u0443\u0434\u0443\u0442 \u043b\u043e\u0433\u0438\u043d \u0438 \u043f\u0430\u0440\u043e\u043b\u044c \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0432\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u043b\u0438. \u041d\u0435\u0447\u0442\u043e \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0435:<\/p>\n<pre><code>admin user not found, creating one =================================== A superuser was created with email admin@example.com and password xLV9i9D7p8bm ===================================<\/code><\/pre>\n<p>\u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 <a href=\"http:\/\/0.0.0.0:8060\/admin\/\"><u>http:\/\/0.0.0.0:8060\/admin\/<\/u><\/a> \u0438 \u0432\u043e\u0439\u0434\u0438\u0442\u0435 \u043f\u043e\u0434 \u044d\u0442\u0438\u043c\u0438 \u0443\u0447\u0435\u0442\u043d\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438.<\/p>\n<p><strong>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u043c\u043e\u0434\u0435\u043b\u0435\u0439:<\/strong><\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0431\u0443\u0434\u0443\u0442 \u043d\u0430\u0448\u0438 \u043c\u043e\u0434\u0435\u043b\u0438 \u0438 API.<\/p>\n<pre><code class=\"python\">docker-compose run web python manage.py startapp myapi<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432 <em>PROJECT_APPS <\/em>\u0432 <em>djangito\/settings.py<\/em>:<\/p>\n<pre><code>PROJECT_APPS = [     'usermodel',     'ses_sns',     'myapi',  # new ]<\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043b\u043e\u0436\u043d\u044b\u0435 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u0439 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f API, \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u043e\u0434\u0435\u043b\u0435\u0439, \u043f\u043e\u044d\u0442\u043e\u043c\u0443, \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u043c\u044b \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u043e\u043c \u043a\u043d\u0438\u0433.<\/p>\n<p>\u0423 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0442\u0440\u0438 \u043c\u043e\u0434\u0435\u043b\u0438: <em>Category, Book, Author<\/em>.<\/p>\n<p>\u0412\u043e\u0442 \u043a\u043e\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0432 <em>myapi\/models.py<\/em>:<\/p>\n<pre><code class=\"python\">from django.db import models   class Category(models.Model):     title = models.CharField(max_length=255)      def __str__(self):         return self.title      class Meta:         verbose_name = 'category'         verbose_name_plural = 'categories'         ordering = ('title',)   class Author(models.Model):     name = models.CharField(max_length=255)      def __str__(self):         return self.name      class Meta:         verbose_name = 'author'         verbose_name_plural = 'authors'         ordering = ('name',)   class Book(models.Model):     title = models.CharField(max_length=255)     category = models.ForeignKey(Category, on_delete=models.CASCADE)     authors = models.ManyToManyField(Author)      def __str__(self):         return self.title      class Meta:         verbose_name = 'book'         verbose_name_plural = 'books'         ordering = ('title',)      def authors_names(self) -> list:         return [a.name for a in self.authors.all()] <\/code><\/pre>\n<p>\u0414\u043b\u044f \u044d\u0442\u0438\u0445 \u043c\u043e\u0434\u0435\u043b\u0435\u0439 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043a\u043e\u0434 \u043c\u044b \u043f\u043e\u043b\u043e\u0436\u0438\u043c \u0432 <em>myapi\/admin.py<\/em>:<\/p>\n<pre><code class=\"python\">from django.contrib import admin from . import models   class CategoryAdmin(admin.ModelAdmin):     pass   admin.site.register(models.Category, CategoryAdmin)   class AuthorAdmin(admin.ModelAdmin):     pass   admin.site.register(models.Author, AuthorAdmin)   class BookAdmin(admin.ModelAdmin):     filter_horizontal = ('authors', )     list_display = ('title', 'category',)   admin.site.register(models.Book, BookAdmin) <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u043c\u043e\u0434\u0435\u043b\u0435\u0439:<\/p>\n<pre><code>docker-compose run web python manage.py makemigrations myapi docker-compose run web python manage.py migrate myapi<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u043f\u0430\u043d\u0435\u043b\u0438 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430 \u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043d\u0430 \u043d\u0430\u0448\u0438 \u043c\u043e\u0434\u0435\u043b\u0438: <a href=\"http:\/\/0.0.0.0:8060\/admin\/myapi\/\"><em>http:\/\/0.0.0.0:8060\/admin\/myapi\/<\/em><\/a><\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u043c\u043e\u0434\u0435\u043b\u0438, \u0447\u0442\u043e\u0431\u044b \u043c\u044b \u0441\u043c\u043e\u0433\u043b\u0438 \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442, \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u0437\u0436\u0435 \u0431\u0443\u0434\u0435\u043c \u0438\u0433\u0440\u0430\u0442\u044c\u0441\u044f \u0441 API.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/719\/c9a\/6ad\/719c9a6ada40e6aac24697f6bb0b9643.jpg\" width=\"1200\" height=\"578\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/719\/c9a\/6ad\/719c9a6ada40e6aac24697f6bb0b9643.jpg\" data-blurred=\"true\"\/><figcaption><\/figcaption><\/figure>\n<p><strong>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 API<\/strong><\/p>\n<p>\u0412\u043e \u0432\u0440\u0435\u043c\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f API \u043c\u044b \u043d\u0435 \u0431\u0443\u0434\u0435\u043c \u0434\u0435\u043b\u0430\u0442\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0438 \u043e\u0442\u043b\u043e\u0436\u0438\u043c \u044d\u0442\u0443 \u0447\u0430\u0441\u0442\u044c, \u0447\u0442\u043e\u0431\u044b \u0441\u043e\u0441\u0440\u0435\u0434\u043e\u0442\u043e\u0447\u0438\u0442\u044c \u043d\u0430 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 Swagger \u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 \u043d\u0430 TypeScript.<\/p>\n<p>\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440\u044b \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u043c\u043e\u0434\u0435\u043b\u0435\u0439.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0444\u0430\u0439\u043b <em>myapi\/serializers.py<\/em>:<\/p>\n<pre><code class=\"python\">from . import models from rest_framework import serializers   class CategorySerializer(serializers.ModelSerializer):     class Meta:         model = models.Category         fields = ('id', 'title',)   class AuthorSerializer(serializers.ModelSerializer):     class Meta:         model = models.Author         fields = ('id', 'name',)   class StringListSerializer(serializers.ListSerializer):     child = serializers.CharField()   class BookSerializer(serializers.ModelSerializer):     authors_names = StringListSerializer()      class Meta:         model = models.Book         fields = ('id', 'title', 'category', 'authors', 'authors_names',)<\/code><\/pre>\n<p>\u042f \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0438\u0442\u0430\u044e \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 API \u0438 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u044e\u0442 HTML, \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u0444\u0430\u0439\u043b\u0430\u0445.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b <em>myapi\/api.py<\/em>:<\/p>\n<pre><code class=\"python\">from rest_framework.generics import ListAPIView from . import serializers from . import models   class CategoryListAPIView(ListAPIView):     serializer_class = serializers.CategorySerializer      def get_queryset(self):         return models.Category.objects.all()   class AuthorListAPIView(ListAPIView):     serializer_class = serializers.CategorySerializer      def get_queryset(self):         return models.Author.objects.all()   class BookListAPIView(ListAPIView):     serializer_class = serializers.BookSerializer      def get_queryset(self):         return models.Book.objects.all()<\/code><\/pre>\n<p>\u041f\u043e\u043b\u043e\u0436\u0438\u0442\u0435 \u044d\u0442\u0438 URL-\u0430\u0434\u0440\u0435\u0441\u0430 \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e API \u0432 \u0444\u0430\u0439\u043b <em>myapi\/urls.py<\/em>:<\/p>\n<pre><code class=\"python\">from django.urls import path from . import api  urlpatterns = [     path('category', api.CategoryListAPIView.as_view(), name='api_categories'),     path('authors', api.AuthorListAPIView.as_view(), name='api_authors'),     path('books', api.BookListAPIView.as_view(), name='api_books'), ] <\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 URL-\u0430\u0434\u0440\u0435\u0441\u0430 \u0432 <em>URLConf<\/em> \u0432 <em>djangito\/urls.py<\/em>:<\/p>\n<pre><code class=\"python\">from django.contrib import admin from django.urls import path, include  urlpatterns = [     path('api\/', include('myapi.urls')),  # new     path('sns\/', include('ses_sns.urls')),     path('admin\/', admin.site.urls),     path('ckeditor\/', include('ckeditor_uploader.urls')), ]<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 <em>books<\/em> \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c, \u043a\u0430\u043a \u0432\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442: <a href=\"http:\/\/0.0.0.0:8060\/api\/books\">http:\/\/0.0.0.0:8060\/api\/books<\/a><\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/bab\/110\/e90\/bab110e90a37d8fb97a644310efe8c1f.jpg\" width=\"1200\" height=\"1010\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/bab\/110\/e90\/bab110e90a37d8fb97a644310efe8c1f.jpg\" data-blurred=\"true\"\/><figcaption><\/figcaption><\/figure>\n<p><strong>\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f Swagger<\/strong><\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e API.<\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c URL-\u0430\u0434\u0440\u0435\u0441\u0430 \u0432 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 <em>URLconf<\/em> \u0432 <em>djangito\/urls.py<\/em>:<\/p>\n<pre><code class=\"python\">from django.conf.urls import url from django.contrib import admin from django.urls import path, include from django.views.generic import TemplateView from drf_yasg.views import get_schema_view  # new from drf_yasg import openapi  # new from rest_framework import permissions  schema_view = get_schema_view(  # new     openapi.Info(         title=\"Snippets API\",         default_version='v1',         description=\"Test description\",         terms_of_service=\"https:\/\/www.google.com\/policies\/terms\/\",         contact=openapi.Contact(email=\"contact@snippets.local\"),         license=openapi.License(name=\"BSD License\"),     ),     # url=f'{settings.APP_URL}\/api\/v3\/',     patterns=[path('api\/', include('myapi.urls')), ],     public=True,     permission_classes=(permissions.AllowAny,), ) urlpatterns = [     path(  # new         'swagger-ui\/',         TemplateView.as_view(             template_name='swaggerui\/swaggerui.html',             extra_context={'schema_url': 'openapi-schema'}         ),         name='swagger-ui'),     url(  # new         r'^swagger(?P&lt;format>\\.json|\\.yaml)$',         schema_view.without_ui(cache_timeout=0),         name='schema-json'),     path('api\/', include('myapi.urls')),     path('sns\/', include('ses_sns.urls')),     path('admin\/', admin.site.urls),     path('ckeditor\/', include('ckeditor_uploader.urls')), ]<\/code><\/pre>\n<p>\u041c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u0434\u0432\u0430 \u0438\u043c\u043f\u043e\u0440\u0442\u0430 \u0438 \u0434\u0432\u0430 \u043d\u043e\u0432\u044b\u0445 URL-\u0430\u0434\u0440\u0435\u0441\u0430. \u041e\u0434\u0438\u043d \u0434\u043b\u044f \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0441\u0445\u0435\u043c\u044b \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0430\u0445 JSON \u0438\u043b\u0438 YAML, \u0430 \u0432\u0442\u043e\u0440\u043e\u0439 \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f <code>TemplateView<\/code> \u0432 \u0443\u0434\u043e\u0431\u043d\u043e\u043c \u0438\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435.<\/p>\n<p>\u0414\u043b\u044f <em>TemplateView<\/em> \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0448\u0430\u0431\u043b\u043e\u043d \u0432 <em>\/templates\/swaggerui\/swaggerui.html<\/em>:<\/p>\n<pre><code class=\"python\">&lt;!DOCTYPE html> &lt;html>   &lt;head>     &lt;title>Swagger&lt;\/title>     &lt;meta charset=\"utf-8\"\/>     &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">     &lt;link rel=\"stylesheet\" type=\"text\/css\" href=\"\/\/unpkg.com\/swagger-ui-dist@3\/swagger-ui.css\" \/>   &lt;\/head>   &lt;body>     &lt;div id=\"swagger-ui\">&lt;\/div>     &lt;script src=\"\/\/unpkg.com\/swagger-ui-dist@3\/swagger-ui-bundle.js\">&lt;\/script>     &lt;script>     const ui = SwaggerUIBundle({         url: \"{% url \"schema-json\" \".yaml\" %}\",         dom_id: '#swagger-ui',         presets: [           SwaggerUIBundle.presets.apis,           SwaggerUIBundle.SwaggerUIStandalonePreset         ],         layout: \"BaseLayout\",         requestInterceptor: (request) => {           request.headers['X-CSRFToken'] = \"{{ csrf_token }}\"           return request;         }       })     &lt;\/script>   &lt;\/body> &lt;\/html><\/code><\/pre>\n<p>\u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435: <a href=\"http:\/\/0.0.0.0:8060\/swagger-ui\/\"><u>http:\/\/0.0.0.0:8060\/swagger-ui\/<\/u><\/a><\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432\u044b \u0432\u0438\u0434\u0438\u0442\u0435, \u0447\u0442\u043e \u0432\u0441\u0435 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u044b \u0438 \u043a\u0430\u0436\u0434\u044b\u0439 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u043d\u0430\u043c\u0438 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u0432 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430\u0445 API, \u0443\u043a\u0430\u0437\u0430\u043d \u0432 \u0440\u0430\u0437\u0434\u0435\u043b\u0435 <em>Models<\/em>.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/804\/c80\/3e7\/804c803e702b274d682cfd109b6edb6a.jpg\" width=\"1200\" height=\"1199\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/804\/c80\/3e7\/804c803e702b274d682cfd109b6edb6a.jpg\" data-blurred=\"true\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0432\u0437\u0433\u043b\u044f\u043d\u0435\u043c \u043d\u0430 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 <em>books<\/em>:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/962\/21a\/2a4\/96221a2a4fba6619a79d8726595e4fd4.png\" width=\"1041\" height=\"649\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/962\/21a\/2a4\/96221a2a4fba6619a79d8726595e4fd4.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u043d\u0430\u0448\u0438\u043c \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440\u0430\u043c \u043c\u044b \u0432\u0438\u0434\u0438\u043c \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0439 \u0442\u0438\u043f \u043e\u0442\u0432\u0435\u0442\u0430 \u0431\u0435\u0437 \u0432\u044b\u0437\u043e\u0432\u0430 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u043e\u0432.<\/p>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u043e\u0441\u043e\u0431\u043e\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u043f\u043e\u043b\u0435 <code>authors_names<\/code> \u043d\u0430\u0448\u0435\u0433\u043e <code>BookSerializer<\/code> \u0438 \u043f\u043e\u043b\u0435 <code>authors_names<\/code> \u0432 \u043e\u0442\u0432\u0435\u0442\u0435 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430.<\/p>\n<p>\u041c\u044b \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u043c\u0435\u0442\u043e\u0434 <code>authors_names<\/code> \u0432 \u043c\u043e\u0434\u0435\u043b\u0438 <code>Book<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0441\u0442\u0440\u043e\u043a, \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 <code>StringListSerializer<\/code> \u0438 \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u043f\u043e\u043b\u0435 <code>authors_names<\/code> \u0432 <code>BookSerializer<\/code>. \u041d\u0430\u043c \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c <code>many=True<\/code> \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u043b\u044f \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440\u0430, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u044d\u0442\u043e \u0443\u0436\u0435 \u0437\u0430\u043b\u043e\u0436\u0435\u043d\u043e \u0432 <code>ListSerializer<\/code> \u0438 \u0435\u0433\u043e <code>many=True<\/code> \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e.<\/p>\n<p>\u041c\u044b \u043c\u043e\u0433\u043b\u0438 \u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u043e\u043b\u0435 \u043b\u0435\u043d\u0438\u0432\u043e, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>authors_names = serializers.SerializerMethodField()<\/code>. \u041e\u0434\u043d\u0430\u043a\u043e \u0432 \u0442\u0430\u043a\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 swagger \u0443 \u043d\u0430\u0441 \u0431\u044b\u043b\u0430 \u0431\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u0442\u0440\u043e\u043a\u0430 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u043b\u044f, \u0447\u0442\u043e \u043d\u0435 \u0441\u043e\u0432\u0441\u0435\u043c \u0432\u0435\u0440\u043d\u043e. \u0421\u0442\u0430\u0440\u0430\u0439\u0442\u0435\u0441\u044c \u0438\u0437\u0431\u0435\u0433\u0430\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f <code>SerializerMethodField<\/code>, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0443 \u0432\u0430\u0441 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u043d\u0430\u0434 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439.<\/p>\n<p>\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435, \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c, \u044d\u0442\u043e \u0442\u043e, \u043a\u0430\u043a \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440\u044b \u043e\u043f\u0438\u0441\u0430\u043d\u044b \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u0432 \u0431\u043b\u043e\u043a\u0435 <code>Models<\/code>:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/5b5\/d7b\/81f\/5b5d7b81f49b445adcf3b8ba427aee35.png\" width=\"1039\" height=\"817\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/5b5\/d7b\/81f\/5b5d7b81f49b445adcf3b8ba427aee35.png\"\/><figcaption><\/figcaption><\/figure>\n<p><strong>\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 CORS<\/strong><\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u043d\u0430\u0448 \u043a\u043b\u0438\u0435\u043d\u0442 \u0441\u043c\u043e\u0433 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a API \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c <code>django-cors-headers<\/code>. \u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u043a\u043e\u0434\u0430 \u0432 \u0444\u0430\u0439\u043b <em>djangito\/settings.py<\/em>:<\/p>\n<pre><code class=\"python\">CORS_ALLOWED_ORIGINS = [     \"http:\/\/localhost:3000\" ]  CORS_EXPOSE_HEADERS = ['Content-Type', 'X-CSRFToken'] CORS_ALLOW_CREDENTIALS = True<\/code><\/pre>\n<p><strong>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f React \u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f TypeScript API Client \u0438\u0437 swagger.json<\/strong><\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 ReactJS.\u00a0<\/p>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0443\u0434\u0430\u043b\u0438\u043c \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e <code>create-react-app<\/code>:<\/p>\n<pre><code>npm uninstall -g create-react-app<\/code><\/pre>\n<p>\u0410 \u0442\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0434\u0435\u043c\u043e-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 React:<\/p>\n<pre><code>npx create-react-app swagger-api-demo --template typescript cd swagger-api-demo<\/code><\/pre>\n<p>\u0413\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 OpenAPI Typescript Codegen:<\/p>\n<pre><code>npm install -g openapi-typescript-codegen<\/code><\/pre>\n<p>\u0418 \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c API \u043a\u043b\u0438\u0435\u043d\u0442\u0430:<\/p>\n<pre><code>wget http:\/\/0.0.0.0:8060\/swagger.json -O swagger.json &amp;&amp; openapi  --input .\/swagger.json --output .\/src\/api -c fetch<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0448 \u043f\u0440\u043e\u0435\u043a\u0442 React \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:\u00a0<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/444\/d0e\/80f\/444d0e80f386fc7e048734778dad2681.png\" width=\"328\" height=\"739\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/444\/d0e\/80f\/444d0e80f386fc7e048734778dad2681.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u043c\u043e\u0434\u0435\u043b\u0438 \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 Swagger \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0444\u0430\u0439\u043b TypeScript \u0432 \u043f\u0430\u043f\u043a\u0435 <em>models<\/em>. \u042d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u044b \u043d\u0430\u0448\u0435\u0433\u043e API \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c\u0438.<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u0438 \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u0442\u044c \u043d\u0430\u0448\u0443 \u043a\u043d\u0438\u0433\u0443.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0444\u0430\u0439\u043b <em>src\/BooksList.tsx<\/em> \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435 React:<\/p>\n<pre><code class=\"python\">import {useEffect, useState} from \"react\"; import {Book, BooksService} from \".\/api\";  function BookItem(props: Book) {     return &lt;div>         &lt;b>{props.title}&lt;\/b>         &lt;i>{props.authors_names.join(', ')}&lt;\/i>     &lt;\/div>; }  export default function BooksList() {     const [books, setBooks] = useState&lt;Book[] | undefined>();     const loadBooks = async () => {         setBooks(await BooksService.booksList());     }     useEffect(() => {         loadBooks();     }, []);     return (         &lt;div>             &lt;h1>Books:&lt;\/h1>             {books &amp;&amp; books.map(                 book => {                     return &lt;BookItem {...book}\/>;                 })}         &lt;\/div>     ); }<\/code><\/pre>\n<p>\u0410 \u0437\u0430\u0442\u0435\u043c \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u0435 <em>src\/App.tsx<\/em> \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043a\u043e\u0434\u043e\u043c:\u00a0<\/p>\n<pre><code class=\"python\">import React from 'react'; import '.\/App.css'; import BooksList from \".\/BooksList\";   function App() {     return (         &lt;div className=\"App\">             &lt;BooksList\/>         &lt;\/div>     ); }  export default App;<\/code><\/pre>\n<p>\u0410 \u0442\u0435\u043f\u0435\u0440\u044c \u0432 \u043a\u043e\u0440\u043d\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 React \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u044d\u0442\u0443 \u043a\u043e\u043c\u0430\u043d\u0434\u0443, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c dev-\u0441\u0435\u0440\u0432\u0435\u0440:<\/p>\n<pre><code>npm start<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u043d\u0435\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u043e\u0442\u043a\u0440\u044b\u0442\u044c\u0441\u044f \u043e\u043a\u043d\u043e \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430. \u0415\u0441\u043b\u0438 \u043d\u0435 \u043e\u0442\u043a\u0440\u044b\u043b\u043e\u0441\u044c, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 <a href=\"http:\/\/localhost:3000\/\"><u>http:\/\/localhost:3000\/<\/u><\/a>.<\/p>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u0438\u0442\u0435, \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 React \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u043b\u043e \u0441\u043f\u0438\u0441\u043e\u043a \u043a\u043d\u0438\u0433 \u0438\u0437 \u043d\u0430\u0448\u0435\u0433\u043e API:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/edf\/8be\/dcd\/edf8bedcd0a23a2d255c303402fd6ad4.jpg\" width=\"1200\" height=\"679\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/edf\/8be\/dcd\/edf8bedcd0a23a2d255c303402fd6ad4.jpg\" data-blurred=\"true\"\/><figcaption><\/figcaption><\/figure>\n<hr\/>\n<blockquote>\n<p>\u041c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043b\u0435\u043d \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u043a\u0443\u0440\u0441\u0430 <a href=\"https:\/\/otus.pw\/fVqo\/\">\u00abWeb-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043d\u0430 Python\u00bb<\/a>. \u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e \u0443\u0437\u043d\u0430\u0442\u044c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043e \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f \u0438 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0435, \u043f\u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u043f\u0440\u0435\u043f\u043e\u0434\u0430\u0432\u0430\u0442\u0435\u043b\u0435\u043c \u043a\u0443\u0440\u0441\u0430 \u2014 \u043f\u0440\u0438\u0433\u043b\u0430\u0448\u0430\u0435\u043c \u043d\u0430 \u0434\u0435\u043d\u044c \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u0445 \u0434\u0432\u0435\u0440\u0435\u0439 \u043e\u043d\u043b\u0430\u0439\u043d. \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044f<a href=\"https:\/\/otus.pw\/e0zC\/\"> \u0437\u0434\u0435\u0441\u044c<\/a>.<\/p>\n<\/blockquote>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/583220\/\"> https:\/\/habr.com\/ru\/articles\/583220\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p> <strong>\u0426\u0435\u043b\u0438:<\/strong><\/p>\n<ul>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0442\u044c API \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Django REST Framework;<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e Swagger;<\/p>\n<\/li>\n<li>\n<p>\u0421\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f API \u043a\u043e\u0434 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043d\u0430 TypeScript;<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 ReactJS, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 \u043d\u0430 TypeScript \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 API.<\/p>\n<\/li>\n<\/ul>\n<p><strong>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434:<\/strong><\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/github.com\/appliku\/drfswagger_tutorial\">\u0420\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0441 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u043c Django<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/github.com\/appliku\/react-swagger-api-demo\">\u0420\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0441 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u043c React<\/a> <\/p>\n<\/li>\n<\/ul>\n<p><strong>\u0422\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f:<\/strong><\/p>\n<p>\u0412 \u044d\u0442\u043e\u043c \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u0435 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u044f\u0442\u0441\u044f \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0435 \u043f\u0430\u043a\u0435\u0442\u044b \u0432 \u0441\u043b\u0443\u0447\u0430\u0435, \u0435\u0441\u043b\u0438 \u0432\u044b \u043d\u0435 \u0431\u0443\u0434\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0448\u0430\u0431\u043b\u043e\u043d \u043f\u0440\u043e\u0435\u043a\u0442\u0430 Djangitos:<\/p>\n<pre><code>Django==3.2.7 djangorestframework==3.12.4 drf-yasg==1.20.0 django-filter==2.4.0 django-cors-headers==3.8.0<\/code><\/pre>\n<p><strong>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/strong><\/p>\n<p>\u0421\u043a\u0430\u0447\u0430\u0439\u0442\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u0448\u0430\u0431\u043b\u043e\u043d \u043f\u0440\u043e\u0435\u043a\u0442\u0430 Djangitos, \u043f\u0435\u0440\u0435\u0438\u043c\u0435\u043d\u0443\u0439\u0442\u0435 \u043f\u0430\u043f\u043a\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438 \u0441\u043a\u043e\u043f\u0438\u0440\u0443\u0439\u0442\u0435 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0439 .<em>env<\/em>-\u0444\u0430\u0439\u043b.\u00a0<\/p>\n<pre><code>curl -sSL https:\/\/appliku.com\/djangitos.zip > djangitos.zip unzip djangitos.zip   mv djangitos-master drfswagger_tutorial cd drfswagger_tutorial cp start.env .env<\/code><\/pre>\n<p>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u0435 \u043f\u0440\u043e\u0435\u043a\u0442:<\/p>\n<pre><code>docker-compose up<\/code><\/pre>\n<p>\u041f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u0435 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438:\u00a0<\/p>\n<pre><code>docker-compose run web python manage.py migrate<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0430\u043a\u043a\u0430\u0443\u043d\u0442 \u0441\u0443\u043f\u0435\u0440\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f:<\/p>\n<pre><code>docker-compose run web python manage.py makesuperuser<\/code><\/pre>\n<p>\u0412 \u0432\u044b\u0432\u043e\u0434\u0435 \u043f\u043e\u0441\u043b\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0431\u0443\u0434\u0443\u0442 \u043b\u043e\u0433\u0438\u043d \u0438 \u043f\u0430\u0440\u043e\u043b\u044c \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0432\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u043b\u0438. \u041d\u0435\u0447\u0442\u043e \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0435:<\/p>\n<pre><code>admin user not found, creating one =================================== A superuser was created with email admin@example.com and password xLV9i9D7p8bm ===================================<\/code><\/pre>\n<p>\u041f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435 \u043d\u0430 <a href=\"http:\/\/0.0.0.0:8060\/admin\/\"><u>http:\/\/0.0.0.0:8060\/admin\/<\/u><\/a> \u0438 \u0432\u043e\u0439\u0434\u0438\u0442\u0435 \u043f\u043e\u0434 \u044d\u0442\u0438\u043c\u0438 \u0443\u0447\u0435\u0442\u043d\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438.<\/p>\n<p><strong>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438 \u043c\u043e\u0434\u0435\u043b\u0435\u0439:<\/strong><\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0431\u0443\u0434\u0443\u0442 \u043d\u0430\u0448\u0438 \u043c\u043e\u0434\u0435\u043b\u0438 \u0438 API.<\/p>\n<pre><code class=\"python\">docker-compose run web python manage.py startapp myapi<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432 <em>PROJECT_APPS <\/em>\u0432 <em>djangito\/settings.py<\/em>:<\/p>\n<pre><code>PROJECT_APPS = [     'usermodel',     'ses_sns',     'myapi',  # new ]<\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043b\u043e\u0436\u043d\u044b\u0435 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u0439 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f API, \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u043e\u0434\u0435\u043b\u0435\u0439, \u043f\u043e\u044d\u0442\u043e\u043c\u0443, \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u043c\u044b \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u043e\u043c \u043a\u043d\u0438\u0433.<\/p>\n<p>\u0423 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0442\u0440\u0438 \u043c\u043e\u0434\u0435\u043b\u0438: <em>Category, Book, Author<\/em>.<\/p>\n<p>\u0412\u043e\u0442 \u043a\u043e\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0432 <em>myapi\/models.py<\/em>:<\/p>\n<pre><code class=\"python\">from django.db import models   class Category(models.Model):     title = models.CharField(max_length=255)      def __str__(self):         return self.title      class Meta:         verbose_name = 'category'         verbose_name_plural = 'categories'         ordering = ('title',)   class Author(models.Model):     name = models.CharField(max_length=255)      def __str__(self):         return self.name      class Meta:         verbose_name = 'author'         verbose_name_plural = 'authors'         ordering = ('name',)   class Book(models.Model):     title = models.CharField(max_length=255)     category = models.ForeignKey(Category, on_delete=models.CASCADE)     authors = models.ManyToManyField(Author)      def __str__(self):         return self.title      class Meta:         verbose_name = 'book'         verbose_name_plural = 'books'         ordering = ('title',)      def authors_names(self) -> list:         return [a.name for a in self.authors.all()] <\/code><\/pre>\n<p>\u0414\u043b\u044f \u044d\u0442\u0438\u0445 \u043c\u043e\u0434\u0435\u043b\u0435\u0439 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043a\u043e\u0434 \u043c\u044b \u043f\u043e\u043b\u043e\u0436\u0438\u043c \u0432 <em>myapi\/admin.py<\/em>:<\/p>\n<pre><code class=\"python\">from django.contrib import admin from . import models   class CategoryAdmin(admin.ModelAdmin):     pass   admin.site.register(models.Category, CategoryAdmin)   class AuthorAdmin(admin.ModelAdmin):     pass   admin.site.register(models.Author, AuthorAdmin)   class BookAdmin(admin.ModelAdmin):     filter_horizontal = ('authors', )     list_display = ('title', 'category',)   admin.site.register(models.Book, BookAdmin) <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u043c\u043e\u0434\u0435\u043b\u0435\u0439:<\/p>\n<pre><code>docker-compose run web python manage.py makemigrations myapi docker-compose run web python manage.py migrate myapi<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u043f\u0430\u043d\u0435\u043b\u0438 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430 \u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043d\u0430 \u043d\u0430\u0448\u0438 \u043c\u043e\u0434\u0435\u043b\u0438: <a href=\"http:\/\/0.0.0.0:8060\/admin\/myapi\/\"><em>http:\/\/0.0.0.0:8060\/admin\/myapi\/<\/em><\/a><\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u043c\u043e\u0434\u0435\u043b\u0438, \u0447\u0442\u043e\u0431\u044b \u043c\u044b \u0441\u043c\u043e\u0433\u043b\u0438 \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442, \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u0437\u0436\u0435 \u0431\u0443\u0434\u0435\u043c \u0438\u0433\u0440\u0430\u0442\u044c\u0441\u044f \u0441 API.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p><strong>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 API<\/strong><\/p>\n<p>\u0412\u043e \u0432\u0440\u0435\u043c\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f API \u043c\u044b \u043d\u0435 \u0431\u0443\u0434\u0435\u043c \u0434\u0435\u043b\u0430\u0442\u044c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u0438 \u043e\u0442\u043b\u043e\u0436\u0438\u043c \u044d\u0442\u0443 \u0447\u0430\u0441\u0442\u044c, \u0447\u0442\u043e\u0431\u044b \u0441\u043e\u0441\u0440\u0435\u0434\u043e\u0442\u043e\u0447\u0438\u0442\u044c \u043d\u0430 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 Swagger \u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 \u043d\u0430 TypeScript.<\/p>\n<p>\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440\u044b \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u043c\u043e\u0434\u0435\u043b\u0435\u0439.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0444\u0430\u0439\u043b <em>myapi\/serializers.py<\/em>:<\/p>\n<pre><code class=\"python\">from . import models from rest_framework import serializers   class CategorySerializer(serializers.ModelSerializer):     class Meta:         model = models.Category         fields = ('id', 'title',)   class AuthorSerializer(serializers.ModelSerializer):     class Meta:         model = models.Author         fields = ('id', 'name',)   class StringListSerializer(serializers.ListSerializer):     child = serializers.CharField()   class BookSerializer(serializers.ModelSerializer):     authors_names = StringListSerializer()      class Meta:         model = models.Book         fields = ('id', 'title', 'category', 'authors', 'authors_names',)<\/code><\/pre>\n<p>\u042f \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0438\u0442\u0430\u044e \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 API \u0438 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u044e\u0442 HTML, \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u0444\u0430\u0439\u043b\u0430\u0445.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b <em>myapi\/api.py<\/em>:<\/p>\n<pre><code class=\"python\">from rest_framework.generics import ListAPIView from . import serializers from . import models   class CategoryListAPIView(ListAPIView):     serializer_class = serializers.CategorySerializer      def get_queryset(self):         return models.Category.objects.all()   class AuthorListAPIView(ListAPIView):     serializer_class = serializers.CategorySerializer      def get_queryset(self):         return models.Author.objects.all()   class BookListAPIView(ListAPIView):     serializer_class = serializers.BookSerializer      def get_queryset(self):         return models.Book.objects.all()<\/code><\/pre>\n<p>\u041f\u043e\u043b\u043e\u0436\u0438\u0442\u0435 \u044d\u0442\u0438 URL-\u0430\u0434\u0440\u0435\u0441\u0430 \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e API \u0432 \u0444\u0430\u0439\u043b <em>myapi\/urls.py<\/em>:<\/p>\n<pre><code class=\"python\">from django.urls import path from . import api  urlpatterns = [     path('category', api.CategoryListAPIView.as_view(), name='api_categories'),     path('authors', api.AuthorListAPIView.as_view(), name='api_authors'),     path('books', api.BookListAPIView.as_view(), name='api_books'), ] <\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 URL-\u0430\u0434\u0440\u0435\u0441\u0430 \u0432 <em>URLConf<\/em> \u0432 <em>djangito\/urls.py<\/em>:<\/p>\n<pre><code class=\"python\">from django.contrib import admin from django.urls import path, include  urlpatterns = [     path('api\/', include('myapi.urls')),  # new     path('sns\/', include('ses_sns.urls')),     path('admin\/', admin.site.urls),     path('ckeditor\/', include('ckeditor_uploader.urls')), ]<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 <em>books<\/em> \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c, \u043a\u0430\u043a \u0432\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442: <a href=\"http:\/\/0.0.0.0:8060\/api\/books\">http:\/\/0.0.0.0:8060\/api\/books<\/a><\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p><strong>\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f Swagger<\/strong><\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e API.<\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c URL-\u0430\u0434\u0440\u0435\u0441\u0430 \u0432 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 <em>URLconf<\/em> \u0432 <em>djangito\/urls.py<\/em>:<\/p>\n<pre><code class=\"python\">from django.conf.urls import url from django.contrib import admin from django.urls import path, include from django.views.generic import TemplateView from drf_yasg.views import get_schema_view  # new from drf_yasg import openapi  # new from rest_framework import permissions  schema_view = get_schema_view(  # new     openapi.Info(         title=\"Snippets API\",         default_version='v1',         description=\"Test description\",         terms_of_service=\"https:\/\/www.google.com\/policies\/terms\/\",         contact=openapi.Contact(email=\"contact@snippets.local\"),         license=openapi.License(name=\"BSD License\"),     ),     # url=f'{settings.APP_URL}\/api\/v3\/',     patterns=[path('api\/', include('myapi.urls')), ],     public=True,     permission_classes=(permissions.AllowAny,), ) urlpatterns = [     path(  # new         'swagger-ui\/',         TemplateView.as_view(             template_name='swaggerui\/swaggerui.html',             extra_context={'schema_url': 'openapi-schema'}         ),         name='swagger-ui'),     url(  # new         r'^swagger(?P&lt;format>\\.json|\\.yaml)$',         schema_view.without_ui(cache_timeout=0),         name='schema-json'),     path('api\/', include('myapi.urls')),     path('sns\/', include('ses_sns.urls')),     path('admin\/', admin.site.urls),     path('ckeditor\/', include('ckeditor_uploader.urls')), ]<\/code><\/pre>\n<p>\u041c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u0434\u0432\u0430 \u0438\u043c\u043f\u043e\u0440\u0442\u0430 \u0438 \u0434\u0432\u0430 \u043d\u043e\u0432\u044b\u0445 URL-\u0430\u0434\u0440\u0435\u0441\u0430. \u041e\u0434\u0438\u043d \u0434\u043b\u044f \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0441\u0445\u0435\u043c\u044b \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0430\u0445 JSON \u0438\u043b\u0438 YAML, \u0430 \u0432\u0442\u043e\u0440\u043e\u0439 \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f <code>TemplateView<\/code> \u0432 \u0443\u0434\u043e\u0431\u043d\u043e\u043c \u0438\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435.<\/p>\n<p>\u0414\u043b\u044f <em>TemplateView<\/em> \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0448\u0430\u0431\u043b\u043e\u043d \u0432 <em>\/templates\/swaggerui\/swaggerui.html<\/em>:<\/p>\n<pre><code class=\"python\">&lt;!DOCTYPE html> &lt;html>   &lt;head>     &lt;title>Swagger&lt;\/title>     &lt;meta charset=\"utf-8\"\/>     &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">     &lt;link rel=\"stylesheet\" type=\"text\/css\" href=\"\/\/unpkg.com\/swagger-ui-dist@3\/swagger-ui.css\" \/>   &lt;\/head>   &lt;body>     &lt;div id=\"swagger-ui\">&lt;\/div>     &lt;script src=\"\/\/unpkg.com\/swagger-ui-dist@3\/swagger-ui-bundle.js\">&lt;\/script>     &lt;script>     const ui = SwaggerUIBundle({         url: \"{% url \"schema-json\" \".yaml\" %}\",         dom_id: '#swagger-ui',         presets: [           SwaggerUIBundle.presets.apis,           SwaggerUIBundle.SwaggerUIStandalonePreset         ],         layout: \"BaseLayout\",         requestInterceptor: (request) => {           request.headers['X-CSRFToken'] = \"{{ csrf_token }}\"           return request;         }       })     &lt;\/script>   &lt;\/body> &lt;\/html><\/code><\/pre>\n<p>\u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435: <a href=\"http:\/\/0.0.0.0:8060\/swagger-ui\/\"><u>http:\/\/0.0.0.0:8060\/swagger-ui\/<\/u><\/a><\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432\u044b \u0432\u0438\u0434\u0438\u0442\u0435, \u0447\u0442\u043e \u0432\u0441\u0435 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u044b \u0438 \u043a\u0430\u0436\u0434\u044b\u0439 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u043d\u0430\u043c\u0438 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u0432 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430\u0445 API, \u0443\u043a\u0430\u0437\u0430\u043d \u0432 \u0440\u0430\u0437\u0434\u0435\u043b\u0435 <em>Models<\/em>.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0432\u0437\u0433\u043b\u044f\u043d\u0435\u043c \u043d\u0430 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 <em>books<\/em>:<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u043d\u0430\u0448\u0438\u043c \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440\u0430\u043c \u043c\u044b \u0432\u0438\u0434\u0438\u043c \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0439 \u0442\u0438\u043f \u043e\u0442\u0432\u0435\u0442\u0430 \u0431\u0435\u0437 \u0432\u044b\u0437\u043e\u0432\u0430 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u043e\u0432.<\/p>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u043e\u0441\u043e\u0431\u043e\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u043f\u043e\u043b\u0435 <code>authors_names<\/code> \u043d\u0430\u0448\u0435\u0433\u043e <code>BookSerializer<\/code> \u0438 \u043f\u043e\u043b\u0435 <code>authors_names<\/code> \u0432 \u043e\u0442\u0432\u0435\u0442\u0435 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430.<\/p>\n<p>\u041c\u044b \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u043c\u0435\u0442\u043e\u0434 <code>authors_names<\/code> \u0432 \u043c\u043e\u0434\u0435\u043b\u0438 <code>Book<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0441\u0442\u0440\u043e\u043a, \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 <code>StringListSerializer<\/code> \u0438 \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u043f\u043e\u043b\u0435 <code>authors_names<\/code> \u0432 <code>BookSerializer<\/code>. \u041d\u0430\u043c \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c <code>many=True<\/code> \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u043b\u044f \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440\u0430, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u044d\u0442\u043e \u0443\u0436\u0435 \u0437\u0430\u043b\u043e\u0436\u0435\u043d\u043e \u0432 <code>ListSerializer<\/code> \u0438 \u0435\u0433\u043e <code>many=True<\/code> \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e.<\/p>\n<p>\u041c\u044b \u043c\u043e\u0433\u043b\u0438 \u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u043e\u043b\u0435 \u043b\u0435\u043d\u0438\u0432\u043e, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>authors_names = serializers.SerializerMethodField()<\/code>. \u041e\u0434\u043d\u0430\u043a\u043e \u0432 \u0442\u0430\u043a\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 swagger \u0443 \u043d\u0430\u0441 \u0431\u044b\u043b\u0430 \u0431\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u0442\u0440\u043e\u043a\u0430 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u043b\u044f, \u0447\u0442\u043e \u043d\u0435 \u0441\u043e\u0432\u0441\u0435\u043c \u0432\u0435\u0440\u043d\u043e. \u0421\u0442\u0430\u0440\u0430\u0439\u0442\u0435\u0441\u044c \u0438\u0437\u0431\u0435\u0433\u0430\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f <code>SerializerMethodField<\/code>, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0443 \u0432\u0430\u0441 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u043d\u0430\u0434 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439.<\/p>\n<p>\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435, \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-417281","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/417281","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=417281"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/417281\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=417281"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=417281"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=417281"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}