Adding another class to serializer in Django - django

I wish to add another class to an existing serializer. I am able to do simple serializers, but this one is rather tricky because the second class I wish to add comes after the serializer's class in the models.py- Anybody with a clever solution?
The models.py is as follows:
class Pokemon(models.Model):
id = models.AutoField(primary_key=True)
type = models.OneToOneField(Type, on_delete=models.PROTECT)
stat = models.ForeignKey(Stat, on_delete=models.PROTECT, null=True)
class Skills(models.Model):
id = models.AutoField(primary_key=True)
pokemon = models.ForeignKey(Pokemon, on_delete=models.PROTECT)
owner = models.ForeignKey(Owner, on_delete=models.PROTECT, related_name='name')
and the serializers.py:
class PokemonSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Pokemon
fields = ('id', 'type', 'stat')
The display that I currently have is:
{
"id": 2228,
"type": "http://localhost:8000/type/4628/",
"stat": "99",
}
and the display that I am shooting for is:
{
"id": 2228,
"type": "http://localhost:8000/type/4628/",
"stat": "99",
"owner": "bob doe"
}
Thank you very much in advance!

You should add new serializer called owner for example
class Owner(serializers.ModelSerializer):
class Meta:
model = skills
fields = ('owner',)
in PokemonSerializer add field owner
owner = Owner(many=True, read_only=True)
You can read further more here

you can use source in the a CharField
class PokemonSerializer(serializers.HyperlinkedModelSerializer):
owner = CharField(source="owner.name")
class Meta:
model = Pokemon
fields = ('id', 'type', 'stat', 'owner')

Related

How to add data from one model to other django?

I am using generics to represent views
class PersonRetrieveView(generics.ListAPIView):
queryset = Person.objects.all()
serializer_class = PersonSerializer
and
class CommentRetrieveView(generics.RetrieveAPIView):
queryset = Comment.objects.all()
serializer_class = CommentSerializer
Person data looks like
{
"id": 2,
"user": {
"first_name": "Testuser",
"last_name": "TestUser1",
"id": 2
},
"name": "Test 2",
"city": "California",
"status": "NotActive",
"phone_number": "9876543222",
"age": 22,
"height": 180
}
and Comment
{
"id": 1,
"comment": "test",
"person": 2
}
Comment linked to Person by id. How can i add data from comment to PersonRetrieveView ?
Serializers looks like this
class PersonSerializer(serializers.ModelSerializer):
user = UserSerializer()
# comment = CommentSerializer()
class Meta:
model = Person
fields = '__all__'
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = '__all__'
Model looks like
class Person(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=20)
city = models.CharField(max_length=20)
status = models.CharField(max_length=9, default='NotActive')
phone_number = models.CharField(max_length=10)
class Comment(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
comment = models.CharField(max_length=255)
You have Many-to-one relationship and you did not set related_name on Foreign key so your comments are accessible through comment_set which can be used to nest serializer
class PersonSerializer(serializers.ModelSerializer):
user = UserSerializer()
comments = CommentSerializer(source='comment_set', many=True)
class Meta:
model = Person
fields = [ 'user', 'city', 'comments', ...]
Provided that your Person model has a relation to Comment models, simply add depth = 1 in your Person serializer as follows:
class PersonSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = Person
fields = '__all__'
depth = 1

Different write and read operations with DRF

I am using Django Rest Framework for a project and I am running into a problem. When the frontend creates a Team they want to reference all relationships with an ID, but when getting the Team, they want the data from the relationship. How can I achieve this?
models:
class Team(models.Model):
class Meta:
db_table = "team"
team_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
organization = models.ForeignKey(Organization, on_delete=models.CASCADE)
class Organization(models.Model):
class Meta:
db_table = "organization"
organization_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
class Position(models.Model):
class Meta:
db_table = "position"
position_id = models.AutoField(primary_key=True)
team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name="positions")
class Player(model.Model):
class Meta:
db_table = "player"
player_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
positions = models.ManyToManyField(Position, related_name="players")
serializers:
class TeamSerializer(serializers.ModelSerializer):
class Meta:
model = Team
fields = ["team_id", "name", "organization", "positions"]
positions = PositionSerializer(many=True) # This is merely for output. There is no need to create a position when a team is created.
organization = OrganizationSerializer() # Since an organization already exists I'd like to just give an organization_id when creating/editing a team.
# I don't think the other serializers matter here but can add them on request.
So when doing POST or PATCH on a team, I'd like the front end to be able to pass this payload
{
"name": "My Team",
"organization": 1
}
but when doing a GET on a team, I'd like the front end to receive this response.
{
"team_id": 1,
"name": "My Team",
"organization": {
"organization_id": 1,
"name": "My Organization"
},
"positions": [{
"position_id": 1,
"players": [{
"player_id": 1,
"name": "Member 1"
}
]
}
Is there a a way to achieve this?
In such situations define two serializers, one is for read operations and one is for write operations.
class TeamWriteSerializer(serializers.ModelSerializer):
# see, here no nested relationships...
class Meta:
model = Team
fields = ["name", "organization"]
class TeamReadSerializer(serializers.ModelSerializer):
class Meta:
model = Team
fields = ["team_id", "name", "organization", "positions"]
positions = PositionSerializer(many=True)
organization = OrganizationSerializer()
and now, use these two serializers properly in your views. For example, I hope you are using the ModelViewSet in views,
class TeamModelViewSet(viewsets.ModelViewSet):
def get_serializer_class(self):
if self.request.method.lower() == 'get':
return TeamReadSerializer
else:
return TeamWriteSerializer

Fetch foreign key object instead of ID in React from Django API

I have a model in my Django API with a foreign key for the parent, which references itself. I've serialized the data and returned it in a viewset. It returns an object in which the parent field shows the parent ID as an int. However, I'm trying to get it to display the actual values of the parent instead (name and category), so that I can then render the relevant data in my React frontend app. I've listed my model, serializer and viewset below, along with the object it's returning that shows the parent and also the child with the parent ID in the parent field. Can anyone please help?
class ComponentModel(models.Model):
name = models.CharField(max_length=50, blank=False)
wattage = models.PositiveIntegerField(blank=True, null=True)
category = models.CharField(max_length=13, choices=CATEGORY_CHOICES, blank=False)
parent = models.ForeignKey(
'self',
blank=True,
null=True,
on_delete=models.CASCADE
)
class ComponentSerializer(serializers.ModelSerializer):
class Meta:
model = ComponentModel
fields = ('id', 'name', 'wattage', 'category', 'parent')
class ComponentViewSet(viewsets.ModelViewSet):
queryset = ComponentModel.objects.all()
serializer_class = ComponentSerializer
[
{
"id": 1,
"name": "AMD",
"wattage": null,
"category": "cpu",
"parent": null
},
{
"id": 5,
"name": "760K",
"wattage": 100,
"category": "cpu",
"parent": 1
}
]
One way is to define a simpler serializer and then use that for the parent as such:
class ParentComponentSerializer(serializers.ModelSerializer):
class Meta:
model = ComponentModel
fields = ('name', 'category')
class ComponentSerializer(serializers.ModelSerializer):
class Meta:
model = ComponentModel
fields = ('id', 'name', 'wattage', 'category', 'parent')
parent = ParentComponentSerializer(many=False)
You can also make them inherit via:
class BaseComponentSerializer(serializers.ModelSerializer):
class Meta:
model = ComponentModel
fields = ['name', 'category']
class ComponentSerializer(serializers.ModelSerializer):
class Meta(BaseComponentSerializer.Meta):
fields = BaseComponentSerializer.Meta.fields + ['id', 'wattage', 'parent']
parent = ParentComponentSerializer(many=False)

How to retrieve foreign key field in Django rest framework?

Given the model and serializer classes below, when I retrieve Track details, it'll only show the Track title but not the related Artist.
How would I also show the Artist name when retrieving Track details?
models.py
class Artist (models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Track (models.Model):
artist = models.ForeignKey(Artist, blank=True, null=True, on_delete=models.SET_NULL, verbose_name="Artist")
title = models.CharField(max_length=100, verbose_name="Title")
def __str__(self):
return self.title
serializers.py
class ArtistSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(read_only=True)
name = serializers.CharField()
class Meta:
model = Artist
fields = ('id', 'name')
class TrackSerializer(serializers.ModelSerializer):
class Meta:
model = Track
fields = '__all__'
I think you need custom field, try this serializer:
class TrackSerializer(serializers.ModelSerializer):
class Meta:
model = Track
fields = ('title', 'artist','artist_name')
artist_name = serializers.SerializerMethodField('get_artists_name')
def get_artists_name(self, obj):
return obj.artist.name
It produce something like this.
[
{
"title": "Don't let me down",
"artist": 2,
"artist_name": "The Beatles"
},
{
"title": "Summertime",
"artist": 1,
"artist_name": "Ella Fitzgerald"
}
]
Try this serializer,
class ArtistSerializer(serializers.ModelSerializer):
class Meta:
model = Artist
fields = '__all__' # or array of fieldnames like ['name_1', 'name_2']
class TrackSerializer(serializers.ModelSerializer):
artist = ArtistSerializer()
class Meta:
model = Track
fields = ('title', 'artist')
Inorder to retrieve Artist details, which is a ForeignKey model, you need to use a nested serializer in django-rest-framework.
By using the TrackSerializer with a nested ArtistSerializer, the retrieved data would look something like this,
{
"title": "Some_Title",
"artist": {
"id": 2, #or id of the artist.
"name": "Artist_name"
}
}
As you can see in the official django rest framework documentations
You should define a serializer field for nested items
First create your Artist (nested item) serializer
class ArtistSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(read_only=True)
name = serializers.CharField()
class Meta:
model = Artist
fields = ('id', 'name')
Then you can use it on related model serializers
class TrackSerializer(serializers.ModelSerializer):
artist = ArtistSerializer()
class Meta:
model = Track
fields = ('title', 'artist')
In the current version of DRF you can simply do this
class TrackSerializer(serializers.ModelSerializer):
artist = serializers.StringRelatedField()
class Meta:
model = Track
fields = '__all__'
StringRelatedField may be used to represent the target of the relationship using its __str__ method.
REF

Using reverse relationships with django-rest-framework's serializer

My model looks like this:
class User(TimestampedModel):
name = models.CharField(max_length=30, null=False, blank=False)
device = models.CharField(max_length=255, null=False, blank=False)
class Comment(TimestampedModel):
user = models.ForeignKey(User, on_delete=models.PROTECT, blank=True, null=True)
contents = models.CharField(max_length=510)
rating = models.IntegerField(blank=False, null=False)
And my serializer looks like this:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('name',)
class CommentListItemSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = Comment
fields = ('user', 'contents', 'rating')
And the view:
class CommentsList(generics.ListAPIView):
serializer_class = CommentListItemSerializer
queryset = Comment.objects.all()
It's almost getting the job done ;). The response I'm getting looks like this:
"results": [
{
"user": {
"name": "Ania"
},
"contents": "Very good",
"rating": 6
},
{
"user": {
"name": "Anuk"
},
"contents": "Not very good",
"rating": 1
}
]
There are two problems with that response.
I don't want to have this nested object "user.name". I'd like to receive that as a simple string field, for example "username".
Serializer makes a database query (not a join, but a separate query) for each user, to get his/her name. Since that's unacceptable, how to fix that?
Serializer makes a database query (not a join, but a separate query)
for each user, to get his/her name.
You can use select_related() on the queryset attribute of your view. Then accessing user.name will not result in further database queries.
class CommentsList(generics.ListAPIView):
serializer_class = CommentListItemSerializer
queryset = Comment.objects.all().select_related('user') # use select_related
I don't want to have this nested object "user.name". I'd like to
receive that as a simple string field, for example "username"
You can define a read-only username field in your serializer with source argument. This will return a username field in response.
class CommentListItemSerializer(serializers.ModelSerializer):
# define read-only username field
username = serializers.CharField(source='user.name', read_only=True)
class Meta:
model = Comment
fields = ('username', 'contents', 'rating')
You can add custom functions as fields
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.PROTECT, blank=True, null=True)
contents = models.CharField(max_length=510)
rating = models.IntegerField(blank=False, null=False)
def username(self):
return self.user.name
class CommentListItemSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ('username', 'contents', 'rating')