Обработка ошибок django rest framework

Обновлено: 07.07.2024

API-сервисы позволяют приложениям общаться с другими приложения с помощью данных, передаваемых в формате JSON. Достаточно создать и использовать его данные из любого API-клиента или фронтенд-приложения.

Django REST Framework — это набор инструментов для создания REST API с помощью Django. В этом руководстве рассмотрим, как правильно его использовать. Создадим эндпоинты(точки доступа к ресурсам) для пользователей, постов в блоге, комментариев и категорий.

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

Вот чему вы научитесь:

  • Добавлять новые и существующие модели Django в API.
  • Сериализовать модели с помощью встроенных сериализаторов для распространенных API-паттернов.
  • Создавать представления и URL-паттерны.
  • Создавать отношения многие-к-одному и многие-ко-многим.
  • Аутентифицировать пользовательские действия.
  • Использовать созданный API Django REST Framework.

Требования

У вас в системе должен быть установлен Python 3, желательно 3.8. Также понадобится опыт работы с REST API. Вы должны быть знакомы с реляционными базами данными, включая основные и внешние ключи, модели баз данных, миграции, а также отношения многие-к-одному и многие-ко-многим.

Наконец, потребуется опыт работы с Python и Django.

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

Для создания нового API-проекта для начала создайте виртуальную среду Python в своей рабочей директории. Для этого запустите следующую команду в терминале:

В Windows это будет source env\Scripts\activate .

Не забывайте запускать все команды из этого руководства в виртуальной среде. Убедиться в том, что она активирована можно благодаря надписи ( env ) в начале строки приглашения к вводу в терминале.

Чтобы деактивировать среду, введите deactivate .

После этого установите Django и REST Framework в среду:

Создание нового API-проекта

Вам также понадобится пользователь admin для взаимодействия с панелью управления Django и API. Из терминала запустите следующее:

Установите любой пароль (у него должно быть как минимум 8 символов). Если вы введете слишком простой пароль, то можете получить ошибку.

Для настройки, добавьте rest_framework и api в файл конфигурации (blog/blog/settings.py):

Добавление ApiConfig позволит добавлять параметры конфигурации в приложение. Другие настройки для этого руководства не потребуются.

Наконец, запустите локальный сервер с помощью команды python manage.py runserver .

админ-панель сайта

Создание API для пользователей

Сериализатор для User

REST Framework Django использует сериализаторы, чтобы переводить наборы запросов и экземпляры моделей в JSON-данные. Сериализация также определяет, какие данные вернет API в ответ на запрос клиента.

Пользователи Django создаются из модели User , которая определена в django.contrib.auth . Для создания сериализатора для модели добавьте следующее в blog/api/serializers.py (файл нужно создать):

По примеру импортируйте модель User из Django вместе с набором сериализаторов из REST Framework Django.

Теперь создайте класс UserSerializer , который должен наследоваться от класса ModelSerializer .

Определите модель, которая должна ассоциироваться с сериализатором (model = User). Массив fields определяет, какие поля модели должны быть включены. Например, можно добавлять поля first_name и last_name .

Класс ModelSerializer генерирует поля сериализатора, которые основаны на соответствующих свойствах модели. Это значит, что не нужно вручную указывать все атрибуты для поля сериализации, поскольку они вытягиваются напрямую из модели.

Этот сериализатор также создает простые методы create() и update() . При необходимости их можно переписать.

Ознакомиться подробнее с работой ModelSerializer можно на официальном сайте.

Представления для User

Есть несколько способов создавать представления в REST Framework Django. Чтобы получить возможность повторного использования кода и избегать повторений, используйте классовые представления.

REST Framework предоставляет несколько обобщенных представлений, основанных на классе APIView . Они представляют собой самые распространенные паттерны.

Например, ListAPIView используется для эндпоинтов с доступом только для чтения. Он предоставляет метод-обработчик get . ListCreateAPIView используется для эндпоинтов с разрешением чтения-записи, а также обработчики get и post .

Для создания эндпоинта только для чтения, который возвращал бы список пользователей, добавьте следующее в blog/api/views.py:

В первую очередь здесь импортируется generics коллекция представлений, а также модель User и UserSerialized из предыдущего шага. Представление UserList предоставляет доступ только для чтения (через get ) к списку пользователей, а UserDetails — к одному пользователю.

Названия представлений должны быть в следующем формате: List и Details для коллекции объектов и одного объекта соответственно.

Для каждого представления переменная queryset содержит коллекцию экземпляров модели, которую возвращает User.objects.all() . Значением serializer_class должно быть UserSerializer , который и сериализует данные модели User .

Пути к эндпоинтам будут настроены на следующем шаге.

URL-паттерны

С моделью, сериализатором и набором представлений для User финальным шагом будет создание эндпоинтов (которые в Django называются URL-паттернами) для каждого представления.

В первую очередь добавьте следующее в blog/api/urls.py (этот файл тоже нужно создать):

Здесь импортируется функция path и также коллекция представлений приложения api .

Функция path создает элемент, который Django использует для показа страницы приложения. Для этого Django в первую очередь ищет нужный элемент с соответствующим URL (например, users/ ) для запрошенного пользователем. После этого он импортирует и вызывает соответствующее представление (то есть, UserList )

Последовательность <int:pk> указывает на целочисленное значение, которое является основным ключом ( pk ). Django захватывает эту часть URL и отправляет в представление в виде аргумента-ключевого слова.

Прежде чем можно будет взаимодействовать с этими URL-паттернами (и теми, которые будут созданы позже) их нужно добавить в проект. Добавьте следующее в blog/blog/urls.py:

Django REST framework User List

Django REST framework User Detail

В итоге класс модели Django сериализуется с помощью UserSerializaer . Он предоставляет данные представлениям UserList и UserDetail , доступ к которым можно получить с помощью паттернов users/ и users/<int:pk> .

Создание API для Post

После базовой настройки можно приступать к созданию полноценного API для блога с эндпоинтами для постов, комментариев и категорий. Начнем с API для Post.

Модель Post

В blog/api/models.py создайте модель Post , которая наследуется от класса Model из Django и определите ее поля:

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

В этом случае пользователь может иметь много статей, но у поста может быть всего один владелец. Поле owner может быть использовано во фронтенд-приложении для получения пользователя и отображения его имени в качестве автора поста.

Аргумент related_name позволяет задать другое имя доступа к текущей модели ( posts ) вместо стандартного ( post_set ). Список постов будет добавлен в сериализатор User на следующем шаге для завершения отношения многие-к-одному.

Каждый раз при обновлении модели запускайте следующие команды для обновления базы данных:

Поскольку мы работаем с моделями Django, таким как User , посты можно изменить из административной панели Django, зарегистрировав ее в blog/api/admin.py:

Позже их можно будет создавать и через графическое представление API.

Также можно выбрать owner среди существующих пользователей. При создании поста в API пользователя выбирать не нужно. Owner будет задан автоматически на основе данных залогиненного пользователя. Это настроим в следующем шаге.

Сериализатор Post

Чтобы добавить модель Post в API, нужно повторить шаги добавления модели User .

Сначала нужно сериализовать данные модели Post . В blog/api/serializers.py добавьте следующее:

Импортируйте модель Post из приложения api и создайте PostSerializer , который будет наследоваться от класса ModelSerializer . Задайте модель и поля, которые будут использоваться сериализатором.

ReadOnlyField — это класс, возвращающий данные без изменения. В этом случае он используется для возвращения поля username вместо стандартного id .

Дальше добавьте поле posts в UserSerializer . Отношение многие-к-одному между постами и пользователями определено моделью Post в прошлом шаге. Название поля ( posts ) должно быть равным аргументу related_field поля Post.owner . Замените posts на post_set (значение по умолчанию), если вы не задали значение related_field в прошлом шаге.

PrimaryKeyRelatedField представляет список публикаций в этом отношении многие-к-одному ( many=True указывает на то, что постов может быть больше чем один).

Если не задать read_only=True поле posts будет иметь права записи по умолчанию. Это значит, что будет возможность вручную задавать список статей, принадлежащих пользователю при его создании. Вряд ли это желаемое поведение.

Обратите внимание на то, что список posts — это, по сути, список id. Вместо этого можно возвращать список URL с помощью HyperLinkModelSerializer .

Исключения… позволяют чисто организовать обработку ошибок в центральном или высокоуровневом месте в структуре программы.

‒ Doug Hellmann, Python Exception Handling Techniques

Обработка исключений в представлениях фреймворка REST¶

Представления фреймворка REST обрабатывают различные исключения и возвращают соответствующие ответы на ошибки.

Обрабатываемыми исключениями являются:

Подклассы APIException , поднятые внутри фреймворка REST.

Исключение Django PermissionDenied .

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

Например, следующий запрос:

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

Ошибки валидации обрабатываются несколько иначе, и в качестве ключей в ответе будут указаны имена полей. Если ошибка валидации не относится к конкретному полю, то будет использован ключ «non_field_errors» или любое строковое значение, установленное для параметра NON_FIELD_ERRORS_KEY .

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

Пользовательская обработка исключений¶

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

Аргумент context не используется обработчиком по умолчанию, но может быть полезен, если обработчику исключения нужна дополнительная информация, например, обрабатываемое в данный момент представление, доступ к которому можно получить в виде context['view'] .

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

Если параметр 'EXCEPTION_HANDLER' не указан, то по умолчанию используется стандартный обработчик исключений, предоставляемый фреймворком REST:

APIException¶

Подпись: APIException()

Чтобы обеспечить пользовательское исключение, подкласс APIException и установите атрибуты .status_code , .default_detail , и default_code на класс.

Проверка исключений API¶

Существует ряд различных свойств, доступных для проверки состояния исключения API. Вы можете использовать их для создания пользовательской обработки исключений в вашем проекте.

Доступными атрибутами и методами являются:

.detail - Возвращает текстовое описание ошибки.

.get_codes() - Возвращает идентификатор кода ошибки.

.get_full_details() - Возвращает как текстовое описание, так и идентификатор кода.

В большинстве случаев деталь ошибки будет простым элементом:

В случае ошибок валидации деталь ошибки будет представлять собой список или словарь элементов:

ParseError¶

Подпись: ParseError(detail=None, code=None)

Возникает, если запрос содержит неправильно сформированные данные при доступе к request.data .

AuthenticationFailed¶

Подпись: AuthenticationFailed(detail=None, code=None)

Возникает, когда входящий запрос содержит неправильную аутентификацию.

NotAuthenticated¶

Подпись: NotAuthenticated(detail=None, code=None)

Возникает, когда неаутентифицированный запрос не прошел проверку на разрешение.

PermissionDenied¶

Подпись: PermissionDenied(detail=None, code=None)

Возникает, когда аутентифицированный запрос не прошел проверку на разрешение.

NotFound¶

Подпись: NotFound(detail=None, code=None)

MethodNotAllowed¶

Подпись: MethodNotAllowed(method, detail=None, code=None)

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

Неприемлемо¶

Подпись: NotAcceptable(detail=None, code=None)

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

UnsupportedMediaType¶

Подпись: UnsupportedMediaType(media_type, detail=None, code=None)

Возникает, если при обращении к request.data нет парсеров, способных обработать тип содержимого данных запроса.

Дросселированный¶

Подпись: Throttled(wait=None, detail=None, code=None)

Возникает, когда входящий запрос не проходит проверку на дросселирование.

ValidationError¶

Подпись: ValidationError(detail, code=None)

Исключение ValidationError несколько отличается от других классов APIException :

Аргумент detail является обязательным, а не опциональным.

Аргумент detail может представлять собой список или словарь сведений об ошибках, а также может быть вложенной структурой данных. Используя словарь, вы можете указать ошибки на уровне полей при выполнении проверки на уровне объектов в методе validate() сериализатора. Например. raise serializers.ValidationError()

По соглашению вы должны импортировать модуль serializers и использовать полностью квалифицированный стиль ValidationError , чтобы отличить его от встроенной ошибки валидации Django. Например. raise serializers.ValidationError('This field must be an integer value.')

Класс ValidationError следует использовать для сериализатора и валидации полей, а также классами валидаторов. Он также возникает при вызове serializer.is_valid с аргументом ключевого слова raise_exception :

rest_framework.exceptions.server_error ¶

Возвращает ответ с кодом состояния 500 и типом содержимого application/json .

Установить как handler500 :

rest_framework.exceptions.bad_request ¶

Возвращает ответ с кодом состояния 400 и типом содержимого application/json .

DPF-Auto - это мощный и гибкий инструмент для легкого создания мощных Web APIs.

DPF-Auto - это мощный и гибкий инструмент для легкого создания мощных Web APIs. Мы умеем:

  • Python (3.4, 3.5, 3.6)
  • Django (1.9, 1.10, 1.11)
  • DjangoRestFramework (3)

Установка через pip .

Добавить 'drf_auto' в ваши INSTALLED_APPS в настройках.

Давайте рассмотрим примеры использования по порядку.

  • Документация.
  • Написание View.
  • Тестирование.
  • Комбинирование для упрощения кода.
  • Настройки.
  • Обработка ошибок.

Для начала напишите свое первое view и зарегистрируем его в роутере.

GetExampleSerializer, PostInExampleSerializer, PostOutExampleSerializer - любые стандартные rest_framework.serializers.Serializer, rest_framework.serializers.ModelSerializer , используются для примера. Затем нужно подключить view с документацией в роутер.

  • В базовое описание поинта попадает __doc__ view объекта, т.е. класса.
  • В описание каждого метода попадает __doc__ каждого метода. Если мы реализуем метод get у TestView тогда в описании метода get будет эта документация. Тут лучше всего описывать url параметры вида (?P<pk>\d+) .
  • Блоки Принимает / Возвращает формируются благодаря дополнительному полю docs_serializer_classes . Где ключом является метод, а значением является описание метода через сериалайзеры. Есть несколько вариантов использования словаря:
    • Указать метод и один сериалайзер: 'get': GetExampleSerializer . В таком случае в блок Возвращает попадет описание этого сериалайзера. Автодокументация возьмет все филды и опишет их. Тип филда, подпись ( label или verbose_name ) в зависимости от типа филда. В блок Принимает не попадет ничего, и он просто не отобразиться в документации.
    • Указать метод и точное описание метода.
    • Не описывать метод вовсе. В таком случае блоки Принимает / Возвращает будут исключены, и вместо них будет блок ALL . Содержимое его будет описанием сериалайзера из атрибута serializer_class . Если таковой установлен. Т.е. автодока сама попытается найти сериалайзер для отображения документации.

    Для полноценного использования всех возможностей DRF-Auto , писать view нужно через классы, и наследоваться от DRF-Auto views . Есть несколько базовых классов. Все находятся в drf_auto.views.rest :

    • AutoPointFailRequest(rest_framework.generics.GenericAPIView) - Класс для удобной работы с ошибками во время обработки запроса.
    • AutoSearchSerializerView(AutoPointFailRequest) - Класс для поиска сериалайзера для обработки данных. Используется в обработке запроса и ответа.
    • AutoResponseSerializerView(AutoSearchSerializerView) - Класс для автоматического формирования ответа от поинта.
    • AutoRequestSerializerView(AutoPointFailRequest) - Класс для помощи в обработке запроса.
    • RestAPIView(AutoRequestSerializerView, AutoResponseSerializerView) - Базовый класс для всех рест апи, которые вы будете использовать.
    • RestListAPIView(AutoRequestSerializerView, rest_framework.generics.ListAPIView, AutoResponseSerializerView) - Generic view class для списка.
    • RestRetrieveAPIView(AutoRequestSerializerView, rest_framework.generics.RetrieveAPIView, AutoResponseSerializerView) - Generic view class, для конкретного объекта.
    • RestUpdateAPIView(AutoRequestSerializerView, rest_framework.generics.UpdateAPIView, AutoResponseSerializerView) - Generic view class для изменения объекта.
    • RestCreateAPIView(AutoRequestSerializerView, rest_framework.generics.CreateAPIView, AutoResponseSerializerView) - Generic view class для создания объекта.
    • RestDestroyAPIView(AutoRequestSerializerView, rest_framework.generics.DestroyAPIView, AutoResponseSerializerView) - Generic view class для удаления объекта.

    Класс предоставляет метод fail(status, code=None, message=None, data=None, fields=None) который формирует и возвращает объект Response с ошибкой обработки запроса. Про обработку ошибок читайте тут . Так же класс автоматически ловит и обрабатывает исключения в любом месте обработки запроса. Исключения ловит только drf_auto.exceptions.FailPointRequest, rest_framework.serializers.ValidationError . Если он увидел такое исключение, тогда он формирует и возвращает соответствующий объект ответа.

    В стандартном случае ответ ошибки выглядит так:

    Этот класс помогает искать сериалайзер для обработки ответа или запроса. Предоставляет метод get_serializer_class(is_response=False) , с помощью которого и происходит поиск.

    • is_response - Флаг, указывающий это поиск сериалайзера для формирования ответа, или обработки запроса?

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

    • serializers_response_field - Название филда, для поиска словаря сериалайзеров для ответа. Дефолтное: serializer_classes .
    • serializers_request_field - Название филда, для поиска словаря сериалайзеров для обработки запроса. Дефолтное: serializer_classes .
    • serializers_request_key - Ключ используются для поиска внутри сериалайзеров. request_key - обработка водящих данных. Дефолтное: in .
    • serializers_response_key - Ключ используются для поиска внутри сериалайзеров. response_key - обработка ответа сервера. Дефолтное: out .

    Этот класс предоставляет метод get_response(code, serializer=None, data=None, is_serializer=False, serializer_class=None, many=False, *args, **kwargs) который формирует и возвращает объект ответа. Методу можно как отдавать готовые серилизованные данные так и необработанные данные и просить обработать сериалайзером. Он сам подберет сериалайзер в зависимости от настроек view и сформирует ответ.

    • code - Код ответа сервера.
    • serializer - Сериалайзер который уже хранит данные.
    • data - Данные, которые будут возвращены в ответе.
    • is_serializer - Флаг, указывающий нужно ли серилизовать данные или они уже готовы к ответу.
    • serializer_class - Класс сериалайзера которым нужно обработать данные. Если указан, тогда обработка будет этим сериалайзером.
    • many - Флаг указывающий, это один объект нужно обработать, или множество объектов нужно обработать сериалайзером.

    Этот класс позволяет менять данные в запросе, до его обработки. Так же немного переопределяет логику поиска сериалайзера для обработки запроса. get_initial_data(data) метод позволяет менять данные в запросе.

    Для работы с DRF-Auto достаточно унаследоваться от RestAPIView или любого Generic view класса.

    Настройки приложения прописаны в settings.py вашего проекта. Установите атрибут REST_FRAMEWORK_AUTO в settings.py . REST_FRAMEWORK_AUTO - Это словарь. Пример:

    • DOCS.HIDE_DOCS - Скрыть ли документацию. Нужно например что бы скрывать документацию апи для разработчиков на продакшн стенде.
    • DOCS.SERIALIZERS_ATTR_NAME - Название атрибута, который будет уставновлен на всех view, для поиска словаря с описанием сериалайзеров для документации.
    • DOCS.EXCLUDE_FIELDS_ATTR_NAME - Название атрибута для исключения филдов из описания документации.
    • DOCS.SERIALIZER_DOC_ATTR - Название атрибута для описания SerialiazerMethodField у сериалайзеров. Прописывать в serializer.Meta классе. Даже если класс не ModelSerializer .
    • DOCS.PARSER_CLASS - Путь до класса, который парсит сериалайзер и возвращает нужные данные для автодоки.
    • AUTO_REST.EXCEPTIONS.PROCESS_EXCEPT - Слудет ли обрабатывать исключения, возникшие при обработке запроса от клиента?
    • AUTO_REST.EXCEPTIONS.PROCESS_EXCEPT_HANDLER - Путь до своего обработчика исключений, во время обработки запроса. Принимает exc - экземпляр исключения. Возвращает rest_framework.response.Response - ответ от сервера. Работает только если включен PROCESS_EXCEPT .
    • AUTO_REST.EXCEPTIONS.EXCEPTION_LIST - Список исключений, которые дополнительно стоит обработать помимо drf_auto.exceptions.FailPointRequest . Работает только если включен PROCESS_EXCEPT .
    • AUTO_REST.EXCEPTIONS.CODE_EXCEPTION_LIST - Код ответа апи при ответе, во время обработки исключения.
    • AUTO_REST.EXCEPTIONS.STATUS_EXCEPTION_LIST - Код ответа сервера при ответе, во время обработки исключения.
    • AUTO_REST.EXCEPTIONS.EXCEPTION_DICT - Словарь с описанием как обрабатывать исключение. Ключ это само исключение из списка EXCEPTION_LIST . Значение это данные для метода fail у апи. Все аргументы кроме data . data берется из самого исключения. Для поиска данных в самом исключении используется data_attr . Это название атрибута у исключения, в котором хранятся данные по ошибке.
    • SERIALIZER_DOC_CODES - Единая база ошибок.
    • SERIALIZERS_RESPONSE_FIELD - Название филда, для поиска словаря сериалайзеров для ответа.
    • SERIALIZERS_REQUEST_FIELD - Название филда, для поиска словаря сериалайзеров для обработки запроса.
    • SERIALIZERS_REQUEST_KEY - Ключ используются для поиска внутри сериалайзеров. request_key - обработка водящих данных. Дефолтное: in .
    • SERIALIZERS_RESPONSE_KEY - Ключ используются для поиска внутри сериалайзеров. response_key - обработка ответа сервера. Дефолтное: out .

    Сообщить клиенту про ошибку, можно двумя способами:

    • Использовать метод fail(status, code=None, message=None, data=None, fields=None, *args, **kwargs) у view класса. Он формирует и возвращает Response , потому его результат нужно возвращать из view.
    • Выбросить исключение drf_auto.exceptions.FailPointRequest(status, code=None, message=None, data=None, fields=None, *args, **kwargs) . Исключение имеет точно такую же сигнатуру что и fail . По сути, fail является оберткой над FailPointRequest . В случае исключения, его можно бросать в любом месте запроса, тогда он будет пойман базовым классом и обработается как следует.

    В стандартном случае ответ ошибки выглядит так:

    Общий словарь ошибок можно сформировать в настройках. Либо в другом месте и в настройках его пробросить. Пример:

    По всем вопросам поддержки, создавайте issue или пишите разработчикам на почту. Проект в альфа версии, и потихоньку будет дорабатываться и улучшаться.

    Все мы знаем что Django — очень мощный и динамично развивающийся фреймворк для создания веб-приложений. Однако, несмотря на наступление эпохи Веб 2.0, в нём всё ещё нет встроенных механизмов для работы с AJAX, в частности отправки и проверки форм. Возможно django просто не хочет навязывать пользователю какой-то js-фреймворк и хочет оставаться гибкой в этом вопросе, но так или иначе при разработке часто требуются формы, работающие через ajax, без перезагрузок страниц.
    О создании таких форм и работе с ними и пойдёт речь в данной статье.

    Форма

    Для примера возьмём простую форму регистрации пользователя на сайте:

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

    Вывод формы

    • Сделать контейнер с display:none внутри всех страниц, с которых можно вызывать форму (или внутри родительского шаблона), затем с помощью JS создавать диалог из этого контейнера.
    • Подгружать форму через ajax с отдельного URL, и затем также создавать диалог.

    Обработка формы

    • В случае успешной проверки формы этот параметр может быть пустым, так как форма логина скорее всего перенаправляет пользователя на необходимый url после входа и нам неважно что там, либо это может быть строка, которую необходимо отобразить в диалоге подтверждения.
    • В случае когда в форме присутствуют ошибки снова возможно 2 способа отображения:
      1. Первый способ состоит в том, чтобы заново отрендерить форму с ошибками через шаблон и весь html-ответ поместить в json-переменную, которая затем заменяет содержимое всей формы.
      2. Второй способ — создать массив ошибок для всех полей формы и разместить его в json-переменной, затем вывести ошибки для каждого поля в цикле.

    Финальная версия view:

    Финальная версия view:

    Вот и всё. Прилагаю скриншоты получившейся формы в различных состояниях:

    image

    Буду рад услышать комментарии и узнать другие способы работы с ajax-формами.

    Читайте также: