Сериализатор 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)
, как предложено здесь.