Использование аргумента источника, SerializerMethodField и to_presentation
Этот пост предполагает базовое знакомство с фреймворком Django REST.
Мы обсудим, как мы можем эффективно использовать сериализаторы во время операций чтения. Мы рассмотрим три мощные функции, которые помогут нам достичь желаемых результатов с меньшим количеством кода.
Мы обсудим:
- Несколько способов использования аргумента сериализатора
source
. - Как и когда использовать
SerializerMethodField
. - Как и когда использовать
to_representation
.
Как использовать аргумент исходного ключевого слова
Сериализатор DRF предоставляет аргумент ключевого слова, называемый source
, который чрезвычайно умен и может помочь избежать множества общих шаблонов.
Давайте напишем сериализатор, который может создавать сериализованные представления User
.
Давайте воспользуемся этим сериализатором для сериализации пользователя:
In [1]: from accounts.serializers import UserSerializer In [2]: from django.contrib.auth.models import User In [3]: user = User.objects.latest('pk') In [4]: serializer = UserSerializer(user) In [5]: serializer.data Out[5]: {'username': 'akshar', 'email': '[email protected]', 'first_name': '', 'last_name': ''}
Предположим, что интерфейс или мобильное приложение, использующее этот API, хочет, чтобы ключ был user_name
вместо username
в сериализованном представлении.
Для этого мы можем добавить CharField
к сериализатору с атрибутом source
.
Убедитесь, что username
заменено на user_name
в Meta.fields
. Перезапустите оболочку и создайте сериализатор.
In [6]: serializer = UserSerializer(user) In [7]: serializer.data Out[7]: {'user_name': 'akshar', 'email': '[email protected]', 'first_name': '', 'last_name': ''}
В последнем примере мы видели, как source
работает с полем User
. source
также может работать с методами на User
.
User
имеет метод get_full_name
. Установим first_name
и last_name
.
In [8]: user.first_name = 'akshar' In [9]: user.last_name = 'raaj' In [10]: user.save() In [11]: user.get_full_name() Out[11]: 'akshar raaj'
Давайте добавим в сериализатор поле full_name
. Установите source
на использование User.get_full_name
.
Давайте перезапустим оболочку и получим сериализованное представление для пользователя.
In [3]: user = User.objects.latest('pk') In [4]: serializer = UserSerializer(user) In [5]: serializer.data Out[5]: {'user_name': 'akshar', 'email': '[email protected]', 'first_name': 'akshar', 'last_name': 'raaj', 'full_name': 'akshar raaj'}
Обратите внимание, как full_name
дает желаемый результат. Под капотом DRF использовал get_full_name
для заполнения full_name
.
source
также может без проблем работать с отношениями, то есть с ForeignKey
, OneToOneField
и ManyToMany
.
Предположим, что существует Profile
модель, которая OneToOne
связана с User
.
Модель профиля выглядит так:
Допустим, мы хотим, чтобы улица и город были отправлены в серийном представлении. Мы могли бы добавить поле улицы и города в сериализатор и установить соответствующий source
.
Давайте перезапустим оболочку и сериализуем пользователя.
In [4]: user = User.objects.latest('pk') In [5]: serializer = UserSerializer(user) In [6]: serializer.data Out[6]: {'email': '[email protected]', 'first_name': 'akshar', 'last_name': 'raaj', 'street': 'Pennsylvania Avenue', 'city': 'Washington'}
source
также легко работает с методами связанных объектов, подобно тому, как он работает с методами объекта.
Мы хотим получить полный адрес пользователей, доступный с помощью user.profile.get_full_address()
.
В таких случаях мы можем установить источник как profile.get_full_address
.
Снова сериализуйте пользователя:
In [3]: user = User.objects.latest('pk') In [4]: serializer = UserSerializer(user) In [5]: serializer.data Out[5]: {'email': '[email protected]', 'first_name': 'akshar', 'last_name': 'raaj', 'full_address': 'Pennsylvania Avenue, Washington'}
Давайте посмотрим, как легко source
работает с ManyToManyField
.
Мы также хотим получить связанные группы пользователей в сериализованном представлении. Давайте сначала добавим пользователю несколько групп.
In [12]: g1 = Group.objects.create(name='BBC') In [13]: g2 = Group.objects.create(name='Sony') In [15]: user.groups.add(*[g1, g2])
Нам нужны id
и name
для каждой группы, связанной с пользователем.
Нам нужно будет написать GroupSerializer
, который может сериализовать экземпляр группы.
Это выглядело бы так:
Наивным способом добавления групп к сериализованным данным было бы определение SerializerMethodField
и добавление user.groups.all()
в метод.
Способ DRFish - добавить поле all_groups
в сериализатор и установить для него значение GroupSerializer
.
Давайте сериализуем пользователя и убедимся, что информация о связанной группе присутствует в сериализованных данных.
In [1]: from accounts.serializers import UserSerializer In [2]: from django.contrib.auth.models import User In [3]: user = User.objects.latest('pk') In [5]: serializer = UserSerializer(user) In [6]: serializer.data Out[6]: {'email': '[email protected]', 'first_name': 'akshar', 'last_name': 'raaj', 'groups': [OrderedDict([('id', 2), ('name', 'BBC')]), OrderedDict([('id', 3), ('name', 'Sony')])]}
DRF достаточно умен, чтобы вызвать user.groups.all()
, хотя мы просто установили source=groups
. DRF делает вывод, что groups
является ManyRelatedManager
, и, таким образом, вызывает .all()
диспетчера, чтобы получить все связанные группы.
Если мы не хотим предоставлять информацию о группе во время POST
вызовов, нам придется добавить аргумент ключевого слова read_only=True
в GroupSerializer
.
Предположим, что существует модель с именем Article
, а Article
имеет значения от ForeignKey
до User
. Мы можем добавить статьи пользователя в сериализованное представление с помощью:
articles = ArticleSerializer(source='article_set', many=True)
Как использовать SerializerMethodField
Бывают случаи, когда вам нужно запустить какой-то собственный код во время сериализации определенного поля.
Вот несколько примеров:
- Преобразуйте
first_name
в titlecase во время сериализации. - Преобразуйте
full_name
в верхний регистр. - Установите
groups
какNone
вместо пустого списка, если с пользователем не связаны никакие группы.
Рассмотрим первый сценарий. Мы хотим изменить first_name
пользователя на регистр заголовка во время сериализации.
До сих пор мы не хотели запускать какой-либо собственный код для first_name
, поэтому наличия first_name
в Meta.fields
было достаточно. Сейчас мы хотим запустить некоторый собственный код, поэтому нам нужно будет явно установить first_name
как SerializerMethodField
.
Когда поле установлено как SerializerMethodField
, DRF вызывает метод с именем get_<field_name>
при вычислении значения для этого поля. Здесь obj
относится к экземпляру user
.
Перезапустите оболочку и сериализуйте пользователя.
In [4]: user = User.objects.latest('pk') In [5]: serializer = UserSerializer(user) In [6]: serializer.data Out[6]: {'email': '[email protected]', 'first_name': 'Akshar', 'last_name': 'raaj'}
Обратите внимание, как first_name
теперь сериализуется в регистр заголовка.
Если мы хотим изменить full_name
на верхний регистр, нам придется изменить сериализатор, чтобы он выглядел так:
Давайте снова сериализуем пользователя:
In [3]: user = User.objects.latest('pk') In [4]: serializer = UserSerializer(user) In [5]: serializer.data Out[5]: {'email': '[email protected]', 'first_name': 'akshar', 'last_name': 'raaj', 'full_name': 'AKSHAR RAAJ'}
Если мы хотим отправить groups
как None
вместо пустого списка, наш сериализатор будет выглядеть так:
Как использовать to_presentation
Сериализаторы предоставляют точку перехвата, называемую to_representation
.
Предположим, мы хотим добавить ключ с именем admin
к сериализованным данным, только когда пользователь является суперпользователем. Для пользователей, не являющихся суперпользователями, ключ admin
не должен присутствовать в сериализованных данных.
Наш сериализатор будет выглядеть так:
instance
относится к сериализованному экземпляру пользователя.
Давайте сериализуем суперпользователя.
In [2]: user = User.objects.latest('pk') In [5]: serializer = UserSerializer(user) In [6]: serializer.data Out[6]: {'email': '[email protected]', 'first_name': 'akshar', 'last_name': 'raaj', 'admin': True}
Отметим пользователя как не суперпользователя и снова сериализуем.
In [7]: user.is_superuser = False In [8]: user.save() In [9]: serializer = UserSerializer(user) In [10]: serializer.data Out[10]: {'email': '[email protected]', 'first_name': 'akshar', 'last_name': 'raaj'}
Обратите внимание, что ключ admin
отсутствует в сериализованных данных для пользователя, не являющегося суперпользователем.
Заключение
В этом посте рассказывается о поведении сериализатора при чтении. Если вы хотите понять, как эффективно использовать сериализаторы во время операций записи, см. Эту статью.
Свяжитесь со мной в Твиттере, я чирикаю информативные и ценные статьи и советы по программированию.