Django Rest Reverse Nested Relationships in Serializers - django

I am working on creating a GET only endpoint that shows the standings of a given season for a sports league. Honestly, at this point I have tried nearly everything with no luck. All other Serializers work fine so far and there is no issue with the way my database is setup. I have omitted many fields and tables due to the complexity, but anything relevant to the question is included below.
First, let me show you the rough format of how I want to JSON to be returned.
Sample Response (The end goal)
{
divisions: [
{
"divisionName": "WEEKNIGHT B",
"divisionId": "ee68d8ab-2752-4df6-b11d-d289573c66df",
teams: [
{
"teamId": "b07560bc-aac2-4c6c-bbfe-11a368137712",
"statsId": "53852698-9b78-4f36-9a2e-4751b21972f9",
"teamName": "FNA",
},
{
"teamId": "406eb5aa-6004-4220-b219-59476a3136d1",
"statsId": "a96ebf10-87c5-4f19-99c3-867253f4a502",
"teamName": "COPENHAGEN ROAD SODAS",
},
]
},
{
"divisionName": "WEEKNIGHT C",
"divisionId": "4e1469ae-2435-4a3d-a621-19a979ede7c1",
teams: [
{
"teamId": "ebc7e632-073e-4484-85f9-29c0997bec25",
"statsId": "cd6373a7-4f53-4286-80f2-eb3a8a49ee3a",
"teamName": "HAWKS",
"gamesPlayed": 29,
},
{
"teamId": "d8cda7a6-15f4-4e8f-8c65-ef14485957e4",
"statsId": "4492a128-763a-44ad-9ffa-abae2c39b425",
"teamName": "DUISLANDERS",
},
]
}
]
}
Through the URL I am passing in the Season ID, so everything is based off the season id. Below is an example of how I can replicate the type of response I want, by iterating through some queries which I know is rather messy, but I am using it just for example purposes.
Sample Query to show how it can be done using queries
standingsDict = {
"standings": []
}
divisionList = []
season = Season.objects.get(name='Fall 2021')
divisions = Division.objects.filter(seasondivision__season__id=season.id)
for div in divisions:
teamstats = TeamStats.objects.filter(
season_division_team__season_division__season_id=season.id,
season_division_team__season_division__division_id=div)
divDict = {
"divisionName": div.name,
"divisionId": div.id,
"teams": []
}
teamsList = []
for stats in teamstats:
teamDict = {
"teamId": stats.season_division_team.team.id,
"statsId": stats.id,
"seasonDivisionTeamId": stats.season_division_team_id,
"teamName": stats.season_division_team.team.name,
}
teamsList.append(teamDict)
divDict['teams'] = teamsList
divisionList.append(divDict)
standingsDict['standings'] = divisionList
Output Snippet of that loop
{
"standings":[
{
"divisionName":"WEEKNIGHT B",
"divisionId":"UUID(""ee68d8ab-2752-4df6-b11d-d289573c66df"")",
"teams":[
{
"teamId":"UUID(""756ea44f-885f-4ea0-ae4c-8a72c894067f"")",
"statsId":"UUID(""8ef1f683-ad5b-40d8-9277-0c2689cff771"")",
"seasonDivisionTeamId":"UUID(""19e29f9a-af04-4a4f-9e38-bad0cbf8817d"")",
"teamName":"GABAGOOLS"
},
{
"teamId":"UUID(""aefdc0dc-e2aa-4ce5-ad28-47f0724b57ab"")",
"statsId":"UUID(""05e0012c-0adc-4d15-bbc5-901e49489667"")",
"seasonDivisionTeamId":"UUID(""8adab3d1-7614-4ece-92f5-f0ab34b23c4c"")",
"teamName":"LONG BEACH THUNDER MAJOR"
},
Currently with the way everything is setup, I can traverse the FK Relationships to get them in a forward manner, for example, I can go from SeasonDivisionTeam, get the TeamStatsSerializer, and for each team, I can display the division. But that displays the same division multiple times for all the Team Stats, which is not what I am looking for.
Models
class BaseModel(LifecycleModelMixin, models.Model):
date_modified = models.DateTimeField(auto_now=True)
date_created = models.DateTimeField(auto_now_add=True)
id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False)
class Meta:
abstract = True
class Season(BaseModel):
name = models.CharField(max_length=50)
startDate = models.DateField(null=True, blank=True)
endDate = models.DateField(null=True, blank=True)
class Division(BaseModel):
name = models.CharField(max_length=50)
seasons = models.ManyToManyField(Season, through='SeasonDivision')
class SeasonDivision(LifecycleModelMixin, models.Model):
id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False)
division = models.ForeignKey(Division, on_delete=models.CASCADE)
season = models.ForeignKey(Season, on_delete=models.CASCADE)
class Team(BaseModel):
name = models.CharField(max_length=50)
season_divisions = models.ManyToManyField(SeasonDivision, through='SeasonDivisionTeam')
class SeasonDivisionTeam(LifecycleModelMixin, models.Model):
id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False)
team = models.ForeignKey(Team, on_delete=models.CASCADE)
season_division = models.ForeignKey(SeasonDivision, on_delete=models.CASCADE)
class Player(BaseModel):
season_division_teams = models.ManyToManyField(SeasonDivisionTeam, through='SeasonDivisionTeamPlayer')
firstName = models.CharField(max_length=80)
class SeasonDivisionTeamPlayer(LifecycleModelMixin, models.Model):
id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False)
player = models.ForeignKey(Player, on_delete=models.CASCADE)
season_division_team = models.ForeignKey(SeasonDivisionTeam, on_delete=models.CASCADE)
class PlayerStats(BaseModel):
season_division_team_player = models.ForeignKey(SeasonDivisionTeamPlayer, on_delete=models.CASCADE)
goals = models.IntegerField(default=0)
class TeamStats(BaseModel):
season_division_team = models.ForeignKey(SeasonDivisionTeam, on_delete=models.CASCADE)
regWins = models.IntegerField(default=0)
Serializers (Many of these have been changed multiple times, so what I have below is just the last thing I was testing that didn't work. )
class StandingsSerializer(serializers.ModelSerializer):
teamstats = TeamStatsSerializer(many=True, source='teamstats_set')
class Meta:
model = SeasonDivisionTeam
fields = [
"teamstats",
]
depth = 1
Views (Same as above, have done alot of testing with these, so what I have below is just the most recent test)
class StandingsList(generics.ListCreateAPIView):
queryset = Season.objects.order_by('-endDate').all()
serializer_class = StandingsSerializer
def get_queryset(self):
season_id = self.request.query_params.get('season_id')
queryset = SeasonDivisionTeam.objects.filter(season_division__season_id=season_id)
return queryset

Related

Writable nested serializer method getting an Error while posting a request

models.py
class Client(models.Model):
client_id = models.AutoField(unique=True, primary_key=True)
org = models.ForeignKey(Organisation, on_delete=models.CASCADE, related_name='org',null=True)
product = models.ManyToManyField(Product,related_name='product')
client_name = models.CharField(max_length=100)
client_code = models.CharField(max_length=20)
client_logo = models.ImageField(upload_to=upload_to,storage=DownloadableS3Boto3Storage, null=True, blank=True)
currency = MoneyField(max_digits=10, decimal_places=2, default_currency='INR', null=True)
billing_method = models.CharField(max_length=40)
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
email_id = models.EmailField(max_length=100)
contact_no = models.CharField(max_length=20)
mobile_no = models.CharField(max_length=20)
description = models.TextField(max_length=500)
street_address = models.CharField(max_length=250)
city = models.CharField(max_length=50)
state = models.CharField(max_length=50)
country = models.CharField(max_length=50)
pincode = models.CharField(max_length=10)
industry = models.CharField(max_length=100)
company_size = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_active = models.IntegerField(default=0, choices=STATUS_CHOICES)
class Meta:
db_table = "client_master"
def __str__(self):
return self.client_name
serializers.py
class Client_Serializers(serializers.ModelSerializer):
#product_name = Product_Serializers(many=True)
product = Product_Serializers(many=True)
class Meta:
model = Client
fields = ('client_id','currency','billing_method','first_name','last_name','description','street_address','city','state','country','pincode','industry','company_size','client_name', 'contact_no','mobile_no', 'email_id','client_logo','client_code','product',)
def create(self, validated_data):
products_data = validated_data.pop('product')
product = Product.objects.create(**validated_data)
for product_data in products_data:
Product.objects.create(product=product, **product_data)
return product
Data receiving on GET method
{
"client_id": 3,
"currency": "0.05",
"billing_method": "credit card",
"first_name": "career",
"last_name": "lab",
"description": "NA",
"street_address": "tiliconveli",
"city": "tirunelveli",
"state": "tamilnadu",
"country": "India",
"pincode": "600200",
"industry": "software",
"company_size": 100,
"client_name": "techfetch",
"contact_no": "1234567890",
"mobile_no": "1234567890",
"email_id": "icanio#gamil.com",
"client_logo": "https://icanio-project-management.s3.amazonaws.com/client_logo/sup_bat_fDauRxK.jpg",
"client_code": "TFH",
"product": [
{
"product_id": 5,
"product_name": "time"
}
]
}
But while posting it in the same format it is not getting posted, showing like
{
"status": "error",
"code": 400,
"data": {
"product": [
"This field is required."
]
},
"message": "success"
}
Views.py for reference
class Client_Viewset(DestroyWithPayloadMixin,viewsets.ModelViewSet):
renderer_classes = (CustomRenderer, )
queryset=models.Client.objects.all()
serializer_class=serializers.Client_Serializers
parser_classes = [MultiPartParser, FormParser]
filter_fields = (
'client_id',
'client_name',
'client_code',
'org_id',
)
How can I post the same data which I get in the GET request of Product field. Please help me resolve this as I was stuck in there for two days. I tried so many ways and end up not getting posted.
Product model
class Product(models.Model):
product_id = models.AutoField(unique=True, primary_key=True)
product_name = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = "product_master"
def __str__(self):
return self.product_name
product serializer
class Product_Serializers(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('product_id','product_name',)
From your code, the Client_Serializers is for Client model but the create method is not creating any Client object.
Client_Serializers should be something on these lines -
class Client_Serializers(serializers.ModelSerializer):
product = Product_Serializers(many=True)
class Meta:
model = Client
fields = ('client_id','currency','billing_method','first_name','last_name','description','street_address','city','state','country','pincode','industry','company_size','client_name', 'contact_no','mobile_no', 'email_id','client_logo','client_code','product',)
def create(self, validated_data):
products_data = validated_data.pop('product')
client = Client.objects.create(**validated_data) # Create a client object
for product_data in products_data:
Product.objects.create(client=client, **product_data)
return client

Get a list of all the field values from a Foreign Key Table in Django

am new to Django and currently trying the Foreign Key concept. I have three models as shown below.
class Basket(models.Model):
basket_name = models.CharField(max_length=5, unique=True)
def __str__(self):
return self.basket_name
class Product(models.Model):
Grams = 'GM'
Kilograms = 'KG'
WeightBased = 'WPG'
QuantityBased = 'CPG'
PRODUCT_UNIT_WT_CHOICES=[
(Grams, 'Grams'),
(Kilograms, 'Kilograms')
]
PRODUCT_TYPE_CHOICES =[
(WeightBased, 'Weight Based Product'),
(QuantityBased, 'Quantity Based Product')
]
product_name = models.CharField(max_length=30, unique=True)
product_description = models.TextField(max_length=300)
product_price = models.DecimalField(max_digits=5, decimal_places=2)
product_unit_weight = models.DecimalField(max_digits=5, decimal_places=2)
product_unit_weight_units = models.CharField(max_length=2, choices=PRODUCT_UNIT_WT_CHOICES, default=Grams)
product_type = models.CharField(max_length=3, choices=PRODUCT_TYPE_CHOICES, default=QuantityBased)
product_image = models.ImageField(upload_to=imageUploadPath, null=True, blank=True)
def __str__(self):
return self.product_name
class BasketProductMapping(models.Model):
basket_reference = models.ForeignKey(Basket, on_delete=models.CASCADE)
product_reference = models.ForeignKey(Product, on_delete=models.CASCADE)
mapped_basket_name = models.CharField(max_length=5,null=False, blank=False)
mapped_product_name = models.CharField(max_length=30, null=False, blank=False)
Here are my serializers:
class ProductSerializer(serializers.ModelSerializer):
product = serializers.CharField(read_only=True)
class Meta:
model = Product
fields = ['id', 'product_name', 'product_description', 'product_price', 'product_unit_weight',
'product_unit_weight_units', 'product_type', 'product_image']
class BasketSerializer(serializers.ModelSerializer):
basket = serializers.CharField(read_only=True)
class Meta:
model = Basket
fields = ('id', 'basket_name')
class BasketProductMappingSerializer(serializers.ModelSerializer):
basket_reference = serializers.CharField(source ='basket.basket_name', read_only=True)
product_reference = serializers.CharField(source='product.product_name', read_only=True)
class Meta:
model = BasketProductMapping
fields = ['id', 'basket_reference', 'product_reference', 'mapped_basket_name', 'mapped_product_name']
What I am trying to achieve is get a list of all the values in 'basket_name' and 'product_name' when I call the BasketProductMappingSerializer. But the output I am getting is this:
{
"status": 1,
"message": "Basket Product Mapping List",
"data": [
{
"id": 1,
"mapped_basket_name": "A1",
"mapped_product_name": "XYZ"
}
]
}
This is my views.py code:
class BasketProductViewSet(APIView):
def get(self, request):
if request.GET.get('id'):
print('Basket Product Mapping Details')
basketProductMappingData = BasketProductMapping.get(id = request.GET.get('id'))
serializer = BasketProductMappingSerializer(basketProductMappingData)
else:
basketProductMappingData = BasketProductMapping.objects.all().values('id', 'basket_reference__basket_name', 'product_reference__product_name', 'mapped_basket_name', 'mapped_product_name')
serializer = BasketProductMappingSerializer(basketProductMappingData, many=True)
response = {'status':1, 'message':"Basket Product Mapping List", 'data':serializer.data}
Where am I going wrong? Sorry if my question is very trivial. Thanks for your help in advance.
i see that there is an error in your views BasketProductMapping.get(id = request.GET.get('id')) should be BasketProductMapping.objects.get(id = request.GET.get('id'))
i don't know if it gonna work but can you try
basket_reference = serializers.ReadOnlyField(source ='basket.basket_name')
product_reference = serializers.ReadOnlyField(source='product.product_name')
instead of
basket_reference = serializers.CharField(source ='basket.basket_name', read_only=True)
product_reference = serializers.CharField(source='product.product_name', read_only=True)

How to get values from ForeignKey's related model in Django queryset?

I'm having trouble accessing a field in a related model using a foreign key.
I have two models, Person and Party as outlined below:
class Person(models.Model):
title = models.CharField(max_length = 4, blank=True, null=True)
first = models.CharField(max_length = 30, blank=True, null=True)
last = models.CharField(max_length = 30, blank=True, null=True)
party = models.ForeignKey(Party, related_name = 'parties', on_delete=models.CASCADE, blank=True, null=True)
def __str__(self):
return self.id
class Party(models.Model):
party_name = models.CharField(max_length = 50)
party_code = models.CharField(max_length = 5)
def __str__(self):
return self.party_name
I'm trying to get a count of people in each party. I'd like the result to look like:
[{
"party_name": "Democrat",
"total_members": 93
},
{
"party_name": "Republican",
"total_members": 32
},
{
"party_name": "Non-Partisan",
"total_members": 34
}]
My serializer is as follows:
class CountPartiesSerializer(serializers.ModelSerializer):
total_members = serializers.IntegerField()
class Meta:
model = Person
fields = ('id', 'party', 'total_members')
And my viewset is as follows:
class CountPartiesViewSet(DefaultsMixin, viewsets.ModelViewSet):
queryset = Person.objects.values('party__party_name').annotate(total_members = Count('id'))
serializer_class = CountPartiesSerializer
But this results in the following:
[{
"party": null,
"total_members": 34
},
{
"party": null,
"total_members": 93
},
{
"party": null,
"total_members": 32
}]
I've tried a million different things and I know I'm missing something, but I can't figure out how to get this working. Any advice would be much appreciated!
You're serializing the wrong thing. Your serializer should be based on Party, and you can then add the count of members.
Edit
class PartySerializer(serializers.ModelSerializer):
total_members = serializers.IntegerField()
class Meta:
model = Party
fields = ('name', 'total_members')
class PartyViewSet(DefaultsMixin, viewsets.ModelViewSet):
queryset = Party.objects.values('name').annotate(total_members=Count('parties'))
serializer_class = PartySerializer
You do of course have a relation between party and person: it is the reverse relationship of your foreign key. Note, the name you have chosen for the related_name there is confusing; it should be 'people', or just leave it as the default, which would be 'person_set'.

Django Rest framework- getting total number of rows

I would like to output a field that counts the number of Candidat in Candidat Model. I am currently using the following serializer:
class CountCSerializer(serializers.ModelSerializer):
user_count = serializers.SerializerMethodField()
class Meta:
model = Candidat
fields = ( 'user_count',)
def get_user_count(self, obj):
return Candidat.objects.count()
and the following api:
class CountCViewSet(ModelViewSet):
queryset = Candidat.objects.all()
serializer_class = CountCSerializer
urls.py:
router.register(r'CountC', CountCViewSet, base_name='users-count')
models.py:
class Candidat(models.Model):
name = models.CharField(max_length=50)
lastName = models.CharField(max_length=50)
email = models.CharField(max_length=50)
tel = models.CharField(max_length=50, default=0)
password = models.CharField(max_length=50)
civility = models.CharField(max_length=50)
birthDate = models.DateField(auto_now=False, auto_now_add=False)
gouvernorate = models.CharField(max_length=50)
def __str__(self):
return "Candidat: {}".format(self.name)
But im getting nothing!
Any help in the matter would be much appreciated.
I was looking for the same and noticed that ModelViewSet generates a count by default. You can see it by navigating to the endpoint in a browser or checking the response body in Postman.
Example:
{
"count": 10,
"next": null,
"previous": null,
"results": [
{
"id": 62,
"entity_name": "The company name",
"entity_website_url": "thecompany.com",
"entity_city": "Los Angeles",
"entity_state": "CA"
}
...
]
}

django rest-framework serializer reverse relation

I am programming django based web site using django rest-framework.
I want to use rest-framework to get model's data.
this is my model.py
class TimeTable(models.Model):
subject_name = models.CharField(max_length=50)
subject_code = models.CharField(max_length=10, unique=True)
classification = models.CharField(max_length=50)
professor = models.CharField(max_length=50)
department = models.CharField(max_length=50)
credit = models.CharField(max_length=1)
year = models.CharField(max_length=4, default='2018')
semester = models.CharField(max_length=1, default='1')
def __str__(self):
return self.subject_code + '-' + self.subject_name
class Class(models.Model):
owner = models.ForeignKey(Profile, null=True)
timetable = models.ForeignKey(TimeTable, null=True)
grade = models.FloatField()
this is serializer.py
class TimeTableSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = TimeTable
fields = ('url', 'subject_name', 'subject_code', 'classification', 'professor', 'department', 'credit', 'year', 'semester')
class ClassSerializer(serializers.HyperlinkedModelSerializer):
timetables = TimeTableSerializer(read_only=True)
class Meta:
model = Class
fields = ('url','owner', 'timetable', 'grade', 'timetables')
I want to get JSON response Like this
(http://localhost:8000/api/classes/)
[
{
"url": "http://localhost:8000/api/classes/8/",
"owner": "http://localhost:8000/api/profiles/19/",
"timetable": "http://localhost:8000/api/timetables/3/",
"grade": 4.5
"timetables": {
"url": "http://localhost:8000/api/timetables/3/",
"subject_name": "Artificial Inteligence",
"subject_code": "3413513413",
"classification": "major",
"professor": "John Lee",
"department": "software",
"credit": "3",
"year": "2018",
"semester": "1"
}
}
]
but i got this
[
{
"url": "http://localhost:8000/api/classes/8/",
"owner": "http://localhost:8000/api/profiles/19/",
"timetable": "http://localhost:8000/api/timetables/3/",
"grade": 4.5
}
]
How Can I get TimeTable's JSON data in Class JSON??
class ClassSerializer(serializers.HyperlinkedModelSerializer):
timetable = TimeTableSerializer(read_only=True)
class Meta:
model = Class
fields = ('url','owner', 'timetable', 'grade')