I am using django rest framework to create a blog website for fun. I am using neon db for the database. I have a users model in which I have a created_at attribute with datetimetz datatype. I have also set the default value to NOW() and in the serializer i have set the attribute to read_only_fields. But when i'm doing a post request to create a new user, it puts in null in the field and the created_at is filled as null in the database table.
My users model:
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'
my serializer:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = Users
read_only_fields = ('created_at',)
fields = '__all__'
my view :
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)
i have tried this:
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")
it didn't work plus i don't want to do this. i want DRF to NOT include the created_at field and let the neon db table set the default value for the field.
The short answer is: the read only field specification works. It just means that the serializer accepts a request as valid if that field is not given. In this case it is set to null value. This also means that the create operation from Django into your database includes created_at=NULL
(or whatever default you set in your model definition), which in turn makes the default that is defined on the database level to not trigger (as you have set a value, even if it is null).
For the long answer, there is one point in your question that irritates me: on the one hand you have in your models.py
the definition of created_at
as non-null, and on the other hand you write that the entry is stored with a null value in the database. As you have managed=False
I suppose you have created the table manually in the database, and as there is a null value in the created_at
column, your definition of the table in the database allows these (contrairy to the model definition in Django). This would be the first thing you need to check.
That said, your model definition in Django should generally reflect your table definition in the database, wich means that you also need to include the default function you defined on database level into your model definition, because otherwise Django will always set the created_at
attribute to s.th. you don't want.
So, your model should look like created_at = models.DateTimeField(default=timezone.now, null=False, blank=True)
as suggested here.