最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - django rest framework serializer read_only_fields does not work - Stack Overflow

programmeradmin0浏览0评论

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.

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.

Share Improve this question edited Feb 6 at 17:00 Nick ODell 25.3k7 gold badges45 silver badges87 bronze badges asked Feb 6 at 15:52 mofu mofumofu mofu 31 bronze badge New contributor mofu mofu is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
Add a comment  | 

3 Answers 3

Reset to default 0

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.

By Default django does not add timestamp to DatetimeFields so you should change your model to the following:

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'

You can try to use auto_now param:

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

    class Meta:
        managed = False
        db_table = 'users'

DateField.auto_now

Automatically set the field to now every time the object is saved. Useful for “last-modified” timestamps. Note that the current date is always used; it’s not just a default value that you can override.

The field is only automatically updated when calling Model.save(). The field isn’t updated when making updates to other fields in other ways such as QuerySet.update(), though you can specify a custom value for the field in an update like that.

发布评论

评论列表(0)

  1. 暂无评论