nested serializer showing null data
from rest_framework import serializers
from .models import PlayerTable, ChildTable
class ChildTableSerializer(serializers.ModelSerializer):
# x= ChildTable.objects.all().values
class Meta:
model = ChildTable
fields = ('season','goals','fk')
# fields =('fk',)
class PlayerTableSerializer(serializers.ModelSerializer):
player_details = ChildTableSerializer(many=True, read_only=True)
class Meta:
model = PlayerTable
fields = ('player_details',)
please help data getting by serializer is null
what is the field 'player-details'? It's not a field on your PlayerTable model. You need to use the name of the related field. In your case since you have a ForeignKey relationship ChildTable --> PlayerTable and you haven't specified the related_name, it's childtable_set. So if you do this it should work:
class PlayerTableSerializer(serializers.ModelSerializer):
childtable_set = ChildTableSerializer(many=True, read_only=True)
class Meta:
model = PlayerTable
fields = ('childtable_set',)
Alternatively, change your models naming to be more aligned with Django conventions:
class PlayerDetail(models.Model):
player = models.ForeignKey(Player, db_column="fk", related_name="player_details", null=True, blank=True, on_delete=models.CASCADE)
...
class Meta:
managed = False
db_table = "child_table"
class Player(models.Model):
name = models.CharField(db_column="player_name", ...)
class Meta:
db_table = "player_table"
then your serializer would work because the relation is player_details. This also has the advantage that when you do details.player you get the player object (now, you have to do details.fk but that actually doesn't return the foreign key value, it returns the Player object). Also your models have more pythonic names (Player not PlayerTable). Your code will be much more readable.
Related
Is there a way of filling some particular fields in a model using a field value of another model object?
For example, I thought of the following scenario:
1 - there are some models
from django.db import models
class Supplier(models.Model):
supplier = models.Charfield(max_length=50, unique=True)
class Object(models.Model):
object = models.Charfield(max_length=50)
supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE, to_field="supplier")
class Transaction(models.Model):
object_id = models.ForeignKey(Object, on_delete=models.CASCADE)
supplier = models.Charfield(max_length=50)
2 - Those models are serialized
from . import models
from rest_framework.serializers import ModelSerializer
class SupplierSerializer(ModelSerializer):
class Meta:
model = models.Supplier
fields = '__all__'
class ObjectSerializer(ModelSerializer):
class Meta:
model = models.Object
fields = '__all__'
class TransactionSerializer(ModelSerializer):
class Meta:
model = models.Transaction
exclude = ('supplier',)
3 - And there is a view
from . import models, serializers
from rest_framework.viewsets import ModelViewSet
class TransactionApiViewset(ModelViewSet):
queryset = models.Transaction.objects.all()
serializer_class = serializers.TransactionSerializer
When submiting a post with 'object_id' field to Transaction Api, I'd like that the 'supplier' field in Transaction Model autofills with the 'supplier' field value of Object object related to 'object_id'.
I'd appreciate any help.
I think you could use property(or cached_property) method for supplier instead of saving as a field:
class Transaction(models.Model):
object = models.ForeignKey(Object, on_delete=models.CASCADE)
#property
def supplier(self):
return self.object.supplier.supplier
If you need to keep supplier saved in Transaction table, then just keep the model definition as you posted, and add a validate method in serializer:
class TransactionSerializer(ModelSerializer):
def validate(self, data):
object = Object.objects.get(id=data["object_id"])
data["supplier"] = object.supplier.supplier
return data
That would automatically update Transaction with new object related supplier's supplier field.
I have two models and I want to call the field values associated with the foreign key present in both the models.
For example:
Say we have two models:
from django.db import models
from django.contrib.auth.models import User
class Bike(models.Model):
bike_model = models.CharField(max_length=200)
owner = models.ForeignKey(User,on_delete=models.CASCADE)
class Car(models.Model):
car_model = models.CharField(max_length=200)
owner = models.ForeignKey(User,on_delete=models.CASCADE)
And the relating serializer class is:
from rest_framework import serializers
from .models import Bike,Car
class BikeSerializer(serializers.ModelSerializer):
class Meta:
model = Bike
fields = ('bike_model','owner')
class CarSerializer(serializers.ModelSerializer):
class Meta:
model = Car
fields = ('car_model','owner')
Now, I want to add a field in BikeSerializer to get all the cars associated with the given owner. That is I want to make the following change:
class BikeSerializer(serializers.ModelSerializer):
cars_owned = ???
class Meta:
model = Bike
fields = ('bike_model','owner','cars_owned')
I am unable to get how the cars owned by the owner can be returned. Thus, for the data from serializer, I want the bike model, the owner id and the list of all the car ids that the owner has.
You should look this SerializerMethodField.
Basically, you need to create a method inside BikeSerializer that return cars owned.
class BikeSerializer(serializers.ModelSerializer):
cars_owned = serializers.SerializerMethodField()
class Meta:
model = Bike
fields = ('bike_model','owner','cars_owned')
def get_cars_owned(self, object):
# This is your query to get all cars associated
return object.cars_owned.all()
You may add seializer as field:
class BikeSerializer(serializers.ModelSerializer):
cars_owned = CarSerializer(source='owner.car_set', many=True, read_only=True)
class Meta:
model = Bike
fields = ('bike_model','owner','cars_owned')
I feel like this is a super basic question but am having trouble finding the answer in the DRF docs.
Let's say I have a models.py set up like so:
#models.py
class Person(models.Model):
name = models.CharField(max_length=20)
address = models.CharField(max_length=20)
class House(models.Model):
name = models.CharField(max_length=20)
owner = models.ForeignKey(Person)
And I have a ModelSerializer set up like so:
#serializers.py
class House(serializers.ModelSerializer):
class Meta:
model = House
fields = '__all__'
What I want to do is to be able to POST new House objects but instead of having to supply the pk of the Person object, I want to be able to supply the name of the Person object.
E.g.
post = {'name': 'Blue House', 'owner': 'Timothy'}
The actual models I'm using have several ForeignKey fields so I want to know the most canonical way of doing this.
One solution may be to use a SlugRelatedField
#serializers.py
class House(serializers.ModelSerializer):
owner = serializers.SlugRelatedField(
slug_field="name", queryset=Person.objects.all(),
)
class Meta:
model = House
fields = '__all__'
This will also change the representation of your serializer though, so it will display the Person's name when you render it. If you need to render the Person's primary key then you could either override the House serializers to_representation() method, or you could implement a small custom serializer field by inheriting SlugRelatedField and overriding to_representation() on that instead.
Change your serializer as below by overriding the create() method
class House(serializers.ModelSerializer):
owner = serializers.CharField()
class Meta:
model = House
fields = '__all__'
def create(self, validated_data):
owner = validated_data['owner']
person_instance = Person.objects.get(owner=owner)
return House.objects.create(owner=person_instance, **validated_data)
The code
I have these models :
class Activity(models.Model):
is_child_friendly = models.BooleanField()
free_wifi = models.BooleanField()
parking_avilable = models.BooleanField()
class RouteOrdering(models.Model):
route_id = models.ForeignKey(Route, on_delete=models.CASCADE)
activity_id = models.ForeignKey(Activity, on_delete=models.CASCADE, related_name='tracks')
day = models.IntegerField()
order = models.IntegerField()
class Meta:
ordering = ['order']
And these Serializers:
class ActivitySerializer(serializers.ModelSerializer):
class Meta:
model = Activity
class RouteOrderingSerializer(serializers.ModelSerializer):
class Meta:
model = RouteOrdering
fields = ('id','day','order','route_id','activity_id','tracks')
The problem
I want the RouteOrderingSerializer to return also the activity with the same activity_id. For this I thought I should use nested serializers.
What I've tried
I've tried adding a nested serializer like so:
activity = ActivitySerializer( read_only=True)
But this resulted no output in the endpoint. I know this is not the use case presented in the documentation, and this is probably why its noy working.
Ill be glad to see what code will work to get this done :)
I'm not sure why you got an error when you tried the following code. I assumed it was a bug related to the _id ending, but everything works fine when I run it.
class ActivitySerializer(serializers.ModelSerializer):
class Meta:
model = Activity
class RouteOrderingSerializer(serializers.ModelSerializer):
activity_id = ActivitySerializer( read_only=True)
class Meta:
model = RouteOrdering
fields = ('id','day','order','route_id','activity_id','tracks')
While you can, I don't recommend using a name ending in _id for a relation in Django. The issue is that Django internally stores all foreign keys under their <relation_name>_id. This allows various nice things, such as setting <object>.<relation_name>_id to an integer id and saving the object vs setting <object>.<relation_name> to the entire related object (which may require a db lookup to get in the first place). The one place I find this behaviour not intuitive is in serializers, where the default is to represent the relation as "<relation_name>": <id> instead of "<relation_name>_id": <id>, though you can explicitly declare the <relation_name>_id field and either remove or nest the <relation_name> field separately.
If you rename your relations to not have the trailing _id, the following will work:
class ActivitySerializer(serializers.ModelSerializer):
class Meta:
model = Activity
class RouteOrderingSerializer(serializers.ModelSerializer):
activity = ActivitySerializer( read_only=True)
class Meta:
model = RouteOrdering
fields = ('id','day','order','route_id','activity','tracks')
(Note that declaring route instead of route_id would be more in line with the default behaviour, but less clear in my opinion)
Try this:
class ActivitySerializer(serializers.ModelSerializer):
class Meta:
model = Activity
class RouteOrderingSerializer(serializers.ModelSerializer):
activity_id = ActivitySerializer()
class Meta:
model = RouteOrdering
fields = ('id','day','order','route_id','activity_id')
I want to fetch the foreign key values in PUT and GET but while using the many=True I am getting error TypeError object is not iterable.
Here are following the my snippets.
I have two models called MasterStatus and MasterType. In MasterType I have foreign key values of MasterStatus.
models.py
class MasterType(models.Model):
id = models.BigIntegerField(primary_key=True)
type_name = models.CharField(max_length=255, blank=True, null=True)
fk_status = models.ForeignKey(MasterStatus)
def __unicode__(self):
return u'%s' % (self.type_name)
class Meta:
managed = False
db_table = 'master_type'
In serializer I am using the many=True to get the nested values of foreignkey. Here I have used PrimaryKeyRelatedField serializer.
serializer.py
class MasterTypeSerializer(serializers.HyperlinkedModelSerializer):
fk_status = serializers.PrimaryKeyRelatedField(queryset=MasterStatus.objects.all(),many=True)
class Meta:
model = MasterType
fields = ('id', 'type_name', 'fk_status', 'last_modified_date', 'last_modified_by')
depth = 2
ForeignKey links to a single MasterStatus instance, therefore it is not many.
Your serializers should look something like this:
class MasterTypeSerializer(serializers.HyperlinkedModelSerializer):
fk_status = serializers.PrimaryKeyRelatedField(
queryset=MasterStatus.objects.all())
class Meta:
model = MasterRepaymentType
class MasterStatusSerializer(serializers.HyperlinkedModelSerializer):
fk_type = serializers.PrimaryKeyRelatedField(
queryset= MasterRepaymentType.objects.all(), many=True)
class Meta:
model = MasterStatus
Note that many is used on the fk_type field as a MasterStatus has many MasterRepaymentType.
Hope this helps.