Django REST Framework group by in nested model - python

I'm new to Django REST Framework.
I have a nested model for saving orders of a restaurant. When I send a GET request I get the following response:
[
{
"menu": {
"id": 1,
"food_name": "food1"
},
"user": {
"id": 49,
"username": "A"
}
},
{
"menu": {
"id": 1,
"food_name": "food1"
},
"user": {
"id": 63,
"username": "B"
}
}
]
But I want to group users with the same menu like this:
[
{
"menu": {
"id": 1,
"food_name": "food1",
"users": {
"1": {
"id": 49,
"username": "A"
},
"2": {
"id": 63,
"username": "B"
}
}
}
}
]
Here is my code:
models.py
class Order(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
menu = models.ForeignKey(Menu, on_delete=models.CASCADE)
views.py
class OrderViewSet(viewsets.ModelViewSet):
queryset = Order.objects.all()
serializer_class = OrderSerializer
serilizers.py
class OrderSerializer(serializers.HyperlinkedModelSerializer):
user = UserSerializer()
menu = MenuSerializer()
class Meta:
model = Order
fields = ['id', 'user', 'menu']
Thanks

Since your output suggests that you are working with menus, I suggest you create a separate viewset that works with menus and returns the expected output you want here.
Doing the expected results in the order viewset is possible, but is tricky to optimise using select_related and prefetch_related. But if you really want this in your order viewset, then you can do this with:
class MenuSerializer(serializers.ModelSerializer):
users = serializers.SerializerMethodField()
class Meta:
model = Menu
fields = ['id', 'food_name', 'users']
def get_users(self, obj):
users = User.objects.filter(pk__in=obj.order_set.all().values('user'))
return UserSerializer(users, many=True).data
class OrderSerializer(serializers.HyperlinkedModelSerializer):
menu = MenuSerializer()
class Meta:
model = Order
fields = ['id', 'menu']
You also need to change the queryset of the view if you want distinct results, otherwise you will get duplicates:
class OrderViewSet(viewsets.ModelViewSet):
queryset = Order.objects.all().distinct('menu')
serializer_class = OrderSerializer
Do note that this is not optimised, and each row in your order table will require hitting the database just to get the users through menu. distinct('menu') also only works on PostgreSQL.

You can nest User into Menu if you want to group by menu
class OrderSerializer(serializers.HyperlinkedModelSerializer):
menu = MenuSerializer(many=True)
class Meta:
model = Order
fields = ['id', 'menu']
class MenuSerializer(serializers.ModelSerializer):
user = UserSerializer(many=True)
class Meta:
model = Menu
fields = ['id', 'user']

Related

Django Rest Framework User Model Serializer Nested

I am having some issues with my DRF Serializers. I essentially have a model that has Django users as Foreign keys so I can see who is attached to a job.
When I try and resolve these user ID's nested inside my Job serializer using a User serializer I only see the ID, but when I use the User serializer on it's own not nested I get the correct fields returned. Below is my code snippets. Any help would be great.
models.py
from profiles.models import UserProfile
class Job(models.Model):
name = models.CharField(max_length=256, blank=False)
designer_one = models.ForeignKey(UserProfile, related_name='designer_one', on_delete=models.DO_NOTHING)
designer_two = models.ForeignKey(UserProfile, related_name='designer_two', on_delete=models.DO_NOTHING)
def __str__(self):
return self.name
class Meta(object):
verbose_name = "Job"
verbose_name_plural = "Jobs"
ordering = ['name']
serializers.py
from django.contrib.auth.models import User
class UsersSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'email', 'first_name', 'last_name')
class JobsSerializer(serializers.ModelSerializer):
tasks = TasksSerializer(many=True, read_only=True)
designer_one = UsersSerializer(many=False, read_only=True)
designer_two = UsersSerializer(many=False, read_only=True)
class Meta:
model = Job
fields = ('id', 'name', 'designer_one', 'designer_two', 'tasks')
What I get returned from UsersSerializer API View
[
{
"id": 1,
"email": "test#example.co.uk",
"first_name": "Admin",
"last_name": "User"
},
{
"id": 2,
"email": "test1#example.co.uk",
"first_name": "",
"last_name": ""
}
]
What I get returned from JobsSerializer API View
{
"id": 1,
"name": "Test job",
"designer_one": {
"id": 1
},
"designer_two": {
"id": 1
},
"tasks": [
{
"id": 1,
"name": "Test Task",
"job": 1
}
]
}
The issue is that you are using UsersSerializer for model User, meanwhile designer_one and designer_two are of type UserProfiile.
You can allow DRF generate the nested serializers for you using the depth option as #Anup Yadav suggested but if you want to have control over which fields are displayed, you need to create your own serializer for Userprofile and use it in the Job serializer

How to retrieve models data having nested relationship using serializers in Django Rest Framework?

i am explaining in detail about a problem that i am facing and seek help from this community. I followed this Django Rest Framework documentation but couldn't get the desired result for multi-level nesting
Consider the relationship of models as explained :
A user can have multiple Workspaces
A workspace can have multiple Projects
A Project can have multiple ProjectLists
ProjectList can have multiple Tasks
Tasks can have multiple updates
User
|
Workspaces (ForeignKey="User")
|____Projects (ForeignKey="Workspaces")
|____TodoList (ForeignKey="Projects")
|____Tasks (ForeignKey="TodoList")
|____Updates (ForeignKey="Tasks")
So what i want is to get all the data that a User has in a nested json format like this:
[
{
"workspace_id": "99a961ec-b89e-11e8-96f8-529269fb1459",
"workspace_owner": "1",
"workspace_title": "Indie Dev Works",
"projects": [
{
"project_id":"db09cfa0-b89e-11e8-96f8-529269fb1459",
"todo_list":[
{
"list_id": "9dc64e4c-b89f-11e8-96f8-529269fb1459",
"list_name":"Project list -1",
"tasks":[
"task_name":"Create HTML docs",
"updates":[
{
"id":"d5eb660e-b89f-11e8-96f8-529269fb1459",
"text":"Creating using PUG"
},
{
.....
.....
.....
.....
},
]
]
}
]
},
{
"project_id":".........",
....
....
....
..
..
.
},
]
}
]
So when my User logs in by entering email, i am trying to get all the Workspaces instance and then pass it to the serializers
in my views as mentioned below:
views.py
class InitializeHome(viewsets.ViewSet):
def list(self,request):
user_email = request.user.email
user_instance = utils.getUserInstance(user_email)
workspace_instance = WorkSpace.objects.filter(workspace_owner=user_instance)
testing_serializer = WorkSpaceSerializer(workspace_instance,many=True)
return Response(testing_serializer.data)
serializers.py
class UpdateSerializer(serializers.ModelSerializer):
class Meta:
# depth = 2
fields = '__all__'
model = Update
class TaskSerializer(serializers.ModelSerializer):
updates = UpdateSerializer(many=True,read_only=True)
class Meta:
# depth = 2
fields = '__all__'
model = Task
class ProjectTodoListSerializer(serializers.ModelSerializer):
tasks = TaskSerializer(many=True,read_only=True)
class Meta:
# depth = 2
fields = '__all__'
model = ProjectTodoListItem
class ProjectSerializer(serializers.ModelSerializer):
project_todo_list = ProjectTodoListItemSerializer(many=True,read_only=True)
class Meta:
# depth = 2
fields = '__all__'
model = Project
class WorkSpaceSerializer(serializers.ModelSerializer):
projects = ProjectSerializer(many=True,read_only=True)
class Meta:
# depth = 2
model = WorkSpace
fields = '__all__'
What I am getting is only this and none of the nested Arrays :
[
{
"id": 1,
"workspace_title": "Indie Dev Work",
"status": "ACTIVE",
"workspace_id": "26c60d80-c018-403c-84b2-d92f01f6fb7e",
"workspace_owner": 1
},
{
"id": 2,
"workspace_title": "Homework Space",
"status": "ACTIVE",
"workspace_id": "08c715cc-bd24-46d3-a1dd-14cf7ff28215",
"workspace_owner": 1
}
]
Found the answer.
By adding source="workspace_set" in all the Serializer classes, the results can be achieved:-
class WorkSpaceSerializer(serializers.ModelSerializer):
projects = ProjectSerializer(many=True,read_only=True,source="workspace_set")
class Meta:
# depth = 2
model = WorkSpace
fields = '__all__'
You can try this way too:
class WorkSpaceSerializer(serializers.ModelSerializer):
class Meta:
# depth = 2
model = WorkSpace
fields = '__all__'
def get_projects(self, obj): # get_YOUR_FIELD_NAME
return ProjectSerializer(obj.projects.all(), many=True).data
and same for other classes.
I'm not sure why your code doesn't work and i can't really test it right now but you can try naming the fields in your meta class (it should't make any difference but you can try it)

DRF, add custom field to ModelSerializer

I have some models in my project and I need a especial response of the API, i'm using Django Rest framework.
class Goal(models.Model):
name = models.CharField()
# more fields
class Task(models.Model):
name = models.CharField()
goal = models.ForeignKey(Goal)
class UserTask(models.Model):
goal = models.ForeignKey(Goal)
user = models.ForeignKey(User)
# other fields
I have this response:
{
"name": "One goal",
"task": [
{
"name": "first task"
},
{
"name": "second tas"
}
]
}
But I need this:
{
"name": "One goal",
"task": [
{
"name": "first task",
"is_in_usertask": true
},
{
"name": "second tas",
"is_in_usertask": false
}
]
}
I saw this in DRF docs but I don't know how to filter UserTask by the current user (or other that is given in URL paramenter) and each Goal.
Edit:
# serializers
class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Task
class GoalSerializer(serializers.ModelSerializer):
# related_name works fine
tasks = TaskSerializer(many=True)
class Meta:
model = Goal
try to use SerializerMethodField field as
class TaskSerializer(serializers.ModelSerializer):
is_in_usertask = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Task
fields = ('name', 'is_in_usertask')
def get_is_in_usertask(self, task):
return UserTask.objects.filter(user=self.context['request'].user, goal=task.goal).exists()
Take a look to this conversation: How to get Request.User in Django-Rest-Framework serializer?
You can't access to request.user directly

Django Model Foreign Key filtering

I need to filter some Models in Django and return them trough REST, but I have some difficulties. I have 4 Models connected with Foreign key's like so:
class Standort(models.Model):
name = models.CharField(max_length=40)
farbe = models.CharField(max_length=20, default="Black")
class Gruppe(models.Model):
standort = models.ForeignKey(Standort)
name = models.CharField(max_length=40)
class Person(models.Model):
name = models.CharField(max_length=40)
gruppe = models.ForeignKey(Gruppe, related_name='personen')
class Eintrag(models.Model):
person = models.ForeignKey(Person, related_name='eintrage')
typ = models.ForeignKey(Typ)
datum = models.DateField()
and Iam serializing them like so:
class EintragSerializer(serializers.ModelSerializer):
class Meta:
model = Eintrag
fields = ('datum', 'typ')
class PersonenSerializer(serializers.ModelSerializer):
eintrage = EintragSerializer(read_only=True, many=True)
class Meta(object):
model = Person
fields = ('id', 'name', 'eintrage')
class GruppenPersonenEintraegeSerializer(serializers.ModelSerializer):
personen = PersonenSerializer(read_only=True, many=True)
class Meta(object):
model = Gruppe
fields = ('id', 'name', 'personnel')
and my view looks like this:
class GruppenPersonenEintraege(APIView):
def get(self, request, standort, jahr):
gruppen = Gruppe.objects.filter(standort=standort)
serializer = GruppenPersonenEintraegeSerializer(gruppen, many=True)
return Response(serializer.data)
The result looks like this:
[
{
"id": 2,
"name": "2.Schicht",
"personen": [
{
"id": 1,
"name": "Rolf der Tester",
"eintrage": [
{
"datum": "2017-02-16",
"typ": 3
},
{
"datum": "2017-02-15",
"typ": 3
},
{
"datum": "2018-04-05",
"typ": 2
}
]
}
]
},
{
"id": 3,
"name": "Test",
"personen": []
}
]
This is totally fine, my Problem is when i also want to filter the year of "eintrage.datum"by adding: .filter(standort=standort, personen__eintrage__datum__year=2017)afterGruppe.objects. Then the entry with "id": 2 is repeated 3 times and the one with "id": 3 isn't displayed at all. how do i filter just the entry's of the second nested dict?
To avoid "id":2 repeated multi times, you can just add a list(set()) surround the filter queryset result, the django restful framework can also treat the list the same way as queryset. Also notice that in django orm, the hash of a model instance is the prime_key of in db, so that's why the set can work on queryset.
As for "id":3 not showing, I also have no ideas as you did, maybe double better check the db again. A little bit more info will be more helpful.

Get single record on Django Rest Framework based on id

I am getting the following response from my REST API at http://127.0.0.1:8000/api/category/:
[
{
"id": "17442811-3217-4b67-8c2c-c4ab762460d6",
"title": "Hair and Beauty"
},
{
"id": "18a136b5-3dc4-4a98-97b8-9604c9df88a8",
"title": "Plumbing"
},
{
"id": "2f029642-0df0-4ceb-9058-d7485a91bfc6",
"title": "Personal Training"
}
]
If I wanted to access, a single record, I presume that I would need to go to http://127.0.0.1:8000/api/category/17442811-3217-4b67-8c2c-c4ab762460d6 to access:
[
{
"id": "17442811-3217-4b67-8c2c-c4ab762460d6",
"title": "Hair and Beauty"
}
]
However, when I attempt this, it returns all of the records. How can I resolve this? This is my code so far:
urls.py
urlpatterns = [
url(r'^category/', views.CategoryList.as_view(), name="category_list"),
url(r'^category/?(?P<pk>[^/]+)/$', views.CategoryDetail.as_view(), name="category_detail")
]
views.py
class CategoryList(generics.ListAPIView):
"""
List or create a Category
HTTP: GET
"""
queryset = Category.objects.all()
serializer_class = CategorySerializer
class CategoryDetail(generics.RetrieveUpdateDestroyAPIView):
"""
List one Category
"""
serializer_class = CategorySerializer
serializers.py
class CategorySerializer(serializers.ModelSerializer):
"""
Class to serialize Category objects
"""
class Meta:
model = Category
fields = '__all__'
read_only_fields = ('id')
models.py
class Category(models.Model):
"""
Category model
"""
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
title = models.CharField(max_length=255)
def __str__(self):
return "%s" % (self.title)
Your first regex, r'^category/', matches both a URL with and without a UUID.
You should anchor it at the end:
r'^category/$'
Additionally/alternatively, you can swap the order of those URL definitions, as Django will take the first one it matches.

Categories

Resources