DRF ModelSerializer make all fields Read Only without specifying them explicitely - python-3.x

I was able to make read only model serializer, e.g.:
class FooSerializer(serializers.ModelSerializer):
class Meta:
model = Foo
fields = ['name', 'ratio']
read_only_fields = fields
However, I tend to add/remove fields to/from Foo frequently. It would be much easier not to update my serializer each time Foo is modified. The fields = '__all__' is very handy:
class FooSerializer(serializers.ModelSerializer):
class Meta:
model = Foo
fields = '__all__'
read_only_fields = fields
However, the read_only_fields does not accept __all__ as a valid option and raises this exception:
Exception Type: TypeError at /api/foo/
Exception Value: The `read_only_fields` option must be a list or tuple. Got str.
How could I mark all fields as read only without explicitely adding each field to read_only_fields list?

You can extend get_fields method like this:
def get_fields(self):
fields = super().get_fields()
for field in fields.values():
field.read_only = True
return fields

Related

Inherit methods in Django rest framework

I'm new to learning django rest framework & python. I'm currently trying to inherit a few methods to which I don't know how. An alternative is I copy the same code which is code replication and against code design principles.
An example of what I what to do :
Class A (models.Model)----->
field1 = models()
field2 = models()
field3 = models()
field4 = models()
#classmethod
def method1():
return something
def method2():
return something
Class B (models.Model)----->
field5 = models() - New field, no relation to Class A fields
field6 = models() - New field, no relation to Class A fields
field7 = models() - New field, no relation to Class A fields
field8 = models() - "Here I wish to link field8 to class A so that filed8 is also referenced while viewing Class A fields .The relation I wish to establish is Many to One using Foreign Key"
#classmethod
"Here I wish to execute the 2 methods from Class A that are already defined, so that when When Class B is called , the 2 methods of Class A are also executed along with the new method of Class B itself"
def method3(): ----> New method of class B.
return something
you can make a Common model class which have fields and methods common for other class. For example
class Common(models.Model):
common_field1 = models.TextField()
common_field2 = models.TextField()
def common_method(self):
pass
class Meta:
abstract = True
class NewModelA(Common):
# ... models A fields
# ... models A methods
pass
class NewModelB(Common):
# ... models B fields
# ... models B fields
pass
Note: you will get related name issue when using relational field in common models. In that case defined each field in each models class.

Django Forms Validation Check is not working

I am giving this validation rule in my django forms, so when the field is none then it will rasie validation error but validationcoming ervey time , i meant when is none or not none i am getting the validationerror every time, How i solve this issue.
models.py
class Check(models.Model):
use_for_car = models.BooleanField(default=False)
Forms.py
class CheckForm(forms.ModelForm):
translated_names = TranslationField()
def clean(self):
cleaned_data = super(CheckForm, self).clean()
use_for_car = self.cleaned_data.get("use_for_car")
if use_for_car is None:
raise ValidationError("Use For Car NEED TO BE FILLED ")
return use_for_registrations
class Meta:
fields = "__all__"
model = models.Check
This is already the case, since you did not specify blank=True [Django-doc], this means that the form field is required, so this means that for the form field, required=True [Django-doc], and for a BooleanField form field [Django-doc], this means:
Since all Field subclasses have required=True by default, the validation condition here is important. If you want to include a boolean in your form that can be either True or False (e.g. a checked or unchecked checkbox), you must remember to pass in required=False when creating the BooleanField.
You thus can simply let Django do the work:
class CheckForm(forms.ModelForm):
translated_names = TranslationField()
# no clean override
class Meta:
model = models.Check
fields = '__all__'

How to serialize multiples objects from a Django model and add dynamically computed data in outputed JSON for each object?

I'm porting a Laravel PHP code to Python Django/Django Rest Framework.
My endpoint will output JSON.
I need to output many objects, but I need to add extra computed values for each object.
How can I achieve this ?
For example, my model is :
from django.db import models
from rest_framework.serializers import ModelSerializer
class MyObject(models.Model):
name = models.CharField(max_length=255)
score = models.IntegerField()
class MyObjectSerializer(ModelSerializer):
class Meta:
model = MyObject
fields = ( 'name', 'score' )
I retrieve a queryset with MyObject.objects.all() (or with filter).
For each MyObject in my queryset, I compute an extra value, called 'stats', that I want to output in my JSON output.
For example, if I have 2 objects MyObject(name='foo',score='1') and MyObject(name='bar',score='2'), I will compute a stats value for each object.
And my JSON output should be like :
{
{
'name': 'foo',
'score': 1,
'stats': 1.2
},
{
'name': 'bar',
'score': 2,
'stats': 1.3
},
}
What is the cleanest way , if any to achieve this ?
I can have a loop for each MyObject, serialize each MyObject, one by one with a serializer, and create and update dictionary for this object adding 'stats' key.
I'm afaid about performance.
What if I compute stats value only for some objects, mixing 2 kind of output ?
You can use SerializerMethodField:
class MyObjectSerializer(ModelSerializer):
stat = SerializerMethodField()
class Meta:
model = MyObject
fields = ( 'name', 'score', 'stat' )
def get_stat(self, obj):
# obj is the model instance (it passes only one even if many=True)
# do calculations with obj and return the value
return None
If performance is a concern where stat field uses related/foreign key models, you can either use annotations or select_related/prefetch_related. Using annotation is more efficient but can get difficult to create depending on the requirement.
If it's possible to annotate you can use other serializer fields like:
class MyObjectSerializer(ModelSerializer):
stat = FloatField(read_only=True)
class Meta:
model = MyObject
fields = ( 'name', 'score', 'stat' )
Apart from what #kyell wrote, you can also create a property in models using #property decorator and return your calculated data, this property is always read only.

Adding an extra field to serializers.ModelSerializer

I have the following serializer which does its job:
from rest_framework import serializers
class FavoriteRecordSerializer(serializers.ModelSerializer):
date = serializers.DateTimeField(format='%b %d, %y')
class Meta:
model = FavoriteRecord
fields = ['user', 'date', 'record']
For some reason, I want the serializer to add an extra field, something like is_favorite = True when I serialize a FavoriteRecord object. So, the resulting serialized object could look something like this:
{ user: 1,
date: April 28, 21,
record: 3,
is_favorite: true //this extra field is what I want
}
Is it possible? (I know it can be done from the view, but I am not allowed to change the view- all I can change is this serializer.)
You can use SerializerMethodField it's value is dynamically evaluated by the serializer itself and not the model. Using this method the value can be generated in the context of the current session.
class FavoriteRecordSerializer(serializers.ModelSerializer):
date = serializers.DateTimeField(format='%b %d, %y')
is_favorite = serializers.SerializerMethodField()
class Meta:
model = FavoriteRecord
fields = '__all__'
def get_is_favorite(self, instance):
return True
Any SerializerMethodField will look for a get_<field_name> method and use it as source.

Serializing foreign key in Django rest framework (A->B->C)

How do i serialize foreign key that already refers to a foreign key of another table. Currently I am having three tables A,B and C (A->B->C). A refers to B and B refers to C. I need to get JSON as shown below
A:
{
A_id:1,
A_name:'aaa',
B:
{
B_id:1,
B_name:'bbb'
C:
{
C_id:1,
C_name:'ccc'
}
}
}
I am able to get the JSON for one reference. i.e; A->B
i used RelatedField and overrided to_representation function to achieve this. This is the code i used
class B_foreign(serializers.RelatedField):
def to_representation(self, value):
return value
class ASerializer(serializers.ModelSerializer):
B = B_foreign(source='B_id', read_only=True)
class Meta:
model = A
fields = '__all__'
Now what will i do to refer 'C' from B ? Do another RelatedField class and override to_representation() function?
I tried that too. It didn't work. Can someone help me with solution?
You just use the serializer for the given model:
class CSerializer(serializers.ModelSerializer):
class Meta:
model = C
fields = '__all__'
class BSerializer(serializers.ModelSerializer):
C = CSerializer()
class Meta:
model = B
fields = '__all__'
class ASerializer(serializers.ModelSerializer):
B = BSerializer()
class Meta:
model = A
fields = '__all__'

Resources