Почему конечная точка PUT django-ninja не использует значение из тела запроса, а вместо этого возвращает значение по умолчанию для обновляемого поля (ModelSchema)?

Цель

Я пытаюсь использовать конечную точку API Django Ninja (с ModelSchema Django Ninja), чтобы обновить поле предпочтений часового пояса (tz_preference*) в модели пользователя моего приложения Django.

*Примечание. Поле tz_preference имеет значение по умолчанию и ограничено списком вариантов.

Проблема

Когда я проверяю конечную точку API с помощью /api/docs, ответ продолжает возвращать значение по умолчанию поля tz_preference («Америка/Денвер»), хотя я даю ему другие допустимые значения в теле запроса («Тихоокеанский регион/Гонолулу», «Америка/Чикаго»). , и т. д.).

Я знаю, что мое поле tz_preference имеет значение по умолчанию «Америка/Денвер», поэтому, скорее всего, именно поэтому тело ответа имеет значение {"tz_preference": "America/Denver"}, но я не уверен, почему оно придерживается значения по умолчанию, а не использует значение, которое я ему указываю. тело запроса.

Код

модели.py

from django.db import models
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.validators import ASCIIUsernameValidator
from django.utils.translation import gettext_lazy as _
from timezone_field import TimeZoneField
from zoneinfo import ZoneInfo

TZ_CHOICES = [
    (ZoneInfo('Pacific/Honolulu'), 'Pacific/Honolulu'),
    (ZoneInfo('America/Anchorage'), 'America/Anchorage'),
    (ZoneInfo('America/Los_Angeles'), 'America/Los_Angeles'),
    (ZoneInfo('US/Arizona'), 'US/Arizona'),
    (ZoneInfo('America/Denver'), 'America/Denver'),
    (ZoneInfo('America/Chicago'), 'America/Chicago'),
    (ZoneInfo('America/New_York'), 'America/New_York'),
]

class CustomUser(AbstractBaseUser, PermissionsMixin):
    username_validator = ASCIIUsernameValidator()

    username = models.CharField(
        _("username"),
        max_length=150,
        unique=True,
        db_index=True,
        validators=[username_validator],
        error_messages = {
            "unique": _("A user with that username already exists."),
        },
    )
    email = models.EmailField(
        _("email address"),
        unique=True,
        db_index=True,
        help_text=_(
            "Required."
        ),
        error_messages = {
            "unique": _("A user with that email address already exists."),
        },
    )
    is_staff = models.BooleanField(_("staff status"), default=False,)
    is_active = models.BooleanField(_("active"), default=True,)

    """ -----------------FIELD I'M TRYING TO CHANGE------------------- """
    tz_preference = TimeZoneField(
        use_pytz=False,
        choices=TZ_CHOICES,
        default = "America/Denver",
        choices_display = "STANDARD",
    )
    """ -------------------------------------------------------------- """

    class CAT_CHOICES(models.TextChoices):
        S = 'STUDENT', _('Student')
        I = 'INSTRUCTOR', _('Instructor')

    category = models.CharField(max_length=10, choices=CAT_CHOICES.choices)

    objects = TerpUserManager()

    EMAIL_FIELD = "email"
    USERNAME_FIELD = "username"
    REQUIRED_FIELDS = ["email", "category"]

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

API.py

# do ye django ninja stuff
from ninja import NinjaAPI

api = NinjaAPI()

# django models stuff (to get my custom user model)
from django.contrib.auth import get_user_model
User = get_user_model()

# ninja schema stuff
from ninja import Schema, ModelSchema

"""------------------------ SCHEMAS ------------------------------"""

class UserTimezoneSchema(ModelSchema):
    class Meta:
        model = User
        fields = ['tz_preference']

class NotFoundSchema(Schema):
    message: str


"""----------------------- ENDPOINTS -----------------------------"""

@api.put("/member/{member_id}", response = {200: UserTimezoneSchema, 404: NotFoundSchema})
def change_tz(request, member_id: int, data: UserTimezoneSchema):
    try:
        member = User.objects.get(pk=member_id)
        member.tz_preference = data.tz_preference
        member.save()
    except User.DoesNotExist as e:
        return 404, {'message': 'User does not exist'}

Информация о запросе и ответе

Запрос (PUT /api/member/{member_id})

  • путь:member_id: 1
  • тело: {"tz_preference": "Pacific/Honolulu"}

КУЛОН

curl -X 'PUT' \
  'http://127.0.0.1:8000/api/member/1' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "tz_preference": "Pacific/Honolulu"
}'

Ответ

  • Код: 200

Тело

{
  "tz_preference": "America/Denver"
}

Заголовки

content-length: 35 
content-type: application/json; charset=utf-8 
cross-origin-opener-policy: same-origin 
date: Mon,01 Jul 2024 18:02:59 GMT 
referrer-policy: same-origin 
server: WSGIServer/0.2 CPython/3.10.14 
x-content-type-options: nosniff 
x-frame-options: DENY 

Ответы (нижняя часть автоматической документации Django Ninja API)

Я думаю, что что-то идет не так:

Изображение схем ответа для этой конечной точки, которое я включил выше, из документации API, которую автоматически генерирует Django Ninja, подсказывает мне, что значение по умолчанию устанавливается автоматически независимо от тела запроса, но у меня нет опыта интерпретации. это автоматически сгенерированная документация, поэтому я не уверен.

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

Другие возможности (хотя они кажутся мне менее вероятными)

  • Возможно, это потому, что я отправляю на сервер простые строки, но варианты выбора для этого поля определяются как объекты ZoneInfo (я сомневаюсь, что это так, потому что простые текстовые данные — единственный тип данных, которые действительно могут быть отправлены через API, верно?)
  • Возможно, мне следует использовать метод PATCH вместо PUT.
  • Возможно, это не работает, потому что мой проект Django использует защиту csrf, но я не включил ее в API, который создаю с помощью Django Ninja.
  • Возможно, это потому, что я пытаюсь изменить только одно поле, но есть и другие обязательные поля, которые я не включаю в запрос.
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
87
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Операторы возврата важны

Оказывается, мне просто нужен был оператор возврата в функции, которую я написал для обработки запроса PUT:

API.py

@api.put("/member/{member_id}", response = {200: UserTimezoneSchema, 404: NotFoundSchema})
def change_tz(request, member_id: int, data: UserTimezoneSchema):
    try:
        member = User.objects.get(pk=member_id)
        member.tz_preference = data.tz_preference
        member.save()
        return member
    except User.DoesNotExist as e:
        return 404, {'message': 'User does not exist'}

Спасибо @vitalik, создателю django-ninja, за указание мне на это.

Другие вопросы по теме