I have a model without field 'test'. I'm assigning this field in runtime:
ability = Ability.objects.first()
ability.test = 'TEST!!'
I also have the serilizer:
class AbilitySerializer(serializers.ModelSerializer):
class Meta:
model = Ability
fields = ('name', 'test',)
And when I use it:
return Response(AbilitySerializer(ability).data)
I'm getting error:
Field Field name `test` is not valid for model `Ability`.
EDIT: I'm still facing this issue when I'm passing array of objects to serializer (with many=True). It's OK when I pass single instance.
Why and how to fix it?
As Ajay Gupta indicated, non-model fields/methods/properties must be explicitly declared:
class AbilitySerializer(serializers.ModelSerializer):
# read_only since test is not a model field
test = serializers.CharField(read_only=True)
class Meta:
model = Ability
fields = ('name', 'test',)
Additionally, if you do not always provide test, consider:
test = serializers.CharField(required=False, read_only=True)
Related
The model:
class Item(models.Model):
company = models.ForeignKey(Company, on_delete=models.CASCADE)
item_num = models.IntegerField()
# other fields...
class Meta:
unique_together = [('company', 'item_num') ]
Serializer:
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = ('company_id', 'item_num', )
The problem is that django rest framework generates a ReadOnlyField() for the company_id field, so this field is not editable when I create a new instance in view code like this:
s = ItemSerializer(data=request.POST)
s.save()
I also lose the default UniqueTogetherValidator that is defined in the model.
Though, if I change the serializer field name from 'company_id' to 'company', I do get the validator, as drf will generate PrimaryKeyRelatedField so it will be editable.
How can I still name my foreign key objects like 'company_id', because I do prefer naming them like this, and still get the default validation and saving behavior? Preferably without adding to much code to the serializer.
company_id is a read-only field because it lacks a proper definition in the Model for the serializer to understand it.
Steps to get it working:
Add an explicit field definition
Add the constraint
Serializer would be:
class ItemSerializer(serializers.ModelSerializer):
company_id = serializers.PrimaryKeyRelatedField(source=company, queryset=Company.objects.all())
class Meta:
model = Item
fields = ('company_id', 'item_num', )
validators = [
UniqueTogetherValidator(
queryset=Item.objects.all(),
fields=('company_id', 'item_num')
)
]
When using the HyperlinkedModelSerializer from Django REST Framework, the field id is not included in the fields by default. This question has an answer that explains that well.
However I have a problem I'd like to solve in a particular way.
I have a model with custom ID and few dozens of other fields:
class Foo(models.Model):
id = models.IntegerField(primary_key=True)
# 20-30 fields
In the serializers.py I'd like to include all fields from the model:
class FooSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Foo
fields = '__all__'
However this doesn't include the field id. Defining id = serializers.ReadOnlyField() doesn't help me either, as id shoud be editable.
Specifying all the fields manually like this:
fields = ('id', # all other fields)
would be a solution I'm trying to circumvent because the model class has a lot of fields and they might change in future.
Is there an elegant possibility to add the field id? Maybe overriding the __init__ method?
Add id attribute in FooSerializer serializer as:
class FooSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.IntegerField(read_only=True)
class Meta:
model = Foo
fields = '__all__'
You can create your custom HyperlinkedModelSerializer and override get_default_field_names to include the id like normal ModelSerializer does.
Example:
class CustomHyperlinkedModelSerializer(HyperlinkedModelSerializer ):
def get_default_field_names(self, declared_fields, model_info):
return (
[model_info.pk.name] +
[self.url_field_name] +
list(declared_fields) +
list(model_info.fields) +
list(model_info.forward_relations)
)
Note : this is just an idea. I have not tested it yet.
HyperlinkedModelSerializer doesn't include the id field by default. You can include id by adding it to the serializer as an attribute like this:
class FooSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.IntegerField()
class Meta:
model = Foo
fields = '__all__'
I removed some fields from my model, but I want the serializer to still accept the fields as input. How do I have fields the serializer accepts but doesn't use?
class EventBaseSerializer(ModelSerializer):
class Meta:
model = models.Event
fields = ("id", "name")
#unused_fields = ("last_name")
From http://www.django-rest-framework.org/api-guide/serializers/
You can add extra fields to a ModelSerializer or override the default
fields by declaring fields on the class, just as you would for a
Serializer class.
class AccountSerializer(serializers.ModelSerializer):
url = serializers.CharField(source='get_absolute_url', read_only=True)
groups = serializers.PrimaryKeyRelatedField(many=True)`
class Meta:
model = Account
If you want a field to be used for input but not output, you'll need to add this field to the fields list and mark it as write_only likely with extra_kwargs
In my case, I want to get data other than the model has and use them in the serializer methods for something else. But the default "create" method of the serializer try to create the object using those foreign fields too and should give an error like:
Got a TypeError when calling MyModel.objects.create(). This may be because you have a writable field on the serializer class that is not a valid argument to MyModel.objects.create(). You may need to make the field read-only, or override the MyModelCreateSerializer.create() method to handle this correctly.
I fixed the issue by popping them from the serializer's data after using them as I want and everything continued just fine.
class MyModelCreateSerializer(serializers.ModelSerializer):
foreign_input_1 = serializers.DateField(write_only=True)
foreign_input_2 = serializers.DateField(write_only=True)
class Meta:
model = MyModel
fields = '__all__'
def validate(self, data):
MySecondModel.objects.create(foreign_input_1=data.pop('foreign_input_1'),
foreign_input_1=data.pop('foreign_input_2'))
return data
Don't forget to use the write_only=True parameter on the foreign fields. The serializer will try to read them from the object in any data return operation, like returning the value of the created object.
models.py:
class Car():
producer = models.ForeignKey(Producer, blank=True, null=True,)
color = models.CharField()
car_model = models.CharField()
doors = models.CharField()
serializers.py:
class CarSerializer(ModelSerializer):
class Meta:
model = Car
fields = Car._meta.get_all_field_names()
So, here I want to use all fields. But I have an error:
Field name producer_id is not valid for model Car.
How to fix that?
Thanks!
According to the Django REST Framework's Documentation on ModelSerializers:
By default, all the model fields on the class will be mapped to a corresponding serializer fields.
This is different than Django's ModelForms, which requires you to specify the special attribute '__all__' to utilize all model fields. Therefore, all that is necessary is to declare the model.
class CarSerializer(ModelSerializer):
class Meta:
model = Car
Update (for versions >= 3.5)
The behaviour described above was deprecated in version 3.3, and forbidden since version 3.5.
It is now mandatory to use the special attribute '__all__' to use all fields in the Django REST Framework, same as Django Forms:
Failing to set either fields or exclude raised a pending deprecation warning in version 3.3 and raised a deprecation warning in 3.4. Its usage is now mandatory.
So now it must be:
class CarSerializer(ModelSerializer):
class Meta:
model = Car
fields = '__all__'
You could use fields = '__all__' to get all your fields or you could specify if you want a limited number of fields to be returned. See documentation.
But this returns the id value for the foreign key field i.e. producer in your case. To get all the fields for producer, you need to create a serializer class for that too. See here.
So your updated serializers.py should be:
class ProducerSerializer(ModelSerializer):
class Meta:
model = Producer
class CarSerializer(ModelSerializer):
producer= ProducerSerializer(read_only=True)
class Meta:
model = Car
fields = ('producer', 'color', 'car_model', 'doors', )
if you want all fields to be included in the serializer you can use fields = '__all__':
class CarSerializer(serializer.ModelSerializer):
class Meta:
fields = '__all__'
model = Car
But this approach is not recommended. We should always explicitly specify all fields. This is because it gives us control over fields displayed. If we don't want a field's data to be displayed, we can avoid that.
class CarSerializer(serializer.ModelSerializer):
class Meta:
fields = ['name', 'color', 'company', 'price', ]
model = Car
I have a form class which takes a model, meta class of the form is as below,
the problem is that I want to make the patient_signature and worker_signature fields unrequired, I tried removing the class wide required_css_class but that did not help, giving each attribute classes required as True/False is also not helping.
Any suggestions...?
class Meta:
model = Locator
exclude = ('patient','worker', 'mode_of_transmission', 'secondary_telephone_number', 'locator', 'grant', 'thumbnail')
creation_date=forms.DateField(initial=datetime.date.today,
widget=SelectDateWidget(),
label="Creation Date")
patient_signature=forms.CharField(widget=ClientSignatureWidget())
worker_signature=forms.CharField(widget=WorkerSignatureWidget())
required_css_class = 'required'
Assuming you are talking about a ModelForm, you cannot override the fields inside the Meta class. It must be outside.
Also, if the field is required in the model but not in the form, then you must provide a default value, like this:
class LocatorForm:
patient_signature = forms.CharField(widget=forms.HiddenInput(), initial=" ")
class Meta:
...
Alternatively, do not mention that field in the fields list and set a value by overriding the submission of the form's POST.
patient_signature=forms.CharField(widget=ClientSignatureWidget(), required=False)