DRF, add custom field to ModelSerializer - python

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

Related

Django REST Framework group by in nested model

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']

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

Retrieve nested dict from Django nested queryset with ForeignKey?

I have a models.py having:
class Other(models.Model):
name = models.CharField(max_length=200)
class ModelA(models.Model):
name = models.CharField(max_length=200)
other = models.ForeignKey(Other, on_delete=models.PROTECT)
in my rest API i want to retrieve as JsonResponse a json like this:
{
"modelA": {
"id": "modelA id automatically assigned by django model",
"name": "my modelA name",
"other": {
"id": "other thing id also automatically assigned by django model",
"name": "other thing name"
}
}
}
What is the most "pythonic" way to do it?
What you are looking for is nested serialization.
In your serializers.py you should use the serializer for the Other model inside the one for your ModelA.
In serializers.py:
from rest_framework import serializers
from .models import Other, ModelA
class OtherSerializer(serializers.ModelSerializer):
class Meta:
model = Other
fields = ('id', 'name')
class ModelASerializer(serializers.ModelSerializer):
other = OtherSerializer(read_only=True)
# The magic happens here.
# You use your already created OtherSerializer inside the one for ModelA
# And that will perform nested serialization
# Which will produce the result that you want
class Meta:
model = ModelA
fields = ('id', 'name', 'other')
# _________________________^
And now you get result like:
{
"id": 1,
"name": "my modelA name",
"other": {
"id": 1,
"name": "other thing name"
}
}

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