I have the following object inheritance model.
class Room:
name = models.CharField(db_index=True, unique=True, max_length=255)
status = models.CharField(default=RoomStatus.ACTIVE, max_length=256, null=True)
members = models.ManyToManyField(User)
last_activity = models.DateTimeField(default=timezone.now)
And the inherited models are,
class LeagueRoom(Room):
league = models.ForeignKey(League, on_delete=models.CASCADE, null=True)
location = models.ForeignKey(Location, on_delete=models.CASCADE, null=True)
logo_url = models.CharField(max_length=1024, null=True)
and
class ClubRoom(Room):
club = models.ForeignKey(Club, on_delete=models.CASCADE, null=True)
location = models.ForeignKey(Location, on_delete=models.CASCADE, null=True)
logo_url = models.CharField(max_length=1024, null=True)
The respective Serializers are as follows,
class RoomSerializer(serializers.ModelSerializer):
members = UserSerializer(read_only=True, many=True)
class Meta:
model = Room
fields = ('id', 'name', 'status', 'members', 'created', 'modified', 'last_active')
and
class LeagueRoomSerializer(serializers.ModelSerializer):
location = LocationSerializer(read_only=True)
league = LeagueSerializer(read_only=True)
room = RoomSerializer(read_only=True)
class Meta:
model = LeagueRoom
fields = ('id', 'name', 'location', 'status', 'league',
'logo_url', 'room', 'created', 'modified',)
and
class ClubRoomSerializer(serializers.ModelSerializer):
location = LocationSerializer(read_only=True)
club = ClubSerializer(read_only=True)
room = RoomSerializer(read_only=True)
class Meta:
model = ClubRoom
fields = ('id', 'name', 'location', 'club', 'logo_url',
'status', 'room', 'created', 'modified',)
My problem is that I have fetched all rooms for an user in the following manner.
rooms = user.room_set.order_by('-last_activity')
Now I want to Serialize this data based on the room type. Thus instead of using the RoomSerializer I want to traverse the list of rooms and if the room is ClubRoom, then use ClubRoomSerializer or else LeagueRoomSerializer.
I'm not sure how to determine the child object from the parent.
Can someone help me with this.
class RoomSerializer(serializers.Serializer):
def to_representation(self, instance):
if isinstance(instance, LeagueRoom)
serializer_class = LeagueRoomSerializer
elif isinstance(instance, ClubRoom):
serializer_class = ClubRoomSerializer
return serializer_class(instance=instance).data
queryset = user.room_set.order_by('-last_activity')
serialized = RoomSerializer(queryset, many=True).data
Related
I currently have to models where a Node can have many Benchmark's, but when displaying it to the end users, I only want the serializer to return the latest benchmark for the node, instead of all of them which it currently does. How can I do this?
Models.py
class Node(models.Model):
node_id = models.CharField(max_length=42, unique=True)
wallet = models.CharField(max_length=42, null=True, blank=True)
earnings_total = models.FloatField(null=True, blank=True)
data = models.JSONField(null=True)
online = models.BooleanField(default=False)
version = models.CharField(max_length=5)
updated_at = models.DateTimeField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
class Benchmark(models.Model):
benchmark_score = models.IntegerField()
benchmarked_at = models.DateTimeField(default=timezone.now)
provider = models.ForeignKey(Node, on_delete=models.CASCADE)
serializers.py
class BenchmarkSerializer(serializers.ModelSerializer):
class Meta:
model = Benchmark
fields = ['benchmark_score', 'benchmarked_at']
class NodeSerializer(serializers.ModelSerializer):
benchmark_set = BenchmarkSerializer(many=True)
class Meta:
model = Node
fields = ['earnings_total', 'node_id', 'data',
'online', 'version', 'updated_at', 'created_at', 'benchmark_set']
You can use SerializerMethodField to have this result :
class NodeSerializer(serializers.ModelSerializer):
last_benchmark = SerializerMethodField('get_benchmark')
class Meta:
model = Node
fields = ['earnings_total', 'node_id', 'data',
'online', 'version', 'updated_at', 'created_at', 'last_benchmark ']
def get_benchmark(self, node):
benchmark = Benchmark.objects.last()
serializer = BenchmarkSerializer(instance=benchmark , many=True)
return serializer.data
To resume, you create a new field in the serializer that return the latest benchmark object serialized with BenchmarkSerializer.
I have a few models to represent a user. A user has a garden, a profile and a gardener_profile. When serialising the user objects, garden and profile are getting showed, but gardener_profile is not. All of them are one to one relations. In my swagger doc the gardener_profile is recognized, but not in the response object.
Here are the serializers:
class WorkingHoursSerializer(serializers.ModelSerializer):
gardener_profile = serializers.StringRelatedField(read_only=True)
class Meta:
model = WorkingHours
fields = '__all__'
class GardenSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=False, queryset=GardreamUser.objects.all())
class Meta:
model = Garden
fields = ['id', 'url', 'grass', 'beds', 'terrace', 'tracks', 'entry', 'user']
class GardenerProfileSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=False, queryset=GardreamUser.objects.all())
working_hours = WorkingHoursSerializer(many=True)
class Meta:
model = GardenerProfile
fields = ['id', 'url', 'working_radius', 'salary', 'iban', 'contract', 'user', 'working_hours']
def create(self, validated_data):
working_hours_data = validated_data.pop('working_hours')
gardener_profile = GardenerProfile.objects.create(**validated_data)
for working_hour_data in working_hours_data:
WorkingHours.objects.create(gardener_profile=gardener_profile, **working_hour_data)
return gardener_profile
class UserProfileSerializer(serializers.ModelSerializer):
user = serializers.StringRelatedField(read_only=True)
class Meta:
model = UserProfile
fields = '__all__'
class UserSerializer(serializers.HyperlinkedModelSerializer):
profile = UserProfileSerializer(required=True)
garden = GardenSerializer(read_only=True)
gardener_profile = GardenerProfileSerializer(read_only=True)
class Meta:
model = CustomUser
fields = ['id', 'url', 'username', 'email', 'first_name', 'last_name', 'password', 'groups', 'profile',
'garden', 'gardener_profile']
extra_kwargs = {'password': {'write_only': True}}
And here are the models:
class CustomUser(AbstractUser):
email = models.EmailField(unique=True)
class UserProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='profile')
address = models.CharField(max_length=255)
country = models.CharField(max_length=50)
city = models.CharField(max_length=50)
zip = models.CharField(max_length=5)
class Garden(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
grass = models.DecimalField(max_digits=6, decimal_places=2)
terrace = models.DecimalField(max_digits=6, decimal_places=2)
class GardenerProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
salary = models.DecimalField(max_digits=6, decimal_places=2)
contract = models.FileField(null=True, blank=True)
class WorkingHours(models.Model):
gardener_profile = models.ForeignKey(GardenerProfile, related_name='working_hours', on_delete=models.CASCADE)
weekday = models.IntegerField(choices=WEEKDAYS)
from_hour = models.TimeField()
to_hour = models.TimeField()
class Meta:
ordering = ('weekday', 'from_hour')
unique_together = ('weekday', 'gardener_profile')
I found the solution: add related_name='gardener_profile' to the user field at GardenerProfile
Here is my view
serializer_class = SceneSerializer
permission_classes = (permissions.IsAuthenticated,)
def get_queryset(self):
scene = Scene.objects.filter(user = self.request.user)
for s in scene:
test = Scene.objects.get(static_id = s.static_id)
lights = test.light.all()
temp = list(chain(scene, lights))
Here are my 2 serializers
class LightSerializer(serializers.ModelSerializer):
class Meta:
model = Light
fields = '__all__'
class SceneSerializer(serializers.ModelSerializer):
class Meta:
model = Scene
fields = '__all__'
Here is my models
class Light(models.Model):
static_id = models.AutoField(primary_key=True)
id = models.CharField(max_length=100, blank=True, null=True)
company = models.CharField(max_length=25, blank=True, null=True)
name = models.CharField(max_length=100, blank=True, null=True)
label = models.CharField(max_length=100, blank=True, null=True)
user = models.ForeignKey(User,on_delete=models.DO_NOTHING, related_name='userLights' )
def __str__(self):
return "{0} - {1}".format(self.user,self.static_id)
class Scene(models.Model):
static_id = models.AutoField(primary_key=True)
color = RGBColorField()
title = models.CharField(max_length=100, blank=True, null=True)
user = models.ForeignKey(User,on_delete=models.DO_NOTHING, related_name='userScenes' )
light = models.ManyToManyField(Light)
def __str__(self):
return "{0} - {1}".format(self.user,self.static_id)
Goal is to return the complete objects of the scene and the lights attached to each scene. the chain is working correctly but when I return the chain I am getting
You can include reverse relationships in serializers by adding the reverse relationship to fields
class SceneSerializer(serializers.ModelSerializer):
class Meta:
model = Scene
fields = ['light', 'static_id', 'color', 'title', 'user']
This will just return a list of ids, to serialize the related objects you can add the related serializer with many=True
class SceneSerializer(serializers.ModelSerializer):
light = LightSerializer(many=True)
class Meta:
model = Scene
fields = ['light', 'static_id', 'color', 'title', 'user']
I currently have a serializer with the following fields
class Meta:
model = Asset
fields = ('id', 'uuid', 'asset_category', 'asset_sub_category',
'make_label',
'asset_code', 'serial_number', 'model_number',
'checkin_status', 'created_at',
'last_modified', 'current_status', 'asset_type',
'allocation_history', 'specs', 'purchase_date',
'notes', 'assigned_to', 'asset_location'
)
Serializer
class AssetSerializer(serializers.ModelSerializer):
checkin_status = serializers.SerializerMethodField()
allocation_history = serializers.SerializerMethodField()
assigned_to = UserSerializer(read_only=True)
asset_category = serializers.SerializerMethodField()
asset_sub_category = serializers.SerializerMethodField()
make_label = serializers.SerializerMethodField()
asset_type = serializers.SerializerMethodField()
model_number = serializers.SlugRelatedField(
queryset=AssetModelNumber.objects.all(),
slug_field="model_number"
)
class Meta:
model = Asset
fields = ('id', 'uuid', 'asset_category', 'asset_sub_category',
'make_label',
'asset_code', 'serial_number', 'model_number',
'checkin_status', 'created_at',
'last_modified', 'current_status', 'asset_type',
'allocation_history', 'specs', 'purchase_date',
'notes', 'assigned_to', 'asset_location'
)
depth = 1
read_only_fields = ("uuid",)
View
class ManageAssetViewSet(ModelViewSet):
serializer_class = AssetSerializer
queryset = Asset.objects.all()
# permission_classes = [IsAuthenticated, IsAdminUser]
# authentication_classes = (FirebaseTokenAuthentication,)
http_method_names = ['get', 'post', 'put', 'delete']
filter_backends = (filters.DjangoFilterBackend,)
filterset_class = AssetFilter
def get_object(self):
queryset = Asset.objects.all()
obj = get_object_or_404(queryset, uuid=self.kwargs['pk'])
return obj
Model
Asset Model. Some fields have been ommited
class Asset(models.Model):
"""Stores all assets"""
uuid = models.UUIDField(unique=True, default=uuid.uuid4, editable=False)
asset_code = models.CharField(
unique=True, null=True, blank=True, max_length=50)
serial_number = models.CharField(
unique=True, null=True, blank=True, max_length=50)
created_at = models.DateTimeField(auto_now_add=True, editable=False)
asset_location = models.ForeignKey('AndelaCentre', blank=True, editable=True, null=True,
on_delete=models.PROTECT)
purchase_date = models.DateField(
validators=[validate_date],
null=True, blank=True)
last_modified = models.DateTimeField(auto_now=True, editable=False)
assigned_to = models.ForeignKey('AssetAssignee',
blank=True,
editable=False,
null=True,
on_delete=models.PROTECT)
model_number = models.ForeignKey(AssetModelNumber,
null=True,
on_delete=models.PROTECT)
current_status = models.CharField(editable=False, max_length=50)
notes = models.TextField(editable=False, default=" ", )
However, on the browsable Api, only 4 fields are showing on the UPDATE/PUT form as shown in the diagram below
What could be the reason some of the other fields are not appearing here. What determines which fields are updatable??
Well, the problem is when you set depth = 1 ModelSerializer tries to generate a NestedSerializer field for any foreignkey related field which you have not explicitly mentioned. And that NestedSerializer field is a read only field. That's why Assest Location is not being displayed. Remove that depth = 1 line and DRF will map the said field with the default mapping i.e. PrimaryKeyRelatedFiel and you will see that the said field is displayed.
In your model, you make editable=False for many fields. That fields won't display. If you want to display and edit that field remove that option.
For more info refer question
I was trying to add new relation to many to many records,
for example i have these models:
models.py
class Team(models.Model):
name = models.CharField(blank=True, unique=True, max_length=100)
players = models.ManyToManyField(User, blank=True, related_name='players')
class TeamInvite(models.Model):
from_user = models.ForeignKey(User, on_delete=models.DO_NOTHING, related_name='invite_by', blank=True, null=True)
to_user = models.ForeignKey(User, on_delete=models.DO_NOTHING, related_name='invite_to', blank=True, null=True)
team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name='invite_to_team', blank=True, null=True)
status = models.NullBooleanField(blank=True, null=True, default=None,)
and my serializer:
serializers.py
class TeamInviteCreateSerializer(serializers.ModelSerializer):
team = serializers.PrimaryKeyRelatedField(queryset=Team.objects.all())
from_user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())
class Meta:
model = TeamInvite
fields = ('id', 'from_user', 'to_user', 'team', 'status')
after that the user which in to_user will take an action to TeamInvite like accept or decline.
I need the serializer which will take the new user and add him to the existing team like the following serializer:
class TeamInviteAcceptDeclineSerializer(serializers.ModelSerializer):
method_name = serializers.SerializerMethodField()
class Meta:
model = TeamInvite
fields = ('id', 'from_user', 'date_time', 'team', 'method_name', 'status')
def get_method_name(self, *args, **kwargs):
method_name = None # kwargs['context']['request'].method_name
return method_name
def update(self, instance, validated_data):
instance.team = validated_data.get('team', instance.team)
method_name = validated_data.get('method_name')
instance.status = validated_data.get('status', instance.status)
instance.to_user = validated_data.get('to_user', instance.to_user)
if method_name == 'decline':
instance.status = False
else:
instance.status = True
team = Team.objects.get(pk=instance.team.pk)
team.players.add(instance.to_user)
# team.players.create(team_id=team, user_id=instance.to_user)
team.save()
instance.save()
return instance
update function does not add the user to existing team and doesn't raise any error either. What am i missing here?
my request was:
{
"from_user": 1,
"to_user": 23
"team": 64,
"method_name": "accept",
"status": null
}
thank you
I got the missing point in my code..
it was in:
class TeamInviteAcceptDeclineSerializer(serializers.ModelSerializer):
method_name = serializers.SerializerMethodField()
class Meta:
model = TeamInvite
fields = ('id', 'from_user', 'date_time', 'team', 'method_name', 'status')
fields = missed 'to_user' pram