Suppose I have some django models:
class Restaurant(Model):
name = CharField(max_length=200)
class Food(Model):
restaurant = ForeignKey(Restaurant, on_delete=CASCADE)
name = CharField(max_length=200)
One Restaurant can have multiple foods.
And I want my json to look like this:
[
{
"name": "xxx",
"food": [{"name": "xxx"}, {"name": "xxx"}, ...]
}, ...
]
I can use something like:
restaurants = Restaurant.objects.all().values()
for restaurant in restaurants:
restaurant['food'] = Food.objects.filter(restaurant__id=restaurant['id']).values()
But this loses some optimization in SQL level, as this is using application level loop.
Is there a better way to do this?
check this
restaurant = Restaurant.objects.all()
result = []
for rest in restaurant:
data = {
'name': rest.name,
'food': rest.food_set.all().values('name')
}
result.appen(data)
Try it....
User django rest framework. using serialzers you can do it more effectively
#Nakamura You are correct, query inside the loop is not a good way of coding.
below is one way of solving
restaurants = Restaurant.objects.all().values()
foods = Food.objects.all()
for restaurant in restaurants:
restaurant['food'] = foods.filter(restaurant__id=restaurant['id']).values()
As you gave required JSON format i suggested this answer.
Related
Working with ManyToManyField I want to get data of all the users related to all the queried model object along with other field data in the model.
For example for the below model, I have 2 users related to this "ChatRoom"
class ChatRoomParticipants(models.Model):
user = models.ManyToManyField(User, related_name='chatroom_users')
room = models.ForeignKey(ChatRoom, on_delete=models.PROTECT)
With the below query
chatrooms = list(ChatRoomParticipants.objects.filter(user=user).values('user__user_uid', 'room__id', 'room__name'))
I'm able to fetch
[{'user__user_uid': UUID('f4253fbd-90d1-471f-b541-80813b51d610'), 'room__id': 4, 'room__name': 'f4253fbd-90d1-471f-b541-80813b51d610-872952bb-6c34-4e50-b6fd-7053dfa583de'}]
But I'm expecting something like
[{
'user__user_uid1': UUID('f4253fbd-90d1-471f-b541-80813b51d610'),
'user__user_uid2': UUID('872952bb-6c34-4e50-b6fd-7053dfa583de'),
'room__id': 4,
'room__name': 'f4253fbd-90d1-471f-b541-80813b51d610-872952bb-6c34-4e50-b6fd-7053dfa583de'
},
{
'user__user_uid1': UUID('f4253fbd-90d1-471f-b541-80813b51d610'),
'user__user_uid2': UUID('eecd66e7-4874-4b96-bde0-7dd37d0b83b3'),
'room__id': 5,
'room__name': 'f4253fbd-90d1-471f-b541-80813b51d610-eecd66e7-4874-4b96-bde0-7dd37d0b83b3'
},
{
'user__user_uid1': UUID('f4253fbd-90d1-471f-b541-80813b51d610'),
'user__user_uid2': UUID('4f4c0f3d-2292-4d06-afdc-1e95962ac5e6'),
'room__id': 6,
'room__name': 'f4253fbd-90d1-471f-b541-80813b51d610-4f4c0f3d-2292-4d06-afdc-1e95962ac5e6'
}]
I've searched and found I can do something like
user_data = chatrooms.users.all().values('user_uid')
But the above doesn't work well with filter and I would miss out data on room.
Note: I know that's not a correct method to do what I'm trying to achieve, if anyone can enlighten with what's the correct way to achieve the same data.
your example is somewhat confusing, but I think what you are looking for is to find the information of the users related to the same room.
chat_room = ChatRoomParticipants.objects.get(id=id_room)
users_chat = chat_room.user.all().values_list('user_uid', flat=True)
data = {
"room__id": chat_room.room.id
"room__name": chat_room.room.name
"users" : users_chat
}
for something more consistent you can use serializers
This is my model
class Mymodel(models.Model):
title = models.CharField(max_length=150)
author = models.CharField(max_length=150)
I have a json like this stored in db q = {"field_name": "title", "value":"django"} and this is what I am trying to do.
Mymodel.objects.filter(q["field_name"]=q["value"])
It is not working. Getting the error
SyntaxError: keyword can't be an expression
How can i make this working ? I have tried things like
Mymodel.objects.filter(getattr(event_model, q["field_name"])=q["value"])
It is possible to make it work like many if elif conditions like below but the code becomes ugly. Is there any cleaner way to do this ?
if q["field_name"] == "title":
Mymodel.objects.filter(title=q["value"])
elif q["field_name"] == "author":
Mymodel.objects.filter(author=q["value"])
You can use the dictionary unpacking technique as,
q = {"field_name": "title", "value": "django"}
Mymodel.objects.filter(**{q["field_name"]: q["value"]})
Right now with the current Views functions I am getting the data given below:
{"item": "zxmnb",
"category": "zxc",
"price": "zxc",
"restaurant": 1}
Here is my views file:
class RestaurantMenuView(generics.RetrieveAPIView):
lookup_field = 'item'
serializer_class = MenuSerializer
def get_queryset(self):
return Menu.objects.all()
But the issue is I want the data to be in a format as:
{"restaurant": "name"
"item":"some item",
"category": "some category",
"price": "some price"
}
I want to mention that Restaurant is another model in my models class,Now I know that if I use restaurant I will only get the pk. But what I want is the JSON to be displayed like that.
You need to modify your MenuSerializer. Specifically, you need to change the restaurant field to be a CharField and also provide a source attribute. Something like the following:
class MenuSerializer(serializers.ModelSerializer):
restaurant = serializers.CharField(source='restaurant.name')
# ... other stuff in the serializer
Here, I am assuming that your Restaurant model has a name field.
You can read more about Serializer fields here: https://www.django-rest-framework.org/api-guide/fields/
Why don't you redefine the to_representation() function. Something like this:
class MenuSerializer(serializers.ModelSerializer):
def to_representation(self, obj):
restaurants = RestaurantSerializer(instance=obj,
context=self.context).data
data = []
for restaurant in restaurants:
data.append(
{
"restaurant": {
"item": obj.item,
"category": obj.category,
"price": obj.price,
"name": restaurant.name,
}
}
)
return data
Without looking at your models or why you want restaurant in there, I added the for loop in order to show you that you can pretty much access any of the data in your to_representation() and put it in any sort of format you want. I use this when I'm trying to render my JSON objects into XML in a specific way. Hope this helps.
Also check out the documentation:
https://www.django-rest-framework.org/api-guide/serializers/#overriding-serialization-and-deserialization-behavior
Another solution that you could consider is adding a Foreign Key on your Menu model back to the Restaurant, then you could define your serializer like this:
class RestaurantSerializer(serializers.ModelSerializer):
menu = MenuViewSerializer(read_only=True, many=True)
class Meta:
model = Restaurant
fields = [
"id",
"name",
"menu",
]
extra_kwargs = {
"menu": {"read_only": True},
}
I have models like -
class Sectors(models.Model):
sector_mc = models.TextField()
class Insector(models.Model):
foundation = models.ForeignKey(Sectors)
name = models.TextField()
value = models.FloatField(default=0)
I want my data to be in a format like-
"name": "cluster",
"children": [
{"name": "Tester", "value": 3938},
{"name": "CommunityStructure", "value": 3812},
]
},
{
"name": "graph",
"children": [
{"name": "BetweennessCentrality", "value": 34},
{"name": "LinkDistance", "value": 5731},
]
}
So "cluster" goes in "sector_mc" of "Sectors"
and cluster has children "Tester" and "CommunityStructure"(these go in "name" and "value" of "Insector" for cluster sector_mc)
Similarly "graph" goes in "sector_mc" of Sectors and graph has children "BetweennessCentrality" and "LinkDistance"(these go in "name" and "value" of "Insector" for graph sector_mc)
And so on.
How to save these with the same relation in the database and also, how to use DRF to structure it in this format. I tried using nested relationship of DRF with this but reached no where.
I need the data to be in this format for d3 sunburst chart. If there are any other workarounds, I'd be very open to know them as well. Thank you
You can create a SectorSerializer which will contain a nested serializer InsectorSerializer.
Since you are using different keys for output we can use source to get the corresponding values from the field names.
name key in the output corresponds to sector_mc, so we use sector_mc as its source.
children key in the output contains all the related instances for a particular Sectors instance. We can use FOO_set as the source for children field. Also, since there will be multiple related instances, we pass many=True parameter along with it.
class InsectorSerializer(serializers.ModelSerializer):
class Meta:
model = Insector
fields = ('name', 'value')
class SectorSerializer(serializers.ModelSerializer):
name = serializers.CharField(source='sector_mc') # get value from 'sector_mc'
children = InsectorSerializer(many=True, source='insector_set') # get all related instances
class Meta:
model = Sectors
fields = ('name', 'children')
There was a problem in my views file while saving data to models. DRF worked fine when views problem solved.
I have a Django model that is like this:
class WindowsMacAddress(models.Model):
address = models.TextField(unique=True)
mapping = models.ForeignKey('imaging.WindowsMapping', related_name='macAddresses')
And two serializers, defined as:
class WindowsFlatMacAddressSerializer(serializers.Serializer):
address = serializers.Field()
class WindowsCompleteMappingSerializer(serializers.Serializer):
id = serializers.Field()
macAddresses = WindowsFlatMacAddressSerializer(many=True)
clientId = serializers.Field()
When accessing the serializer over a view, I get the following output:
[
{
"id": 1,
"macAddresses": [
{
"address": "aa:aa:aa:aa:aa:aa"
},
{
"address": "bb:bb:bb:bb:bb:bb"
}
],
"clientId": null
}
]
Almost good, except that I'd prefer to have:
[
{
"id": 1,
"macAddresses": [
"aa:aa:aa:aa:aa:aa",
"bb:bb:bb:bb:bb:bb"
],
"clientId": null
}
]
How can I achieve that ?
Create a custom serializer field and implement to_native so that it returns the list you want.
If you use the source="*" technique then something like this might work:
class CustomField(Field):
def to_native(self, obj):
return obj.macAddresses.all()
I hope that helps.
Update for djangorestframework>=3.9.1
According to documentation, now you need override either one or both of the to_representation() and to_internal_value() methods. Example
class CustomField(Field):
def to_representation(self, value)
return {'id': value.id, 'name': value.name}
Carlton's answer will work do the job just fine. There's also a couple of other approaches you could take.
You can also use SlugRelatedField, which represents the relationship, using a given field on the target.
So for example...
class WindowsCompleteMappingSerializer(serializers.Serializer):
id = serializers.Field()
macAddresses = serializers.SlugRelatedField(slug_field='address', many=True, read_only=True)
clientId = serializers.Field()
Alternatively, if the __str__ of the WindowsMacAddress simply displays the address, then you could simply use RelatedField, which is a basic read-only field that will give you a simple string representation of the relationship target.
# models.py
class WindowsMacAddress(models.Model):
address = models.TextField(unique=True)
mapping = models.ForeignKey('imaging.WindowsMapping', related_name='macAddresses')
def __str__(self):
return self.address
# serializers.py
class WindowsCompleteMappingSerializer(serializers.Serializer):
id = serializers.Field()
macAddresses = serializers.RelatedField(many=True)
clientId = serializers.Field()
Take a look through the documentation on serializer fields to get a better idea of the various ways you can represent relationships in your API.