I have a model in which I use a JSON field and when I serializer it alone, it works perfectly. However, when I include it as a nested serializer it doesn't convert from JSON and I am left with a string.
I have tried using transform_fieldname but it doesn't get hit if the serializer is nested. It gets hit if the serializer is not nested.
I am using the django-jsonfield module in my model.
class TopLevelSerializer(serializers.ModelSerializer):
nest = NestedSerializer(many=True)
class Meta:
model = ModelTop
fields = ('id', 'nest')
class NestedSerializer(serializers.ModelSerializer):
def transform_options(self, obj, value):
print 'This doesn't get hit if nested'
return json.loads(obj)
class Meta:
model = ModelTwo
fields = ('id', 'options')
Have you tried specifing the depth of the serialization of your relation?
See http://www.django-rest-framework.org/api-guide/serializers#specifying-nested-serialization
Related
I'm using Django Rest Framework with django-simple-history and currently I would like to return the history modifications in my Board rest API, currently it is doing well but I would like to hide some fields. This is the currently output:
But, I don't need id, history_id, etc.
my implementation is the same of alexander answer in this post.
this is my currently serializers, where I put history on my Board model
class HistoricalRecordField(serializers.ListField):
child = serializers.DictField()
def to_representation(self, data):
representation = super().to_representation(data.values())
# i've tried to do it by deleting, but does't work well.
del representation[0]['history_id']
return representation
class BoardSerializer(serializers.ModelSerializer):
history = HistoricalRecordField(read_only=True)
class Meta:
model = Board
fields = '__all__'
But it does not seem the best way to do it.
If you have some hint about how to do it the correct way I would like to know.
Thanks in advance!
You can try this for history_id at least:
def to_representation(self, data):
representation = super().to_representation(data.values())
for hist in representation['history']:
hist.pop('history_id')
return representation
I don't know django-simple-history, so they may be better solutions than mine. However, you can do this with a more DRF-friendly approach by simply using a ModelSerializer instead of a ListSerializer:
class HistoricalRecordSerializer(serializers.ModelSerializer):
class Meta:
model = HistoricalRecords
fields = ('name', 'description', 'share_with_company', [...]) # Only keep the fields you want to display here
class BoardSerializer(serializers.ModelSerializer):
history = HistoricalRecordSerializer(read_only=True, many=True)
class Meta:
model = Board
fields = ('name', 'description', 'history', [...]) # Only keep the fields you want to display here
If you want to only retrieve the latest update, you could use a SerializerMethodField (documentation here). Remember to declare it in Meta.fields instead of 'history' (or rename your SerializerMethodField "history" if you want to keep this name):
class HistoricalRecordSerializer(serializers.ModelSerializer):
class Meta:
model = HistoricalRecords
fields = ('name', 'description', 'share_with_company', [...]) # Only keep the fields you want to display here
class BoardSerializer(serializers.ModelSerializer):
latest_history = serializers.SerializerMethodField()
def get_latest_history(self, instance):
latest_history = instance.history.most_recent() # Retrieve the record you want here
return HistoricalRecordSerializer(latest_history).data
class Meta:
model = Board
fields = ('name', 'description', 'latest_history', [...]) # Only keep the fields you want to display here
Keep in mind that I don't know much about this lib, so this should work but I cannot guarantee it's the best way to to it.
Many time we access data via serializer directory according to relationship defined in models in Django(1.11.10). How can i set a filter like fetch-only is_active=1.
class DaasJobsSerializer(serializers.ModelSerializer):
class Meta:
model = DaasJobs
fields = '__all__'
class DaasScheduleSerializer(serializers.ModelSerializer):
jobs = DaasJobsSerializer(read_only=True,many=True)
class Meta:
model = DaasSchedule
fields = '__all__'
Here i just want to set a filter to fetch only those Jobs which db field is_active=1 in this line like that DaasJobsSerializer(read_only=True,many=True, filter={"is_active":1}) how to do something like this ??
Currently it is giving me all the data without checking is_active,
and i dont want to create serializerMethodField for that.. because all methods written earlier.. i am just setting a is_active field later in the tables in db.
If you want to do it via serializers you can try overriding the ListSerializer and passing it as a custom list_serializer_class.
class IsActiveListSerializer(serializers.ListSerializer):
def to_representation(self, data):
data = data.filter(is_active=1)
return super().to_representation(data)
In your serializer:
class DaasJobsSerializer(serializers.ModelSerializer):
class Meta:
model = DaasJobs
fields = '__all__'
list_serializer_class = IsActiveListSerializer # import it here
Of course this is a specific use-case, you could make a more generalized version of the ListSerializer to:
class FilteredListSerializer(serializers.ListSerializer):
filter_kwargs = {}
def to_representation(self, data):
if not self.filter_kwargs or not isinstance(self.filter_kwargs, dict):
raise TypeError(_('Invalid Attribute Type: `filter_kwargs` must be a of type `dict`.'))
data = data.filter(**self.filter_kwargs)
return super().to_representation(data)
And then you could sub-class that to make other specific ListSerializers such as:
class IsActiveListSerializer(FilteredListSerializer):
filter_kwargs = {'is_active': 1}
and many others...
I'm using Django 2.1, DRF 3.7.7.
I've some models and their relative (model) serializers: these models are nested, and so are the serializers.
Let me give an example:
# models.py
class City(models.Model):
name = models.CharField(max_length=50)
class Person(models.Model):
surname = models.CharField(max_length=30)
birth_place = models.ForeignKey(City)
# serializers.py
class CitySerializer(serializers.ModelSerializer):
class Meta:
model = models.CitySerializer
fields = "__all__"
class PersonSerializer(serializers.ModelSerializer):
birth_place = CitySerializer()
class Meta:
model = models.Person
fields = "__all__"
If I submit an AJAX request with a json like:
{'surname': 'smith', 'birth_place': 42}
I get back a Bad Request response, containing: Invalid data. Expected a dictionary, but got int.
If I submit a nested json like:
{'surname': 'smith', 'birth_place': {'id': 42, 'name': 'Toronto'}}
the relation is not converted, the id field is ignored and the rest is parsed to:
OrderedDict([('birth_place', OrderedDict([('name', 'Toronto')]))])
The following is the post method I'm using on a class-based view:
def post(self, request):
print("Original data:", request.data)
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
self.data = serializer.validated_data
print("Parsed data:", self.data)
...
I only need to get data from the endpoints connected to the serializers, I don't need to write/save anything through the REST interface, since the POST processing of the form is done by Django.
TL;DR: How should I correctly submit a JSON request to a nested serializer, without having to write handmade conversions? Did I commit errors in setting up the serializers?
Edit: I've discovered that by adding id = serializers.IntegerField() to the serializer parent class (e.g. City), the serializer parser now processes the id. At least now I'm able to perform actions in the backend with django.
Generic writing for nested serializers is not available by default. And there is a reason for that:
Consider, you are creating a person with a birthplace, using a POST request. It is not clear if the submitted city is a new one or an existing one. Should it return an error if there isn't such a city? Or should it be created?
This is why, if you want to handle this kind of relationship in your serializer, you need to write your own create() and update() methods of your serializer.
Here is the relevant part of the DRF docs: http://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers
It's definitely not clearly put into the docs of django-rest. If you follow the process of serializers processing the data for creation then it becomes clear that django manages m2m by saving the parent instance first and then adding the m2m values, but somehow the m2m fields don't go through the validation if you mark them as read_only.
The solution to this is to overr run_validation method of the serializer. The serializer should look like this:
class ExampleSerializer(serializers.ModelSerializer):
queryset = SomeModel.objects.all()
tags = TagSerializer(many=True, read_only=True)
class Meta:
model = SomeModel
fields = ['pk', 'name', 'tags']
def run_validation(self, data):
validated_data = super(StudyResourceSerializer, self).run_validation(data)
validated_data['tags'] = data['tags']
return validated_data
The request body should look like this:
{
"tags": [51, 54],
"name": "inheritance is a mess"
}
I am using the Django REST framework to create an API. I would like to add data from more than one model to the serialised output.
At the moment my serialiser looks like this:
class ItemSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Item
fields = ('url', 'owner', 'item_type')
I would like to add an
item_cost
value from my Costs model to the serialised output (a different cost for each item in the Item model). I would also like to add a unix timestamp value to the serialised output (one value to be placed at the end of all of the other serialised output).
My serialiser is used in a view as follows:
class ItemViewSet(viewsets.ModelViewSet):
queryset = Item.objects.all().order_by('-date_added')
serializer_class = ItemSerializer
I can't work out how to add the other data items to the serialised output.
You can use a SerializerMethodField from rest_framework.serializers and create a method that returns the value you are looking for, eg:
class ItemSerializer(serializers.HyperlinkedModelSerializer):
cost = serializers.SerializerMethodField()
def get_cost(self, obj):
value = ... # calculate your value
return value
class Meta:
model = Item
fields = ('url', 'owner', 'item_type', 'cost')
Reference in the docs: http://www.django-rest-framework.org/api-guide/fields/#serializermethodfield
I want to serialize a model, but want to include an additional field that requires doing some database lookups on the model instance to be serialized:
class FooSerializer(serializers.ModelSerializer):
my_field = ... # result of some database queries on the input Foo object
class Meta:
model = Foo
fields = ('id', 'name', 'myfield')
What is the right way to do this? I see that you can pass in extra "context" to the serializer, is the right answer to pass in the additional field in a context dictionary?
With that approach, the logic of getting the field I need would not be self-contained with the serializer definition, which is ideal since every serialized instance will need my_field. Elsewhere in the DRF serializers documentation it says "extra fields can correspond to any property or callable on the model". Are "extra fields" what I'm talking about?
Should I define a function in Foo's model definition that returns my_field value, and in the serializer I hook up my_field to that callable? What does that look like?
Happy to clarify the question if necessary.
I think SerializerMethodField is what you're looking for:
class FooSerializer(serializers.ModelSerializer):
my_field = serializers.SerializerMethodField('is_named_bar')
def is_named_bar(self, foo):
return foo.name == "bar"
class Meta:
model = Foo
fields = ('id', 'name', 'my_field')
http://www.django-rest-framework.org/api-guide/fields/#serializermethodfield
You can change your model method to property and use it in serializer with this approach.
class Foo(models.Model):
. . .
#property
def my_field(self):
return stuff
. . .
class FooSerializer(ModelSerializer):
my_field = serializers.ReadOnlyField(source='my_field')
class Meta:
model = Foo
fields = ('my_field',)
Edit: With recent versions of rest framework (I tried 3.3.3), you don't need to change to property. Model method will just work fine.
With the last version of Django Rest Framework, you need to create a method in your model with the name of the field you want to add. No need for #property and source='field' raise an error.
class Foo(models.Model):
. . .
def foo(self):
return 'stuff'
. . .
class FooSerializer(ModelSerializer):
foo = serializers.ReadOnlyField()
class Meta:
model = Foo
fields = ('foo',)
if you want read and write on your extra field, you can use a new custom serializer, that extends serializers.Serializer, and use it like this
class ExtraFieldSerializer(serializers.Serializer):
def to_representation(self, instance):
# this would have the same as body as in a SerializerMethodField
return 'my logic here'
def to_internal_value(self, data):
# This must return a dictionary that will be used to
# update the caller's validation data, i.e. if the result
# produced should just be set back into the field that this
# serializer is set to, return the following:
return {
self.field_name: 'Any python object made with data: %s' % data
}
class MyModelSerializer(serializers.ModelSerializer):
my_extra_field = ExtraFieldSerializer(source='*')
class Meta:
model = MyModel
fields = ['id', 'my_extra_field']
i use this in related nested fields with some custom logic
My response to a similar question (here) might be useful.
If you have a Model Method defined in the following way:
class MyModel(models.Model):
...
def model_method(self):
return "some_calculated_result"
You can add the result of calling said method to your serializer like so:
class MyModelSerializer(serializers.ModelSerializer):
model_method_field = serializers.CharField(source='model_method')
p.s. Since the custom field isn't really a field in your model, you'll usually want to make it read-only, like so:
class Meta:
model = MyModel
read_only_fields = (
'model_method_field',
)
If you want to add field dynamically for each object u can use to_represention.
class FooSerializer(serializers.ModelSerializer):
class Meta:
model = Foo
fields = ('id', 'name',)
def to_representation(self, instance):
representation = super().to_representation(instance)
if instance.name!='': #condition
representation['email']=instance.name+"#xyz.com"#adding key and value
representation['currency']=instance.task.profile.currency #adding key and value some other relation field
return representation
return representation
In this way you can add key and value for each obj dynamically
hope u like it
This worked for me.
If we want to just add an additional field in ModelSerializer, we can
do it like below, and also the field can be assigned some val after
some calculations of lookup. Or in some cases, if we want to send the
parameters in API response.
In model.py
class Foo(models.Model):
"""Model Foo"""
name = models.CharField(max_length=30, help_text="Customer Name")
In serializer.py
class FooSerializer(serializers.ModelSerializer):
retrieved_time = serializers.SerializerMethodField()
#classmethod
def get_retrieved_time(self, object):
"""getter method to add field retrieved_time"""
return None
class Meta:
model = Foo
fields = ('id', 'name', 'retrieved_time ')
Hope this could help someone.
class Demo(models.Model):
...
#property
def property_name(self):
...
If you want to use the same property name:
class DemoSerializer(serializers.ModelSerializer):
property_name = serializers.ReadOnlyField()
class Meta:
model = Product
fields = '__all__' # or you can choose your own fields
If you want to use different property name, just change this:
new_property_name = serializers.ReadOnlyField(source='property_name')
As Chemical Programer said in this comment, in latest DRF you can just do it like this:
class FooSerializer(serializers.ModelSerializer):
extra_field = serializers.SerializerMethodField()
def get_extra_field(self, foo_instance):
return foo_instance.a + foo_instance.b
class Meta:
model = Foo
fields = ('extra_field', ...)
DRF docs source
Even though, this is not what author has wanted, it still can be considered useful for people here:
If you are using .save() ModelSerializer's method, you can pass **kwargs into it. By this, you can save multiple dynamic values.
i.e. .save(**{'foo':'bar', 'lorem':'ipsum'})
Add the following in serializer class:
def to_representation(self, instance):
representation = super().to_representation(instance)
representation['package_id'] = "custom value"
return representation