Creating a Nested Serializer with two "Sibling" Models - django

I'm attempting to get a JSON output similar to this:
{
name: John Doe,
best_buy_price: 420,
best_sell_price: 69,
player_profile: {
tsn_link: https://a_link.com
}
playerlistingadvanced: { # This is where I'm having the issue
sales_minute: 7,
}
}
I have three models. playerProfile being the "main" model and playerListing and playerListingAdvanced are connected to playerProfile via a one-to-one relationship. playerPfofile will have its own endpoint, but I'd also like to create an endpoint that is primarily the listings and advanced listing data (as seen above).
Here is a stripped down version of the model.py file:
class PlayerProfile(models.Model):
card_id = models.CharField(max_length=120, unique=True, primary_key=True)
name = models.CharField(max_length=420, null=True)
tsn_link = models.CharField(max_length=420, null=True)
class PlayerListing(models.Model):
player_profile = models.OneToOneField(
PlayerProfile,
on_delete=models.CASCADE,
null=True)
name = models.CharField(max_length=420, null=True)
best_sell_price = models.IntegerField(null=True)
best_buy_price = models.IntegerField(null=True)
class PlayerListingAdvanced(models.Model):
player_profile = models.OneToOneField(
PlayerProfile,
on_delete=models.CASCADE,
null=True)
sales_minute = models.DecimalField(max_digits=1000, decimal_places=2, null=True)
Here is the serializer.py I have tried, but haven't gotten to work.
class PlayerListingAdvancedForNestingSerializer(serializers.ModelSerializer):
class Meta:
model = PlayerListingAdvanced
fields = (
'sales_minute',
'last_week_average_buy',
'last_week_average_sell',
)
class PlayerListingSerializer(serializers.ModelSerializer):
player_profile = PlayerProfileForListingSerializer() # works
playerlistingadvanced = PlayerListingAdvancedForNestingSerializer() #doesn't work
class Meta:
model = PlayerListing
fields = (
'name',
'best_sell_price',
'best_buy_price',
'playerlistingadvanced',
'player_profile'
)
I'm assuming because playerListing and playerListingAdvanced are not directly related, that I'll need to do something else to make this work. Can someone point me in the right direction?

You can use SerializerMethodField. Your assuming is correct. Try this:
class PlayerListingSerializer(serializers.ModelSerializer):
player_profile = PlayerProfileForListingSerializer()
playerlistingadvanced = serializers.SerializerMethodField()
class Meta:
model = PlayerListing
fields = (
'name',
'best_sell_price',
'best_buy_price',
'playerlistingadvanced',
'player_profile'
)
def get_playerlistingadvanced(self, obj: PlayerListing):
player_profile = obj.player_profile
if hasattr(player_profile, 'playerlistingadvanced'):
serializer = PlayerListingAdvancedForNestingSerializer(instance=player_profile.playerlistingadvanced)
return serializer.data
return None

Related

How to add nested field after creating the ModelSerializer? Not in the class

Basically i want to have fk_inventory as a nested field in StorageRackSerializer but as you guys can see I also need to use StorageRackSerializer in InventorySerializer.
How can i set the field after creating the serializer class?
I have tried creating a fk_inventory field and set it to None and tried to set to InventorySerializer afterwards but didn't work.
class Inventory(models.Model):
inventory_id = models.AutoField(primary_key=True)
fk_building = models.OneToOneField(Store, on_delete=models.CASCADE, unique=True, related_name='inventory')
def __str__(self):
return f"{self.inventory_id}"
class StorageRack(models.Model):
storage_rack_id = models.AutoField(primary_key=True)
quantity = models.IntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(50)])
fk_inventory = models.ForeignKey(Inventory, on_delete=models.CASCADE, related_name="storage_racks")
fk_product_id = models.ForeignKey(Product, on_delete=models.SET_NULL, null=True, related_name="storage_racks")
def __str__(self):
return f"{self.storage_rack_id}"
class StorageRackSerializer(serializers.ModelSerializer):
fk_product_id = ProductSerializer(read_only=True)
fk_inventory = None
class Meta:
model = StorageRack
fields = ('storage_rack_id', 'quantity', 'fk_inventory', 'fk_product_id')
class InventorySerializer(serializers.ModelSerializer):
fk_building = StoreSerializer()
storage_racks = StorageRackSerializer(many=True)
class Meta:
model = Inventory
fields = ('inventory_id', 'fk_building', 'storage_racks')
StorageRackSerializer.fk_inventory = InventorySerializer()
You can add a field 'fk_inventory' in the validated data from SorageRackSerializer
validated_data = StorageRackSerializer(data=data)
validated_data['fk_inventory'] = InventorySerializer().data

Django Rest Framework get attributes from foreign key's class

here's my models:
class Recruteur(models.Model):
entrepriseName = models.CharField(max_length=50)
emplacement = models.CharField(max_length=50)
class Offre(models.Model):
title = models.CharField(max_length=100, blank=True, default=0)
idRecruteur = models.ForeignKey(Recruteur,verbose_name = "idRecruteur", on_delete=models.CASCADE, default=None)
and here's my api.py:
class VilleViewSet(ModelViewSet):
queryset = Offre.objects.values('idRecruteur__emplacement').distinct()
serializer_class = VilleSerializer
serializers.py:
class EmplacementSerializer(serializers.ModelSerializer):
class Meta:
model = Recruteur
fields = ('emplacement',)
class VilleSerializer(serializers.ModelSerializer):
emplacements = EmplacementSerializer(source='idRecruteur', read_only=True)
class Meta:
model = Offre
fields = ( 'emplacements',)
i was expecting a result like this
but i got nothing instead ..
Any ideas why?
You get nothing, because there is no emplacements field in Offre model. I guess, what you want is to get serialized Recruteur record, that is referenced by idRecruteur field. Try this:
class VilleSerializer(serializers.ModelSerializer):
idRecruteur = EmplacementSerializer(read_only=True)
class Meta:
model = Offre
fields = ( 'idRecruteur',)

Nested serializers in drf throws error with one to one field

My input json is :
{
"availability": "Current",
"drive_type": [{
"drive_name": "drive1",
"requirements": {
"performance_unit": "by_iops",
}
}]
}
I am getting error Cannot assign "
OrderedDict([('performance_unit', 'Basic')])":
"DriveType.requirements" must be a "Requirements" instance
.I am not able to figure it out to map in create method for one to one fields in tables
Below are my models.py
class ProductLine(models.Model):
availability = models.CharField(max_length=20, blank=True, null=True)
class Meta:
db_table = "product_line"
class DriveType(models.Model):
drive_name = models.CharField(max_length=20, blank=True, null=True)
product_line = models.ForeignKey(ProductLine, related_name="drive_type")
class Meta:
db_table = "drive_type"
class Requirements(models.Model):
performance_unit = models.CharField(max_length=100, blank=True, null=True)
drive_type = models.OneToOneField(DriveType,on_delete=models.CASCADE,primary_key=True,related_name="requirements")
class Meta:
db_table = "requirements"
Serializers.py :
class DriveTypeSerializer(serializers.ModelSerializer):
requirements = RequirementsSerializer(many = True)
class Meta:
model = DriveType
fields = (
"drive_name","workload_type")
class ProductLineSerializer(serializers.ModelSerializer):
drive_type = DriveTypeSerializer(many=True)
class Meta:
model = ProductLine
fields = ('availability', "drive_type")
def create(self, validated_data):
print("validate_data",validated_data)
drive_type_data = validated_data.pop("drive_type")
product_line = ProductLine.objects.create(**validated_data)
for drive_data in drive_type_data:
drive_type = DriveType.objects.create(product_line=product_line, **drive_data)
return product_line
You have one to one relationship of DriveType and Requirements
So remove many = True from DriveTypeSerializer for RequirementsSerializer
class DriveTypeSerializer(serializers.ModelSerializer):
requirements = RequirementsSerializer()
class Meta:
model = DriveType
fields = ("drive_name","workload_type")
Your input json has only one object of requirements not a list

How to serialize an object instance which gets the data from 2 separate models using Django Rest Framework?

Query :
I have a GET request which gives the data from the 2 models (single_tracklog_object in View). However, when I serialize this object instance using the serializers I am getting an empty data for devices as below.
{
"lat": "51.0038",
"lon": "8.0038",
"speed": 50,
"course": 5,
"device": {} # this needs to be having a value but is empty.
}
I am not understanding why its happening. Please guide me on this.
My Models :
class Tracklogs(models.Model):
tracklog_id = models.AutoField(primary_key=True)
device = models.ForeignKey(Tracking_devices, related_name='tracklogs')
lat = models.CharField(max_length=10, null=False)
lon = models.CharField(max_length=11, null=False)
.........
#timestamps
tracker_datetime = models.DateTimeField(blank=True, null=True)
class Meta:
get_latest_by = "tracker_datetime"
class Tracking_devices(models.Model):
device_id = models.AutoField(primary_key=True)
device_type = models.ForeignKey(Device_type)
user = models.ForeignKey(User, null=False)
name = models.CharField(max_length=100)
description = models.CharField(max_length=256, blank=True, null=True)
My View :
serializer_class = ShowLocationInfoSerializer
def get(self, request, *args, **kwargs):
# get the imei from the url
imei = self.kwargs['imei']
try:
single_tracklog_object = Tracklogs.objects.filter(device__imei = imei).values('lat', 'lon','speed','course','device','device__name').latest()
# Here its causing problem!!!
serializer = self.serializer_class(single_tracklog_object)
return Response(serializer.data)
except ObjectDoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
My Serializer:
class Tracking_DeviceSerializer(serializers.ModelSerializer):
name = serializers.CharField(read_only=True)
class Meta:
model = Tracking_devices
fields = ('name')
class ShowLocationInfoSerializer(serializers.ModelSerializer):
lat = serializers.CharField(read_only=True)
lon = serializers.CharField(read_only=True)
speed = serializers.IntegerField(read_only=True)
course = serializers.FloatField(read_only=True)
device = Tracking_DeviceSerializer()
class Meta:
model = Tracklogs
fields = ('lat', 'lon', 'tracker_datetime', 'speed', 'course', 'device' )
Thanks in advance.

Trying to make a django rest api that doesn't look like my models

I'm trying to make an API for an outside group where I define user access. I have a setup in django that makes it easy to administer, but I would like the output to be quite simplistic for the other team.
This is the output I'm looking for would be something like:
{
"user_list": {
"user": {
"username": "username1",
"top_accesses": ["top_access_1", "top_access_2", "top_access_5"],
"middle_accesses": ["middle_access_1", "middle_access_2", "middle_access_7"],
"lower_accesses": ["lower_access_1", "lower_access_2", "lower_access_22"],
},
"user": {
"username": "username2",
"top_accesses": ["top_access_1", "top_access_2", "top_access_8"],
"middle_accesses": ["middle_access_3", "middle_access_5", "middle_access_6"],
"lower_accesses": ["lower_access_21", "lower_access_33", "lower_access_36"],
}
}
}
However, I'm having trouble using django's built in ORM to come up with these sets from my models. I can think of how to do it in SQL, but this isn't a particularly clean method. I know there must be a better way to do it since using TabularInline shows exactly what I want to see in the admin page
Here are my models:
class TopAccess(models.Model):
name = models.CharField(max_length=100)
site_user_access = models.ManyToManyField(User, blank=True)
site_group_access = models.ManyToManyField(Group, blank=True)
class Meta:
verbose_name_plural = "Top Access"
def __str__(self):
return self.name
class MiddleAccess(models.Model):
name = models.CharField(max_length=100)
site_user_access = models.ManyToManyField(User, blank=True)
site_group_access = models.ManyToManyField(Group, blank=True)
class Meta:
verbose_name_plural = "Middle Access"
def __str__(self):
return self.name
class LowerAccess(models.Model):
name = models.CharField(max_length=100)
site_user_access = models.ManyToManyField(User, blank=True)
site_group_access = models.ManyToManyField(Group, blank=True)
class Meta:
verbose_name_plural = "Lower Access"
def __str__(self):
return self.name
Ideally I would be able to return a query object that plays well with the django-rest-framework in the end, since I like how nicely it returns the same data in whichever form is requested
Edit:
This is what I'm thinking is going to be close to the solution, but I know I'm using class inheritance incorrectly
class MaybeThisCouldWork(User):
t_user = TopAccess.objects.filter(site_user_access=User)
m_user = MiddleAccess.objects.filter(site_user_access=User)
l_user = LowerAccess.objects.filter(site_user_access=User)
user_groups = User.objects.filter(id=User)
for user_group in user_groups:
t_group = TopAccess.objects.filter(
site_group_access=user_groups
)
m_group = MiddleAccess.objects.filter(
site_group_access=user_groups
)
l_group = LowerAccess.objects.filter(
site_group_access=user_groups
)
t_user = t_user | t_group
m_user = m_user | m_group
l_user = l_user | l_group
You could use serializers, may be something like this,
class TopAccessSerializer(serializers.ModelSerializer):
class Meta:
model = TopAccess
fields = ['name']
class MiddleAccessSerializer(serializers.ModelSerializer):
class Meta:
model = MiddleAccess
fields = ['name']
class LowerAccessSerializer(serializers.ModelSerializer):
class Meta:
model = LowerAccess
fields = ['name']
class UserSerializer(serializers.ModelSerializer):
topaccess = TopAccessSerializer(source='topaccess_set', many=True)
middleaccess = MiddleAccessSerializer(source='middleaccess_set', many=True)
loweraccess = LowerAccessSerializer(source='loweraccess_set', many=True)
class Meta:
model = User
fields = ['username', 'topaccess', 'middleaccess', 'loweraccess']