How to build a Serializer for a Normalized DataBase - django

Maybe my question has already been answered, but I could not find anything.
Lets say that Im building a stock inventory system using Django + Django REST for the backend. To supply this I have this two models:
class Shoe(models.Model):
_id = models.AutoField(primary_key=True)
description = models.CharField(null=False, blank = False)
provider = models.CharField(null=False, blank=False)
category = models.CharField(choices=CATEGORIES, null=False, blank=False)
class Sizes(models.Model):
shoe_id = models.ForeignKey(Shoe, on_delete=models.CASCADE)
size = models.IntegerField(choices=SIZE_LIST, null=False, blank=False)
amount = models.IntegerField(null=False,default=0)
My doubt is, how can I (using ModelViewSet, cause based on my experience with DRF is the easiest way) serve JSON files like this:
[
{
"_id": "1",
"description": "Air Max Black",
"provider": "NIKE",
"category": "Casual",
"available_sizes": {36: 400, 37: 250}, #size: amount
"amount": "650" #total amount
},
]
Based on what I understand of DRF to "join" the two models in a JSON I should write a custom serializer right?
Usually my serializers are like
class FooSerializer(serializers.ModelSerializer):
class Meta:
model = Foo
fields = ['some_stuff_here']
Please help me or recomend me reading material for do that, I have read the DRF docs about Serializers but cant understand how to do stuff like this.

You can just use a nested relation:
class ShoeSerializer(serializers.ModelSerializer):
class Meta:
model = Shoe
fields = '__all__' # all for all fields, or a tuple with the fields
class SizeSerializer(serializers.ModelSerializer):
shoe = ShoeSerializer(many=True, read_only=True)
class Meta:
model = Size
fields = '__all__'
But I believe your model is inverted, maybe size should be a fk on shoe model if you want to bring the size of an shoe instance? If that is the case, just invert the serializers.

Related

How to Merge Two Model Serialisers in Django Rest Framework

I want to merge the results of two models. I have two models as below.
First is Product and the other is ProductChannelListing
class Product(models.Model):
name = models.CharField()
category = models.ForeignKey()
# has many other fields which I want in the response
class ProductChannelListing(models.Model):
product = models.ForeignKey()
channel = models.ForeignKey()
is_visible = models.BooleanField()
price = models.IntegerField()
I want the result in the following way.
{
"name": "ABC",
"category": {},
"is_visible": true,
"price": 200,
**
}
I want make the query efficient query such that it should not make so many queries.
from .models import Product, ProductChannelListing
class ProductSerializer(model.serializer):
class Meta:
model: Product
fields = '__all__'
class ProductChannelListingSerializer(model.serializer):
product = ProductSerializer(ready_only=True)
class Meta:
model: ProductChannelListing
fields = '__all__'
Do the same for channel also, and u will get all fields visible at one viewpoint with serializer_class as ProductChannelListingSerializer.

Django-Rest-Framework: Serializing Many to Many Model with a through in Post Request

I am having really big trouble while trying to implement handling of POST request within DRF and Serializers. The error I have is following;
When I try to debug the issue, sporadically, request is handled successfully. I am really confused.
AttributeError: Got AttributeError when attempting to get a value for
field equipment_types on serializer FreightCreateSerializer. The
serializer field might be named incorrectly and not match any
attribute or key on the Freight instance. Original exception text
was: 'Freight' object has no attribute 'FreightEquipmentType_set'.
My Models are;
class EquipmentType(model.Model):
name = models.CharField(max_length=100)
description = models.CharField(max_length=250)
class Freight(model.Model):
name = models.CharField(max_length=250)
equipment_types = models.ManyToManyField(EquipmentType, through='FreightEquipmentType')
class FreightEquipmentType(model.Model):
equipment_type = models.ForeignKey(
EquipmentType,
on_delete=models.CASCADE
)
freight = models.ForeignKey(
Freight,
on_delete=models.CASCADE
)
quantity = models.DecimalField(max_digits=18, decimal_places=2, default= 0)
My Serializers are,
class EquipmentTypeSerializer(serializers.ModelSerializer):
class Meta:
model = EquipmentType
fields = "__all__"
class FreightEquipmentTypeSerializer(serializers.ModelSerializer):
class Meta:
model = FreightEquipmentType
fields = ('equipment_type', 'quantity', )
class FreightCreateSerializer(serializers.ModelSerializer):
equipment_types = FreightEquipmentTypeSerializer(many=True, source='FreightEquipmentType_set', read_only=False)
class Meta:
model = Freight
fields = "__all__"
def create(self, validated_data):
equipment_types_validated_data = validated_data.pop('FreightEquipmentType_set')
freight = Freight.objects.create(**validated_data)
equipment_type_serializer = self.fields['equipment_types']
for each in equipment_types_validated_data:
each['freight'] = freight
equipment_types = equipment_type_serializer.create(equipment_types_validated_data)
return freight
I would like to handle following post request.
{
"name": "0",
"equipment_types": [{
"equipment_type": "8a9c1f57-c6c9-4bfa-84a8-2082d6c3d112",
"quantity": 2
}, {
"equipment_type": "7242a8fb-3eba-4988-98fa-806c21562101",
"quantity": 3
}]
}
Any help is highly appreciated.
After hours of research and try-outs. I finally found a blog post.
https://adnankayace.blogspot.com/2019/03/django-rest-framework-many-to-many_21.html
It solved my problem. Hope it helps others as well.

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

Serialization of related pivot models with Django Rest Framework

I am learning Django, and are using Django Rest Framework. In my application, I have three different models
Bar (holds information about a bar, has multiple beers through the BarBeer model)
Beer (holds information about a beer)
BarBeer (connection between a bar and a beer, has pivot fields such as alcohol, type, price and volume)
This is how the different models are defined:
class Bar(models.Model):
name = models.CharField(max_length=60)
location = models.PointField()
address = models.CharField(max_length=60)
description = models.TextField(default='')
beers = models.ManyToManyField('api.Beer', through='api.BarBeer')
class Beer(models.Model):
name = models.CharField(max_length=60)
alcohol = models.FloatField(default=0)
class BarBeer(models.Model):
bar = models.ForeignKey(Bar, on_delete=models.CASCADE)
beer = models.ForeignKey(Beer, on_delete=models.CASCADE)
price = models.FloatField(default=0)
type = EnumField(Type, default=Type.Cask)
volume = models.IntegerField(default=0)
Now I want to serialize a given bar with all the beers for that particular bar including the extra fields in the pivot model BarBeer. For example, below is what I'd like the output to be (note the extra three fields on the beer, that comes from the BarBeer model):
{
"id": 1,
"name": "A bar",
"beers": [
{
"id": 1,
"name": "Ship Full of IPA",
"alcohol": 6.5,
"type": "bottle",
"price": "35",
"volume": "33"
}
]
}
I can't figure out how to get the extra fields from the pivot model as part of the serialized output. This is what my serializer looks like right now:
class BarDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = Bar
fields = ('id', 'name', 'beers')
depth = 3
Firstly beers = models.ManyToManyField('api.Beer', through='api.BarBeer') this field is unneccessary, because you have already created a table named BarBeer. ManyToManyField means adding exra table. So, if we assume this field is not exist and you have BarBeer table, you can do this with using BarBeerSerializer like that:
serializers.py
class BarBeerSerializer(serializers.ModelSerializer):
name = serializers.SerializerMethodField()
alchol = serializers.SerializerMethodField()
class Meta:
model = BarBeer
fields = ['id','name','alchol','type','price','volume']
def get_name(self,obj):
return obj.beer.name
def get_alchol(self,obj):
return obj.beer.alchol
class BarSerializer(serializers.ModelSerializer):
beers = serializers.SerializerMethodField()
class Meta:
model = Bar
fields = ['id', 'name', 'beers']
def get_beers(self,obj:Bar):
beers = obj.barbeer_set.all()
return BarBeerSerializer(beers,many=True).data
If there is an error, please ask it on comment.
One way to do this is you can create a custom serializer for the BarBeer model and pass the other two models serializers in it something like :~
class BarDetailsSerializer(serializers.Serializer):
bar = BarSerializer()
beer = BeerSerializer()
price = serializers.FloatField(required=True)
type = serializers.CharField(required=True)
volume = serializers.IntegerField(required=True)
just pass your BarBeer object to this serializer and it would return all the data points, even for the objects connected through Foreign Key.
Another way to achieve this as the exact same response that you mentioned in the question would be to create a renderer for you api and format and structure the data as you want in it accordingly.

Django REST displayed filtered nested resource

I'm attempting to return JSON in a simple GET that includes a row from a joined table. The models:
class School(models.Model):
id = models.CharField(max_length=18,primary_key=True)
name = models.CharField(max_length=255,blank=False)
class Meta:
db_table='school'
class FKTable(models.Model):
school = models.ForeignKey(School,blank=False, db_column='school_id', on_delete=models.CASCADE)
name = models.CharField(max_length=45, blank=False)
value = models.CharField(max_length=255, blank=True, null=True)
class Meta:
db_table='fk_table'
And the serializer:
class SchoolListSerializer(serializers.ModelSerializer):
id = serializers.CharField(source='id')
name = serializers.CharField(source='name')
fkColumn = ????
class Meta:
model = models.School
fields = ('id','name','fkColumn')
The problem is I want to also filter the nested resource that I join against on the 'name' column so I don't know what to put for 'fkColumn' in the serializer. In SQL it would look something like this:
SELECT school.*, fk_table.value from school INNER JOIN fk_table on
fk_table.school_id = school.id AND fk_table.name = 'some name'
The end result JSON I want is:
{
"schools": [
{
"id": "someId",
"name": "someName",
"fkColumn": "Value"
}]}
RelatedField does not seem to work and none of the documentation seems to give a solid answer on how to do this. Any ideas?
Revised after clarification. Define an fkeySerializer:
class fkeySerializer(serializers.ModelSerializer):
...
Then link this in your main serializer:
fkey = fkeySerializer(many=True)
class Meta:
model = models.School
fields = ('id','name','fkColumn',)
You can use a MethodField serializer here. For example:
fkColumn = serializers.SerializerMethodField()
def get_fkColumn(self, obj):
return obj.fkColumn
class Meta:
model = models.School
fields = ('id','name','fkColumn',)
As an aside, perhaps there's a good reason for it, but it might be easier to rename this table to be more easily readable