Django rest framework: How can I display a read-only field in the browsable api?
When I add result = serializers.CharField(read_only=True)
to my model serializer, the form no longer renders the results field.
I understand the security concerns of a user removing the disabled
attribute on a form input (though I am surprised django doesn't handle this natively), so how can I implement a read-only field in the api.html template from result
?
serializers.py
class SnippetSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
result = serializers.CharField(read_only=True)
class Meta:
model = Snippet
fields = ('title', 'code', 'owner', 'url', 'result')
I am new to the django-rest framework, so any help would be appreciated!
In function-based views, we can pass extra context to serializer with “context” parameter with a dictionary. To access the extra context data inside the serializer we can simply access it with “self. context”. From example, to get “exclude_email_list” we just used code 'exclude_email_list = self.
Serializers in Django REST Framework are responsible for converting objects into data types understandable by javascript and front-end frameworks. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.
SerializerMethodField. This is a read-only field. It gets its value by calling a method on the serializer class it is attached to. It can be used to add any sort of data to the serialized representation of your object. Signature: SerializerMethodField(method_name=None)
The .is_valid() method takes an optional raise_exception flag that will cause it to raise a serializers.ValidationError exception if there are validation errors.
You have 2 options:
either to calculate the result in the model
or to add the field within the serialization
What you choose depends on whether you want to use that calculated result also somewhere else and whether you can touch models.
Follow the example of Django's derived fullname, somewhere around: https://github.com/django/django/blob/master/django/contrib/auth/models.py#L348
Or explained here in the doc: https://docs.djangoproject.com/en/dev/topics/db/models/#model-methods
That will act as a read only field for DRF automatically.
You can see the usage in the code bellow (get_full_name).
You have the answer in the DRF docs: http://www.django-rest-framework.org/api-guide/fields/#serializermethodfield
SerializerMethodField This is a read-only field...It can be used to add any sort of data to the serialized representation of your object.
Example hours_since_joined in serializers.py:
from django.contrib.auth.models import User, Group
from rest_framework import serializers
from django.utils.timezone import now
class UserSerializer(serializers.HyperlinkedModelSerializer):
hours_since_joined = serializers.SerializerMethodField()
class Meta:
model = User
fields = ('url', 'username', 'email', 'groups', 'hours_since_joined', 'first_name', 'last_name', 'get_full_name' )
def get_hours_since_joined(self, obj):
return (now() - obj.date_joined).total_seconds() // 3600
class GroupSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Group
fields = ('url', 'name', 'user_set')
For your case:
class SnippetSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
result = serializers.SerializerMethodField()
class Meta:
model = Snippet
fields = ('title', 'code', 'owner', 'url', 'result')
def get_result(self, obj):
# code here to calculate the result
# or return obj.calc_result() if you have that calculation in the model
return "some result"
You need to list them in the Meta's fields - see example above. That will present that in the browsable output on requests. However it will not show them in the DRF's HTML form. The reason is that the HTML form is used for submitting information only, so the restframework template skips the read only field in rendering.
As you can see the full name and hours since joined is not rendered in the form, but is available for API:
If you want to show read only fields also on the form
you need to override the restframework templates.
copy the form.html and input.html from Python's Lib\site-packages\rest_framework\templates\rest_framework\horizontal\
change the form.html
{% load rest_framework %} {% for field in form %} {% render_field field style=style %} {% endfor %}
change input line in the input.html (adding disabled attribute)
<input name="{{ field.name }}" {% if field.read_only %}disabled{% endif %} {% if style.input_type != "file" %}class="form-control"{% endif %} type="{{ style.input_type }}" {% if style.placeholder %}placeholder="{{ style.placeholder }}"{% endif %} {% if field.value %}value="{{ field.value }}"{% endif %}>
The result:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With