How to make nested serializers represent data in given way? - python

models:
class Category(models.Model):
name = models.CharField(max_length=80)
created_at = models.DateTimeField(auto_now_add = True, null = True)
created_by = models.ForeignKey(User, default = None, blank=True, null=True, on_delete=models.DO_NOTHING, related_name="created_by")
updated_at = models.DateTimeField(auto_now = True, null = True)
updated_by = models.ForeignKey(User, default = None, blank=True, null=True, on_delete=models.DO_NOTHING, related_name="updated_by")
deleted_at = models.DateTimeField(null = True)
deleted_by = models.ForeignKey(User, default = None, blank=True, null=True, on_delete=models.DO_NOTHING, related_name='deleted_by')
class Meta:
verbose_name_plural = "Categories"
def __str__(self):
return self.name
class Product(models.Model):
category = models.ManyToManyField(Category, blank=False)
name = models.CharField(max_length=100)
description = models.TextField(max_length=250)
price = models.DecimalField(max_digits=6, decimal_places=2)
quantity = models.IntegerField(default=0)
created_at = models.DateTimeField(auto_now_add = True, null = True)
created_by = models.ForeignKey(User, default = None, blank=True, null=True, on_delete=models.DO_NOTHING, related_name="products_created_by")
updated_at = models.DateTimeField(auto_now = True, null = True)
updated_by = models.ForeignKey(User, default = None, blank=True, null=True, on_delete=models.DO_NOTHING, related_name="products_updated_by")
deleted_at = models.DateTimeField(null = True)
deleted_by = models.ForeignKey(User, default = None, blank=True, null=True, on_delete=models.DO_NOTHING, related_name='products_deleted_by')
class Meta:
ordering = ("name", )
def __str__(self):
return self.name
serializer:
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ("id", "name", "created_at", "created_by", "updated_at", "updated_by", "deleted_at", "deleted_by",)
class ProductSerializer(serializers.ModelSerializer):
# category = serializers.PrimaryKeyRelatedField(many=True, read_only=True, source="category")
category = CategorySerializer(many=True)
created_by = serializers.StringRelatedField()
updated_by = serializers.StringRelatedField()
deleted_by = serializers.StringRelatedField()
class Meta:
model = Product
fields = (
"id", "name", "category", "description", "price",
"quantity", "created_at", "created_by", "updated_at",
"updated_by", "deleted_at", "deleted_by",
)
response in postman:
"id": 6,
"name": "Watch",
"category": [
{
"id": 3,
"name": "Electronics",
"created_at": "2020-12-07T12:45:32.521172Z",
"created_by": 2,
"updated_at": "2020-12-07T12:45:32.521217Z",
"updated_by": null,
"deleted_at": null,
"deleted_by": null
},
{
"id": 4,
"name": "Accessories",
"created_at": "2020-12-07T12:46:00.992857Z",
"created_by": 2,
"updated_at": "2020-12-07T12:46:00.992910Z",
"updated_by": null,
"deleted_at": null,
"deleted_by": null
}
],
"description": "Accessory for Smart Phones",
"price": "99.99",
"quantity": 0,
"created_at": "2020-12-07T14:00:12.312151Z",
"created_by": "user1",
"updated_at": "2020-12-07T14:00:12.312222Z",
"updated_by": null,
"deleted_at": null,
"deleted_by": null
}
I want to achieve something like this:
{
"category_ids": [
1,
2
],
"category": [
{
"name": "Category1"
},
{
"name": "Category2"
}
],
"creator": {
"name": "Admin"
},
"updater": {
"name": "Admin2"
}
}
my head hurts. can't find any solution to this problem. probably I am missing something or I am unaware of something.
it's many to many field. I tried to use PrimaryKeyRelatedField and source, but nothing. my biggest problem is, I don't know how to list category_ids seperately

You Can Use SerializerMethodField to achieve this
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ("name",)
class UserSerializer(serializers.Serializer):
name = serializers.CharField()
class ProductSerializer(serializers.ModelSerializer):
category_ids = serializers.SerializerMethodField()
category = CategorySerializer(many=True)
creator = UserSerializer()
updator = UserSerializer()
class Meta:
model = Product
fields = ("category_ids","category", "creator", "updator")
def get_category_ids(self, instance):
return list(instance.category.all().values_list('id', flat=True))

Related

How to edit api and add changes in django

I'm sending api like:
[
{
"id": 1,
"name": "Burger",
"name_ru": "Burger",
"name_en": "Burger",
"img": null,
"parent_category": 1
},
{
"id": 3,
"name": "Pizza",
"name_ru": "Pizza",
"name_en": "Pizza",
"img": null,
"parent_category": 1
}
]
how can edit this api? I want to send parent_category with id and name, not just id.
My views.py:
class TestList(APIView):
def get(self, request, pk, format=None):
cc = Child_Category.objects.filter(parent_category_id = pk)
serializer = ChildCategorySerializer(cc, many=True)
return Response(serializer.data)
My models.py:
class Parent_Category(models.Model):
name = models.CharField(max_length=255)
name_ru = models.CharField(max_length=255)
name_en = models.CharField(max_length=255)
img = models.ImageField(blank=True, null=True)
def __str__(self):
return self.name_en
class Meta:
verbose_name_plural = 'Parent Categories'
class Child_Category(models.Model):
name = models.CharField(max_length=255)
name_ru = models.CharField(max_length=255)
name_en = models.CharField(max_length=255)
parent_category = models.ForeignKey(Parent_Category, on_delete=models.CASCADE)
img = models.ImageField(blank=True, null=True)
def __str__(self):
return self.name_en
class Meta:
verbose_name_plural = 'Child Categories'

Calculate sum of model objects in django serializers

In my app, each user has his wallets in which he can save his daily expenses.
How can I get sum of money for each instance of wallet with many expenses saved to it?
I've tried serializers.SerializerMethodField()
from django.db.models import Sum
from django.db.models import Q
class WalletInstanceSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.id')
entry = BudgetEntrySerializer(many=True, read_only=True)
expense_sum = serializers.SerializerMethodField()
class Meta:
model = WalletInstance
fields = '__all__'
def get_expense_sum(self, obj):
return WalletInstance.objects.filter(Q(id=obj.id)|Q(entry__entry_type='income')).aggregate(Sum('entry__amount'))['entry__amount__sum']
And this gives me data in the correct format but the sum is wrong, for example
[
{
"id": "d458196e-49f1-42db-8bc2-ee1dba438953",
"owner": 1,
"entry": [
{
"id": 3,
"owner": 1,
"title": "dsfdsf",
"amount": 7,
"description": "sdfdsf",
"entry_type": "income",
"date_added": "2022-08-13",
"entry_category": {
"id": 2,
"name": "Transport"
}
},
{
"id": 4,
"owner": 1,
"title": "fesfvsdfgvbtdg",
"amount": 12,
"description": "efesf",
"entry_type": "income",
"date_added": "2022-08-13",
"entry_category": {
"id": 2,
"name": "Transport"
}
}
],
"viewable": [
"admin#gmail.com"
],
"expense_sum": 83,
"name": "dsdsds",
"date_added": "2022-08-13"
}
]
Expense_sum is 83 but amount fields are 7 and 12 so that's equal to 19
How can i get the correct number?
Models
class BudgetEntry(models.Model):
STATE= [
('income','income'),
('expenses','expenses'),
]
owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='owner_of_entry', on_delete=models.CASCADE)
title = models.CharField(max_length=20)
amount = models.IntegerField()
description = models.CharField(max_length=60, null=True)
entry_type = models.CharField(max_length=15, choices=STATE, null=True)
entry_category = models.ForeignKey(Category, null=True, blank=True, related_name='category_of_entry', on_delete=models.SET_NULL)
date_added = models.DateField(auto_now_add=True)
class WalletInstance(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True)
name = models.CharField(max_length=30, null=True)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='owner', on_delete=models.CASCADE)
viewable = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='can_view', blank=True)
entry = models.ManyToManyField(BudgetEntry, related_name='BudgetEntry', blank=True)
date_added = models.DateField(auto_now_add=True)
I think ORM query for the BudgetEntry is wrong. It should be like the following:
def get_expense_sum(self, obj):
return obj.entry.filter(entry_type='income').aggregate(Sum('entry__amount'))['entry__amount__sum']
I've figured it out. Firstly I've created #property in my database to get sum of expenses, but this wasn't very useful If I ever wanted to count income so I passed the value arg to the function where the value can be expense/income
class WalletInstance(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True)
name = models.CharField(max_length=30, null=True)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='owner', on_delete=models.CASCADE)
viewable = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='can_view', blank=True)
entry = models.ManyToManyField(BudgetEntry, related_name='BudgetEntry', blank=True)
date_added = models.DateField(auto_now_add=True)
def total_amount(self, value):
queryset = self.entry.filter(entry_type=value).aggregate(
total_amount=models.Sum('amount'))
return queryset["total_amount"]
And now I'm calling this function in my serializer instead of counting there
class WalletInstanceSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.id')
entry = BudgetEntrySerializer(many=True, read_only=True)
viewable = serializers.SlugRelatedField(many=True, queryset=get_user_model().objects.all(), slug_field='email')
expense_sum = serializers.SerializerMethodField()
class Meta:
model = WalletInstance
fields = '__all__'
def get_expense_sum(self, obj):
wallet_obj = WalletInstance.objects.get(id=obj.id)
return wallet_obj.total_amount('expenses')

How to filter and get an "object" instead of a "quesryset" in Django Rest Framework

I am trying to filter the users by passing the email_id through URL by passing email_id and getting that particular user. I tried doing this using django-filters but I got a queryset with one object inside it but instead I need the object itself.
here's the model -
class User(models.Model):
"""This is the Users model where all users data will be saved"""
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
email_id = models.EmailField(max_length=100, default="")
city = models.CharField(max_length=20, blank=True)
state = models.CharField(max_length=50, blank=True)
country = models.CharField(max_length=20, blank=True)
zip_code = models.CharField(max_length=6, blank=True)
mobile_number = models.CharField(max_length=10, blank=True)
birthdate = models.DateField(blank=True, null=True)
photo_url = models.URLField(max_length=255, blank=True)
gender = models.ForeignKey(Gender, on_delete=models.CASCADE, null=True, blank=True,
related_name='user_gender')
joined = models.DateTimeField(auto_now_add=True, blank=True, null=True)
def __str__(self):
return self.first_name + ' ' + self.last_name
I tried using this viewset but it gives me a queryset instead of an object and I coudn't put my mind around on it.
class UserFilter(filters.FilterSet):
class Meta:
model = User
fields = {
'email_id': ['iexact']
}
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
filter_backends = [DjangoFilterBackend]
filterset_class = UserFilter
This is the url I am currently passing -
http://127.0.0.1:8000/api/v1/users/?email_id__iexact=somemail#gmail.com
This is the JSON I'm getting -
[
{
"id": 26,
"first_name": "Sarthak",
"last_name": "Rohatgi",
"email_id": "sarthak#gmail.com",
"city": "city",
"state": "state",
"country": "India",
"zip_code": "000000",
"mobile_number": "9876543210",
"birthdate": "2001-01-00",
"photo_url": "",
"gender": {
"gender": "Male"
}
}
]
This is the JSON that I want -
{
"id": 26,
"first_name": "Sarthak",
"last_name": "Rohatgi",
"email_id": "sarthak#gmail.com",
"city": "city",
"state": "state",
"country": "India",
"zip_code": "000000",
"mobile_number": "9876543210",
"birthdate": "2001-01-00",
"photo_url": "",
"gender": {
"gender": "Male"
}
}

django rest framework get all related fields and filter in a manytomany field nested

I'm new in Django I want to get all informations of my contacts for a store given in parameter and all extra from another model.
Here is my code :
Models.py
class Store(models.Model):
name = models.CharField(max_length=100)
​
​
class Contact(models.Model):
id = models.CharField(primary_key=True, max_length=200)
first_name = models.CharField(max_length=100, null=True, blank=True)
last_name = models.CharField(max_length=100, null=True, blank=True)
email = models.CharField(max_length=100, null=True, blank=True)
stores = models.ManyToManyField(Store, related_name='contacts')
​
​
class DataType(models.Model):
datatype = models.CharField(max_length=10)
class DataField(models.Model):
name = models.CharField(max_length=100)
label = models.CharField(max_length=100)
datatype = models.ForeignKey(
'DataType',
on_delete=models.SET_NULL,
null=True
)
class DataFieldValue(models.Model):
contact = models.ForeignKey(
Contact,
on_delete=models.SET_NULL,
related_name='datafieldvalues',
null=True
)
datafield = models.ForeignKey(
'DataField',
on_delete=models.SET_NULL,
null=True
)
value = models.CharField(
max_length=250,
null=True,
blank=True
)
Views.py
# Contact API views.
class ContactListAPIView(generics.ListAPIView):
queryset = Contact.objects.all()
serializer_class = ContactSerializer
filter_class = ContactFilter
Filters.py
class ContactFilter(django_filters.rest_framework.FilterSet):
store = django_filters.CharFilter()
class Meta:
model = Contact
fields = ['store']
Serializers.py
class ContactSerializer(serializers.ModelSerializer):
class Meta:
model = Contact
fields = '__all__'
(Additionnal) Here is the schema :
schema
this is what i want:
GET /contacts/?store=54
{
"count": 25,
"next": "<url>/contacts/?page=3",
"previous": "<url>/contacts/?page=2",
"data": [
{
"id": "a519d2cd-13c6-48f3-b391-9a7e0cee58cc",
"src_id": 356,
"first_name": "Benjamin",
"last_name": "Pavard",
"email": "benjamin.pavard#email.fr",
"datafields" : {
"age" : "eighteen"
},
"store": 54
},
....
]
}
but this is what i got :
{
"next": null,
"previous": null,
"count": 10,
"data": [
{
"id": "1234567",
"first_name": null,
"last_name": null,
"email": "cartouche#email.fr",
},
.....
}
Questions :
how can i get the "datafields" ?
"age" is a "datafield" object and "eighteen" is a datafieldvalue object.
How can i get only the store that i've previously gave in parameter?
Thanks a lot for your help whatever it was.
You need to add more fields in the ContactSerializer
class ContactSerializer(serializers.ModelSerializer):
store = serializers.SerializerMethodField()
datafields = serializers.SerializerMethodField()
class Meta:
model = Contact
fields = '__all__'
def get_datafields(self, obj):
result = {}
for data_field_value in obj.datafieldvalues.all().select_related('datafield'):
result[data_field_value.datafield.label] = data_field_value.value
return result
def get_store(self, obj):
request = self.context['request']
store = request.query_params.get('store', '')
return store

return a nested list of one model

I have a lookups table that contains course categories and subcategories separate:
{
"id": 138,
"lookup": "CRS_CTGRY",
"attr1": "Arts and Humanities",
"attr2": "الفنون والعلوم الإنسانية",
"attr3": null,
"attr4": null,
"attr5": null,
"editable": 1
},
{
"id": 155,
"lookup": "CRS_SB_CTGRY",
"attr1": "Photography",
"attr2": "النصوير",
"attr3": "138",
"attr4": null,
"attr5": null,
"editable": 1
},
The relation between them is that attr3 = id_of_the_category && attr1 = CRS_SB_CTGRY
I want to merge them together in one list like:
{"id":138,"
"lookup":"CRS_CTRGY",
"name":"Arts and Humanities",
"subcategories":{"id": 154,
"lookup": "CRS_SB_CTGRY",
"attr1": "Music",
"attr2": "الموسيقي",
"attr3": "138",
"attr4": null,
"attr5": null,
"editable": 1
}}
This is my models.py:
class Lookups(models.Model):
lookup = models.CharField(max_length=45)
attr1 = models.CharField(max_length=100)
attr2 = models.CharField(max_length=100, blank=True, null=True)
attr3 = models.CharField(max_length=100, blank=True, null=True)
attr4 = models.CharField(max_length=100, blank=True, null=True)
attr5 = models.CharField(max_length=100, blank=True, null=True)
editable = models.IntegerField(blank=True, null=True)
class Meta:
managed = True
db_table = 'lookups'
unique_together = (('lookup', 'attr1', 'attr2', 'attr3', 'attr4', 'attr5'),)
How can i do it? and where to put the code? in the serializers class?
You need to implement serializer for your subcategory:
class SubcategorySerializer(serializers.ModelSerializer):
class Meta:
model = Lookups
fields = ( 'id', 'lookup', 'attr1', 'attr2', 'attr3',)
And use it in your Category Serializer when you select all related subcategories:
class CategorySerialier(serializers.ModelSerializer):
subcategories = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Lookups
fields = ( 'id', 'lookup', 'subcagories')
def get_subcategories(self, obj):
subs = Lookups.objects.filter(attr3=obj.id)
return SubcategorySerializer(subs,many=True).data
If these are two models, and one serialize inside the other.
http://www.django-rest-framework.org/api-guide/relations/#nested-relationships

Categories

Resources