Django Rest Framework serialize additional fields from intermediate model - django

I am developing a small application that lists the customers of a store. I'm trying to retrieve the additional fields of the intermediate model because a contact can belong to several stores but depending on the store it is premium or not and if he is happy or not.
Here's the JSON response I'd like to get for a Store like /contacts/?store=my_store
[
{
"id": "UX",
"first_name": "UX",
"last_name": "UX",
"email": null,
"mobile": null,
"happy": True,
"premium": True
},
{
"id": "AX",
"first_name": "AX",
"last_name": "AX",
"email": null,
"mobile": null,
"happy": False,
"premium": True
}
]
here are my models:
class Store(BaseModel):
id = models.CharField(primary_key=True, max_length=200)
name = models.CharField(max_length=100)
class Contact(BaseModel):
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)
mobile = models.CharField(max_length=100, null=True, blank=True)
stores = models.ManyToManyField(
Store, through="MemberShip", through_fields=("contact", "store")
)
class MemberShip(BaseModel):
contact = models.ForeignKey(
Contact, on_delete=models.CASCADE, related_name="contact_to_store"
)
store = models.ForeignKey(
Store, on_delete=models.CASCADE, related_name="store_to_contact"
)
happy = models.BooleanField(default=True)
premium = models.BooleanField(default=False)
and my serializers:
class MemberShipSerializer(serializers.ModelSerializer):
class Meta:
model = MemberShip
fields = ("contact", "store", "happy", "premium")
class StoreSerializer(serializers.ModelSerializer):
class Meta:
model = Store
fields = ("id", "name")
class ContactSerializer(serializers.ModelSerializer):
infos = MemberShipSerializer(
source="contact_to_store" many=True, read_only=True
)
class Meta:
model = Contact
fields = (
"id", "first_name", "last_name", "email", "mobile", "infos"
)
As you can see, I first tried to gather all the information of the intermediate model in a field before displaying happy and premium but, strangely enough, the infos field is returned with an empty array value.
Python v 3.7
Django v 2.1
DRF v 3.9

You must provide data to MemberShipSerializer . You can use SerializerMethodField. Like that:
class ContactSerializer(serializers.ModelSerializer):
infos = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Contact
fields = (
"id", "first_name", "last_name", "email", "mobile", "infos"
)
def get_infos(self,obj:Contact):
return MemberShipSerializer(obj.contact_to_store.all(),many=True).data

I tried kamilyrb's solution here's what I changed:
class MemberShipSerializer(serializers.ModelSerializer):
class Meta:
model = MemberShip
fields = ("contact", "store", "happy", "premium")
class StoreSerializer(serializers.ModelSerializer):
class Meta:
model = Store
fields = ("id", "name")
class ContactSerializer(serializers.ModelSerializer):
happy = serializers.SerializerMethodField(read_only=True)
premium = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Contact
fields = (
"id", "first_name", "last_name", "email", "mobile", "happy", "premium"
)
def get_premium(self, obj):
return MemberShipSerializer(obj.contact_to_store.all(), many=True).data
def get_happy(self, obj):
return MemberShipSerializer(obj.contact_to_store.all(), many=True).data
This is what i have now:
[
{
"id": "UX",
"first_name": "UX",
"last_name": "UX",
"email": "0",
"mobile": null,
"happy": [
{
"store": "my-store",
"contact": "UX",
"happy": true,
"premium": false,
},
{
"store": "my-store2",
"contact": "UX",
"happy": false,
"premium": false,
}
],
"premium": [
{
"store": "my-store",
"contact": "UX",
"optin_sms": true,
"happy": false,
"premium": false
}
]
}
]
how can i get that ?
[
{
"id": "UX",
"first_name": "UX",
"last_name": "UX",
"email": null,
"mobile": null,
"happy": True,
"premium": True
},
{
"id": "AX",
"first_name": "AX",
"last_name": "AX",
"email": null,
"mobile": null,
"happy": False,
"premium": True
}
]
I also noticed that all the stores to which a contact is affiliated are shown but as I said before the url is called as follows contact/?store=my-store

Related

I want to build a nested serializer to display order with product and each product showing its individual value. I have my code below

I am trying to get a nested serializer with individual value displayed with particular product but i am getting all the values in a single product.
i dont kow what to do there is some mistake in my code i cant figure out where.
please help me out.
this is my code
models.py
class Order(models.Model):
user = models.ForeignKey(Account, related_name='orders',
on_delete=models.CASCADE)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
email = models.CharField(max_length=100)
address = models.CharField(max_length=100)
zipcode = models.CharField(max_length=100)
place = models.CharField(max_length=100)
phone = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
paid_amount = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
class Meta:
ordering = ['-created_at',]
def __str__(self):
return self.first_name
def get_total_price(self):
total = sum(item.get_cost() for item in self.items.all())
class OrderItem(models.Model):
order = models.ForeignKey(Order, related_name='items', on_delete=models.CASCADE, null=True,
blank=True)
glasses = models.ForeignKey(Glasses, related_name='glass', on_delete=models.CASCADE)
price = models.DecimalField(max_digits=8, decimal_places=2)
quantity = models.IntegerField(default=1)
ordered = models.BooleanField(default=False)
def get_cost(self):
return self.price * self.quantity
class Power(models.Model):
frame = models.ForeignKey(OrderItem, related_name='power', on_delete=models.CASCADE)
type = models.CharField(max_length=20, choices=[('Powered', 'Powered'), ('bifocal', 'bifocal'),
('Frame_only', 'Frame_only')])
left_eye_power = models.DecimalField(max_digits=3, decimal_places=2, null=True, blank=True)
left_eye_cylinder = models.DecimalField(max_digits=3, decimal_places=2, null=True, blank=True)
left_eye_bifocal = models.DecimalField(max_digits=3, decimal_places=2, null=True, blank=True)
left_eye_axis = models.DecimalField(max_digits=3, decimal_places=2, null=True, blank=True)
right_eye_power = models.DecimalField(max_digits=3, decimal_places=2, null=True, blank=True)
right_eye_cylinder = models.DecimalField(max_digits=3, decimal_places=2, null=True, blank=True)
right_eye_bifocal = models.DecimalField(max_digits=3, decimal_places=2, null=True, blank=True)
right_eye_axis = models.DecimalField(max_digits=3, decimal_places=2, null=True, blank=True)
serializer.py
class PowerSerializer(serializers.ModelSerializer):
class Meta:
model = Power
fields = '__all__'
class OrderItemSerializer(serializers.ModelSerializer):
#power = serializers.RelatedField(read_only=True)
power = PowerSerializer(many=True)
class Meta:
model = OrderItem
fields = (
"price",
"glasses",
"power",
"quantity",
)
class OrderSerializer(serializers.ModelSerializer):
items = OrderItemSerializer(many=True)
class Meta:
model = Order
fields = (
"id",
"first_name",
"last_name",
"email",
"address",
"zipcode",
"place",
"phone",
"items",
)
views.py
class OrderListView(ListCreateAPIView):
queryset = Order.objects.all()
serializer_class = OrderSerializer
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.AllowAny,)
output i got:
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"first_name": "priyank",
"last_name": "nair",
"email": "priyankshashinair#gmail.com",
"address": "1/A 401",
"zipcode": "400102",
"place": "Mumbai",
"phone": "9833437879",
"items": [
{
"price": "234.00",
"glasses": 1,
"power": [
{
"id": 1,
"type": "Powered",
"left_eye_power": "0.16",
"left_eye_cylinder": "0.14",
"left_eye_bifocal": "0.16",
"left_eye_axis": "0.13",
"right_eye_power": "0.14",
"right_eye_cylinder": "0.15",
"right_eye_bifocal": "0.14",
"right_eye_axis": "0.19",
"frame": 1
},
{
"id": 2,
"type": "Frame_only",
"left_eye_power": null,
"left_eye_cylinder": null,
"left_eye_bifocal": null,
"left_eye_axis": null,
"right_eye_power": null,
"right_eye_cylinder": null,
"right_eye_bifocal": null,
"right_eye_axis": null,
"frame": 1
}
],
"quantity": 1
},
{
"price": "234.00",
"glasses": 2,
"power": [],
"quantity": 2
}
]
}
]
}
Output i want:
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"first_name": "priyank",
"last_name": "nair",
"email": "priyankshashinair#gmail.com",
"address": "1/A 401",
"zipcode": "400102",
"place": "Mumbai",
"phone": "9833437879",
"items": [
{
"price": "234.00",
"glasses": 1,
"power": [
{
"id": 1,
"type": "Powered",
"left_eye_power": "0.16",
"left_eye_cylinder": "0.14",
"left_eye_bifocal": "0.16",
"left_eye_axis": "0.13",
"right_eye_power": "0.14",
"right_eye_cylinder": "0.15",
"right_eye_bifocal": "0.14",
"right_eye_axis": "0.19",
"frame": 1
}
],
"quantity": 1
},
{
"price": "234.00",
"glasses": 2,
"power": [
{
"id": 2,
"type": "Frame_only",
"left_eye_power": null,
"left_eye_cylinder": null,
"left_eye_bifocal": null,
"left_eye_axis": null,
"right_eye_power": null,
"right_eye_cylinder": null,
"right_eye_bifocal": null,
"right_eye_axis": null,
"frame": 1
},
],
"quantity": 2
}
]
}
]
}
Everything is correct, the problem is in your data - both Power objects clearly show "frame": 1, and your desired output is not possible without the second power having "frame": 2.

Django serializers filter foreignkey

view:
class MPNView(viewsets.ModelViewSet):
queryset = MPN.objects.all()
serializer_class = MPNSerializer
serializers:
class ProductsSerializer(serializers.ModelSerializer):
class Meta:
model = Products
fields = "__all__"
class MPNSerializer(serializers.ModelSerializer):
products = ProductsSerializer(many=True)
class Meta:
model = MPN
fields = "__all__"
model:
class MPN(Model):
number = models.CharField(max_length=50)
created_at = models.DateTimeField(auto_now_add=True)
class Product(Model):
mpn = models.ForeignKey(to=MPN, on_delete=models.CASCADE, related_name="products", null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
Results i am getting:
[
{
"id": 1221,
"products": [],
"number": "B07BMTYSMR",
"created_at": "2020-09-29T03:05:01.560801Z"
},
{
"id": 1222,
"products": [
{
"id": 2352,
"created_at": "2020-09-30T12:49:09.347655Z",
},
{
"id": 2352,
"created_at": "2020-09-30T12:49:09.347655Z",
}
]
}
]
Results i am expecting:
[
{
"id": 1222,
"products": [
{
"id": 2352,
"created_at": "2020-09-30T12:49:09.347655Z",
},
{
"id": 2352,
"created_at": "2020-09-30T12:49:09.347655Z",
}
]
}
]
Here is my code . I have shared view, model and serializers.
Here I am trying to get result with ForeignKey related fields.
But, I want to add one filter so that it ignores data where products is [ ] (empty array)
Please have a look how can I achieve that.
Try filtering the queryset:
queryset = MPN.objects.all().exclude(products__isnull=True)
Here you would use the "products" and check if it is empty. Empty results would be excluded.

Django Search in many to many field

I am trying to create a search in my Django Rest Framework application I have a table of follower and following via one model called: UserFollowing and the code is:
class UserFollowing(TimeStampedModel):
user_id = models.ForeignKey(
User, related_name="following", on_delete=models.CASCADE, blank=True)
following_user_id = models.ForeignKey(
User, related_name="followers", on_delete=models.CASCADE, blank=True)
#property
def username(self):
profile_instance = Profile.objects.get(user=self.user_id)
username = profile_instance.profile_username
return username
#property
def name(self):
profile_instance = Profile.objects.get(user=self.user_id)
name = profile_instance.name
return name
class Meta:
unique_together = ("user_id", "following_user_id")
ordering = ["-user_id"]
def __str__(self):
return str(self.id)
Here everything works fine but in my following list API, I am appending username and name of the user. When I am getting a list of follower using:
paginator = PageNumberPagination()
query_set = UserFollowing.objects.filter(user_id=request.user.id)
context = paginator.paginate_queryset(query_set, request)
serializer = UserFollowingSerializer(context, many=True)
Now I want to search via the result my serializer.data look like:
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"id": 233,
"username": "Username1",
"user_profile_image": "url",
"name": "CDE",
"is_active": true,
"created_at": "2020-07-25T06:00:31.786154Z",
"updated_at": "2020-07-25T06:00:31.786185Z",
"user_id": 88,
"following_user_id": 53,
"is_following": true
},
{
"id": 49,
"username": "username",
"user_profile_image": "URL",
"name": "ABC",
"is_active": true,
"created_at": "2020-07-15T08:32:35.352484Z",
"updated_at": "2020-07-15T08:32:35.352517Z",
"user_id": 88,
"following_user_id": 144,
"is_following": true
}
]
}
So based on my output how can I search a result where username = What can be the best way to search in this case.

Inserting foreign key in django rest framework

Here is the code that i am using for serializer
class CitySerializer(serializers.ModelSerializer):
addedBy = serializers.ReadOnlyField(source='addedBy.username')
country = CountrySerializer()
class Meta:
model = City
fields = ('id','name','slug','country','addedBy','createdAt')
class ActivitySerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
tag = ActivityTagSerializer()
location = CitySerializer()
class Meta:
model = Activity
fields = ('id','title','slug','description','location','tag','owner','createdAt')
i am able to insert data if i remove tag and location serializer by adding there pk in form but not with them being serialized. Here is the views that are being used
class ActivityList(generics.ListCreateAPIView):
queryset = Activity.objects.all()
serializer_class = ActivitySerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
class ActivityDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Activity.objects.all()
serializer_class = ActivitySerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
The owner field is read-only so thats why had to create a separate insert for the same. Now here are the outputs from the two case , with and without reference to other serializer (tag and location) in ActivitySerializer
{
"id": 3,
"title": "sdfsdf",
"slug": "sdfsdf",
"description": "sdfsdfsdf",
"location": {
"id": 2,
"name": "goa",
"slug": "goa",
"country": {
"id": 1,
"name": "india",
"slug": "india",
"addedBy": "georoot",
"createdAt": "2016-06-17T05:56:30.099121Z"
},
"addedBy": "georoot",
"createdAt": "2016-06-17T06:01:12.771079Z"
},
"tag": {
"id": 3,
"title": "temp",
"slug": "temp",
"addedBy": "georoot",
"createdAt": "2016-06-17T06:03:44.647455Z"
},
"owner": "georoot",
"createdAt": "2016-06-17T06:24:50.006344Z"
}
This is with reference to serializer which is the output view that i want, but insert not working in this case
and this is without reference to the serializer
{
"id": 3,
"title": "sdfsdf",
"slug": "sdfsdf",
"description": "sdfsdfsdf",
"location": 2,
"tag": 3,
"owner": "georoot",
"createdAt": "2016-06-17T06:24:50.006344Z"
}
Here i can change the values of tag and location by passing in the pk.
How can i do the same while referencing to other serializer as in the previous output
EDIT
structure for models
class City(models.Model):
name = models.CharField(max_length=200)
slug = AutoSlugField(populate_from='name')
country = models.ForeignKey(Country)
addedBy = models.ForeignKey('auth.user')
createdAt = models.DateTimeField(auto_now_add=True, blank=True)
class Activity(models.Model):
owner = models.ForeignKey('auth.user')
title = models.CharField(max_length=200)
slug = AutoSlugField(populate_from='title')
description = models.TextField()
location = models.ForeignKey(City)
tag = models.ForeignKey(ActivityTag)
createdAt = models.DateTimeField(auto_now_add=True, blank=True)

Distinct field Rest Framework Django

I need to make a distinct with a field of my model and not how to make
My model is:
class CheckList(CoreModel):
date = models.DateTimeField(default=datetime.now, blank=True, null=True, verbose_name=_('Date'))
establishment = models.ForeignKey(Establishment, related_name="checklists", on_delete=models.CASCADE, null=True, verbose_name=_('Establishment'))
user = models.ForeignKey(ITManager, related_name="checklists", on_delete=models.CASCADE, null=True, verbose_name=_('User'))
class Meta:
verbose_name_plural = _("Checklist")
verbose_name = _("Checklists")
def __str__(self):
return str(self.date)
My serializer and view:
class CheckListSerializer(BulkSerializerMixin, serializers.ModelSerializer):
user = ITManagerSerializer()
class Meta:
model = CheckList
list_serializer_class = BulkListSerializer
fields = ['id', 'user', 'establishment', 'date']
class ChecklistBulkViewSet(BulkModelViewSet):
queryset = CheckList.objects.values('establishment', 'user', 'date').distinct()
model = CheckList
serializer_class = CheckListSerializer
filter_class = ChecklistFilter
The api return me:
"results": [
{
"id": 1,
"user": {
"id": 3,
"first_name": "Andres",
"last_name": "Gallardo",
"rut": "21312",
"email": null,
"user_name": "andres",
"password": null,
"user": 4,
"country": [],
"active": true
},
"establishment": 3,
"date": "2016-06-14T15:15:00Z"
},
{
"id": 2,
"user": {
"id": 2,
"first_name": "Ramiro",
"last_name": "Gutierrez",
"rut": "15616+",
"email": null,
"user_name": null,
"password": null,
"user": 2,
"country": [
{
"id": 1,
"name": "Argentina",
"code_area": null
}
],
"active": false
},
"establishment": 3,
"date": "2016-06-09T15:40:04Z"
}]
I need you just leave me an establishment with the same id
any suggestions??
Thanks !