Confused to Serializer relations in Django Rest - django-rest-framework

In the Serializer relations part on Django official website, it provides a music albums example to represent and explain some relationship.
class Album(models.Model):
album_name = models.CharField(max_length=100)
artist = models.CharField(max_length=100)
class Track(models.Model):
album = models.ForeignKey(Album, related_name='tracks')
order = models.IntegerField()
title = models.CharField(max_length=100)
duration = models.IntegerField()
class Meta:
unique_together = ('album', 'order')
ordering = ['order']
def __unicode__(self):
return '%d: %s' % (self.order, self.title)
Later, it interacts model with Serializer.
class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.StringRelatedField(many=True)
class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')
The result is:
{
'album_name': 'Things We Lost In The Fire',
'artist': 'Low',
'tracks': [
'1: Sunflower',
'2: Whitetail',
'3: Dinosaur Act',
...
]
}
I am confused variable tracks in the AlbumSerializer. It seems that the variable tracks is irrelevant to Album and Track model.How it is related to the Album and Track models? How it calls and invokes unicode method of Track model in the AlbumSerializer?

It is related via related_name
album = models.ForeignKey(Album, related_name='tracks')
The related_name attribute specifies the name of the reverse relation from Album to Track.
Read more https://docs.djangoproject.com/en/1.9/ref/models/fields/#django.db.models.ForeignKey.related_name

Related

Django Rest - get related data in M2M relation

I have 2 models:
class Artist(models.Model):
name = models.CharField(max_length=255)
music_type = models.CharField(max_length=50)
class Event(models.Model):
event_name = models.CharField(max_length=255)
description = models.TextField()
place = models.CharField(max_length=50)
longitude = models.DecimalField(max_digits=9, decimal_places=6)
latitude = models.DecimalField(max_digits=9, decimal_places=6)
date = models.DateField()
artists = models.ManyToManyField(Artist)
For every artist I would like to get list of other artists if they have ever been in any event together.
I was able to create close solution, but only for specific artist:
def get_related_artists(request):
artist_id = 2
related_events = Artist.objects.filter(id=artist_id).first().event_set.all()
related_artists_ids = []
for event in related_events:
related_artists_ids = related_artists_ids + list(event.artists
.all()
.values_list('id', flat=True)
.all())
related_artists = Artist.objects\
.filter(id__in=set(related_artists_ids))\
.exclude(id=artist_id)
serializer = ArtistRelatedSerializer(related_artists, many=True)
return JsonResponse(serializer.data, safe=False)
So firstly I get all event where specific artist took part. Later I iterate over this events and get other artist's ids. Another step is to remove duplicated ids and specific artist id. At the end I use serializer to return data.
Serializer looks like:
class ArtistRelatedSerializer(serializers.ModelSerializer):
class Meta:
model = Artist
fields = '__all__'
Unfortunately I think it isn't optimal solution and works only for hardcoded artist's id. I would like to get all artists and for ech list of other artists.
I was thinking about creating loop and iterate over Artist.objects.count() but I couldn't find a solid solution to maintain all this queries.
Is there any other, maybe easier way to solve this solution?
You can solve this issue from Serializer by using SerializerMethodField. Get all events in each Artist, the example below:
class ArtistSerializer(serializers.ModelSerializer):
class Meta:
model = Artist
fields = ('id', 'name', 'music_type', 'events')
events = serializers.SerializerMethodField()
def get_events(self, obj):
events_qs = Event.objects.filter(artists__in=[obj.id])
events = EventSerializer(
events_qs, many=True, context=self.context).data
return events
To avoid the error by the circle imports, you should use import in the function

Django Rest Framework - Nested Serializer - Access one attribute from a list

In Dango Rest Framework, how do I write a nested serializer that accesses a single attribute of one item from a related list of items?
For example, using the models from the documentation,
class Album(models.Model):
album_name = models.CharField(max_length=100)
artist = models.CharField(max_length=100)
class Track(models.Model):
album = models.ForeignKey(Album, related_name='tracks', on_delete=models.CASCADE)
order = models.IntegerField()
title = models.CharField(max_length=100)
duration = models.IntegerField()
How would I write a serializer that would return the album name, the title of the first track, and the title of the last track?
A sample response might be:
{
"album_name": "Kid A",
"first_track_title": "Everything in its right place",
"last_track_title": "Motion picture soundtrack"
}

Saving(create,update) along with foreignkey value from another model which is related user model

It may be a challenging question if you didn't get rightly. Here I have three models in which department model should be created by taking its place name from Place model which is related to the staff model. The Staff Model is in a OneToOneField relationship with User, so when a user creates a department the place name should be passed like HiddenField in HTML . This place name is related to place model with the user with GenericForeignKey. i have created a serializer which is not working as expected, it is returning the place name ,
.
In shortly I want to create a department while place should be selected from current user ID
class Staff(BaseModel):
ROLES = [
('ADMIN', 'Admin'),
('TEACHER', 'Teacher')
]
auth_user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=50)
school_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
school_id = models.PositiveIntegerField()
school = GenericForeignKey('school_content_type', 'school_id')
role = models.CharField(null=True, blank=True, choices=ROLES, max_length=20)
class Places(BaseModel):
name = models.CharField(max_length=50)
code = models.CharField(max_length=12, unique=True)
class Department(BaseModel):
TYPES = [
('ONLINE', 'Online'),
('OFFLINE', 'OfFline')
]
department_type = models.CharField(max_length=15, choices=TYPES)
service_no = models.CharField(max_length=50)
instructions = models.TextField(null=True, blank=True)
place = models.ForeignKey(Places, to_field='code', db_column='place_code', on_delete=models.PROTECT)
SERIALIZERS
class DepartmentCreateSerializer(serializers.ModelSerializer):
place_code=serializers.CharField(read_only=True)
class Meta:
model=Department
fields = ('department_type','service_no','instructions')
def get_place(self, request):
user_id=self.context['request'].user.id
school_id=Staff.objects.get(auth_user_id= user_id).school_id
places_code_name=Places.objects.get(id= school_id).name
class PlacesSerializer(serializers.ModelSerializer):
class Meta:
model = Places
fields = ('id', 'code', 'name')
from places.serializers import PlacesSerializer
class DepartmentCreateSerializer(serializers.ModelSerializer):
place= PlacesSerializer(read_only=True)
class Meta:
model=Department
fields = ('place','service_no','instructions')
def validate(self, attrs):
palce_obj = self.context['request'].user.staff.place()
attrs.update({'place': place_obj})
attrs = super().validate(attrs)
if not attrs.get('place', None):
raise serializers.ValidationError({'place': ["Place required"]})
return attrs

Django Rest Framework get many-to-many relation showing in API

My little podcast backend has some models, one of which is Episode. Now there are two new ones for guests (Guest) and topics (Topic) - both of which have a ManyToManyField to Episode. This basically works as intended in the admin. Now I need to bring that to the API.
The detailed Serializer/View for each episode already has some other related data (no many-to-many so far). I also checked this post but still can't get it it work and think I might miss something.
Here's the essence just for the guest example to keep it dense, topic is literally identical:
Episode Model:
class Episode(models.Model):
number = models.CharField(max_length=10, blank=False)
title = models.CharField(max_length=100, blank=False)
show = models.ForeignKey(Show, on_delete=models.PROTECT)
created_at = models.DateTimeField(auto_now=False)
published_at = models.DateTimeField(auto_now=False)
updated_at = models.DateTimeField(auto_now=False)
cover_image = models.URLField(null=True)
def __str__(self):
return self.title
Guest Model:
class Guest(models.Model):
episodes = models.ManyToManyField(Episode)
name = models.CharField(max_length=100, blank=False)
twitter = models.CharField(max_length=20, null=True)
def __str__(self):
return self.name
Serializer:
class GuestSerializer(serializers.ModelSerializer):
class Meta:
model = Guest
fields = ('name',)
Episode Serializer where it should appear:
class EpisodeDetailSerializer(serializers.ModelSerializer):
...
guest = GuestSerializer(many=True, read_only=True)
topic = TopicSerializer(many=True, read_only=True)
class Meta:
model = Episode
fields = (..., 'guest', 'topic')
depth = 1
I have put some data on for guests and topics but I can't get them showing up on the API. I've also tried 'Guest.episodes.through' like I do have it on the admin for Inline Admin Classes but that didn't change anything.
Your Episode model does not have any forward relationships to your Guest or Topic models. For that reason, your Episode serializer should seek to serialize guest_set and topic_set instead of guest and topic.
If you specify a related name for those M2M field, you can customize the naming convention here. Right now you have this:
class Guest(models.Model):
episodes = models.ManyToManyField(Episode)
...
Which means that to get to all the Guests associated with a given Episode called ep, the reverse lookup would be ep.guest_set.all(). Define a related name and you can call the middle term whatever you want:
class Guest(models.Model):
episodes = models.ManyToManyField(Episode, related_name='guests')
...
With this you could use ep.guests.all(), and you could update your Episode serializer to look for guests as opposed to guest_set.

Is there more better cancel nested ManyToMany field unique check

# models.py
class Student(models.Model):
name = models.CharField( max_length=256)
student_numer = models.CharField(max_length=256)
teachers = models.ManyToManyField(Teacher)
class Teacher(models.Model):
name = models.CharField(max_length=256)
# serializers.py
class StudentSerializer(serializers.ModelSerializer):
class Meta:
model = Student
fields = ('name', 'student_number', )
class TeacherSerializer(serializers.ModelSerializer):
students = StudentSerializer(many=True)
class Meta:
model = Teacher
fields = '__all__'
def update(self, instance, validated_data):
students = validated_data.pop('students')
# here I want delete all old students, and add all new students
# of course, I have more better way to update it, but here just for simple.
But in fact, if I update teacher instance, and if the students not change, it will
raise: student with this student_numer already exist.
I know why, because StudentSerializer's student_numer field has the validator check unique.
And I can add some code like this to fixed this problem:
class StudentSerializer(serializers.ModelSerializer):
class Meta:
model = Student
fields = ('name', 'student_number', )
extra_kwargs = {
'student_numer': {
'validators':[]
}
}
Now I want to know is there any more better way ??

Resources