Сериализатор read_only_fields фреймворка django rest framework не работает

Я использую фреймворк django rest для создания веб-сайта для блога для развлечения. Я использую neon db для базы данных. У меня есть модель users, в которой у меня есть атрибут created_at с типом данных datetime tz. Я также установил значение по умолчанию в NOW(), а в сериализаторе я установил атрибут в read_only_fields. Но когда я выполняю запрос post для создания нового пользователя, он вводит значение null в поле, и created_at заполняется как null в таблице базы данных.

Модель моих пользователей:

class Users(models.Model):
    user_id = models.AutoField(primary_key=True,null=False,blank=True)
    username = models.CharField(max_length=100)
    email = models.CharField(max_length=254)
    password_hash = models.CharField(max_length=30, blank=True, null=True)
    created_at = models.DateTimeField(null=False,blank=True)

    class Meta:
        managed = False
        db_table = 'users'

мой сериализатор:

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = Users
        read_only_fields = ('created_at',)
        fields = '__all__'

мое мнение :

class UsersList(APIView):
    """
        get all users or create a single user
    """
    def get(self, request, format=None):
        users = Users.objects.all()
        serializer = UserSerializer(users,many=True)
        return Response(serializer.data,status=status.HTTP_200_OK)
    
    def post(self, request, format=None):
        serializer = UserSerializer(data = request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data,status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

я пробовал это:

class UserSerializer(serializers.ModelSerializer):
    created_at = serializers.SerializerMethodField()
    class Meta:
        model = Users
        read_only_fields = ('created_at',)
        fields = '__all__'
    
    def get_created_at(self, instance):
        return instance.created_at.strftime("%B %d %Y")

это не сработало, к тому же я не хочу этого делать. я хочу, чтобы DRF не включал поле created_at и позволял таблице neon db устанавливать значение по умолчанию для этого поля.

По умолчанию django не добавляет временную метку в поля даты и времени , поэтому вам следует изменить свою модель на следующую:

from django.utils import timezone

class Users(models.Model):
    user_id = models.AutoField(primary_key=True, null=False, blank=True)
    username = models.CharField(max_length=100)
    email = models.CharField(max_length=254)
    password_hash = models.CharField(max_length=30, blank=True, null=True)
    created_at = models.DateTimeField(default=timezone.now, null=False, blank=True)

    class Meta:
        managed = False
        db_table = 'users'

Вы можете попробовать использовать auto_now параметр:

class Users(models.Model):
    ...
    
    created_at = models.DateTimeField(auto_now=True, null=False, blank=True)

    class Meta:
        managed = False
        db_table = 'users'

Поле даты.auto_now

Автоматически присваивает этому полю значение "сейчас" при каждом сохранении объекта. Полезно для временных меток “последнего изменения”. Обратите внимание, что всегда используется текущая дата; это не просто значение по умолчанию, которое можно переопределить.

Поле автоматически обновляется только при вызове Model.save(). Это поле не обновляется при внесении изменений в другие поля другими способами, такими как QuerySet.update(), хотя вы можете указать пользовательское значение для этого поля при подобном обновлении.

Краткий ответ таков: спецификация поля, доступного только для чтения, работает. Это просто означает, что сериализатор принимает запрос как действительный, если это поле не задано. В этом случае для него устанавливается значение null. Это также означает, что операция создания из Django в вашу базу данных включает created_at=NULL (или любое другое значение по умолчанию, которое вы установили в определении своей модели), что, в свою очередь, приводит к тому, что значение по умолчанию, определенное на уровне базы данных, не запускается (поскольку вы установили значение, даже если оно равно null).

Что касается длинного ответа, то в вашем вопросе есть один момент, который меня раздражает: с одной стороны, в вашем models.py есть определение created_at как ненулевое, а с другой стороны, вы пишете что запись хранится в базе данных с нулевым значением. Поскольку у вас есть managed=False, я полагаю, вы создали таблицу вручную в базе данных, и поскольку в столбце created_at есть значение null, ваше определение таблицы в базе данных допускает это (в отличие от определения модели в Django). Это было бы первое, что вам нужно было бы проверить.

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

Итак, ваша модель должна выглядеть как created_at = models.DateTimeField(default=timezone.now, null=False, blank=True), как предложено здесь.

Вернуться на верх