How to serialize the result from select_related query in django - django

i want to join two tables using django select_related. But how can i serialize those data from two tables to get serialized details including full image url from another table
My View.py
class getALlUserProfileDataAPIView(views.APIView):
permission_classes = (permissions.AllowAny,)
def get(self, request):
#usedraa = User.objects.filter(qs).select_related('userAddress').values('id','useraddress__country', 'useraddress__state', 'useraddress__city','email','username','first_name','last_name','password')
usedraa = User.objects.filter().select_related('Profile').values('username','profile__image','first_name','last_name')
#usedraa = User.objects.all().prefetch_related()
userProfileSerializer0 = userProfileSerializer(usedraa, context={'request': request}, many=True)
return Response(userProfileSerializer0.data, status=status.HTTP_201_CREATED)
Serailizer
ass userProfileSerializer(serializers.ModelSerializer):
prodata = serializers.HyperlinkedIdentityField(read_only=True, view_name = 'profile')
# prodata = ProfileSerializer(read_only=True, many=True) # many=True is required
class Meta:
model = User
fields = ("first_name","last_name","username","prodata")

Related

How to upload multiple images with django rest framework CreateAPI in view and create method in serializers

I have a post serializer and a postimage serializer to upload multiple images to a post. I have this serializer, but I am not sure how to make it in a way, so that I can upload multiple images, for example 5 images with a single post, like how we use with formsets coz now I can upload only 1 image in 'images' field.
These are the serializers. Please do have a look and let me know what changes I have to make...
Thanks
class PostImageSerializer(serializers.ModelSerializer):
class Meta:
model = PostImage
fields = ['id', 'images',]
class PostSerializer(TaggitSerializer, serializers.ModelSerializer):
user = serializers.ReadOnlyField(source='user.username')
post_date = serializers.ReadOnlyField()
postimage_set = PostImageSerializer(many=True)
likes = UserSerializer(many=True)
class Meta:
model = Post
fields = ['id','title', 'post_date', 'updated', 'user', 'image', 'postimage_set']
def create(self,validated_data):
user = self.context['request'].user
title = validated_data['title']
image = self.context['request'].FILES.get('image')
images = self.context['request'].FILES.get('images')
m1 = Post(user=user,title=title,image=image,)
m1.save()
m2 = PostImage(post=m1, images= images)
m2.save()
validated_data['images']=m2.images
validated_data['image']=m1.image
return validated_data
views
class CreatePostAPIView(generics.CreateAPIView):
serializer_class = PostCreateSerializer
permission_classes = [IsAuthenticated]
def create(self, request, *args, **kwargs):
serializer = PostCreateSerializer(data=request.data, context={'request':request,})
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
This solved the problem
def create(self, validated_date):
images = self.context['request'].FILES.getlist('images')
for image in list(images):
m2 = PostImage(post=m1, images= image)
m2.save()
This is how you do the multiple upload on drf: example below on create you can access the multiple item you posted:
e.g posted:
Formdata
images: File.jpeg
images: File2.jpeg
images: File3.jpeg
class TestSerializer(serializers.Serializer):
images = serializers.ListField(child=serializers.FileField())
class CreatePostAPIView(generics.CreateAPIView):
permission_classes = [AllowAny]
serializer_class = TestSerializer
def create(self, request, *args, **kwargs):
images = request.data['images']
I would do it like this.
Make a PostModel and a PostImageModel (with a post ForeignKey) for each post image. And then in the serializer, make relation field with both.
Something like this:
models.py
class Post(Model):
post details ...
class PostImage(Model):
post = models.ForeigKey(Post, ...)
image = models.ImageField(...)
serializers.py
class PostImageSerializer(serializers.ModelSerializer):
class Meta:
model = PostImage
fields = '__all__'
class PostSerializer(serialiers.ModelSerializer):
images = serializers.SlugRelatedField(slug_field='image', many=True, ...)
class Meta:
model = Post
fields = [..., 'images']
views.py
class CreatePostView(CreateView):
classForm = PostForm (with images field)
def form_valid(self, form):
new_post = form.save(commit=False)
for image in new_post.images:
new_image = PostImage(post=new_post, image=image)
new_image.save()
new_post.save()
return super()form_valid(form)
So then in your api you should be able to see the post model with the images attached to it.

Django 3 ModelSerializier().data returns {}

I would like to create a route that returns some model properties as a JSON with the Django REST framework and filter them based on some properties. But I don't get the serializer to working properly.
class MaxQuantPathSerializer(serializers.ModelSerializer):
class Meta():
model = MaxQuantRun
fields = '__all__'
depth=0
class MaxQuantPathsAPI(generics.ListAPIView):
filter_fields = ['project', 'setup']
filter_backends = [DjangoFilterBackend]
def get(self, request, format=None):
get_data = request.query_params
queryset = MaxQuantRun.objects.filter(project=get_data['project'])
serializer = MaxQuantPathSerializer(queryset, many=True)
data = serializer.data
return JsonResponse(data, status=201, safe=False)
The queryset returns some values but the serializer returns an empty dictionary.
I updated the code based on the comments below. Now, a json file is returned.

Django Rest Framework SerializerMethodField only on GET Request

Running into a little snag here with my DRF backend.
I am populating fields with choices on certain models.
I have a foreign key requirement on one model. When I create the model I want to save it under the foreign id.
When I request the models, I want the model with whatever the choice field maps to.
I was able to do this with SerializerMethodField, however when I try to create a model, I get a 400 error because the block is not valid. If I remove the SerializerMethodField, I can save, but get the number stored in the db from the request.
Any help would be appreciated.
class BlockViewSet(ModelViewSet):
model = apps.get_model('backend', 'Block')
queryset = model.objects.all()
serializer_class = serializers.BlockSerializer
permissions = ('All',)
def create(self, request, format=None):
data = request.data
data['user'] = request.user.id
data['goal'] = WorkoutGoal.objects.get(goal=data['goal']).id
block = serializers.BlockSerializer(data=data, context={'request': request})
if block.is_valid():
new_block = block.save()
return Response({'block': {'name': new_block.name, 'id': new_block.id}}, status=status.HTTP_201_CREATED)
else:
return Response(block.errors, status=status.HTTP_400_BAD_REQUEST)
class WorkoutGoalSerializer(serializers.ModelSerializer):
class Meta:
model = apps.get_model('backend', 'WorkoutGoal')
fields = ('goal',)
goal = serializers.SerializerMethodField(read_only=True, source='get_goal')
def get_goal(self, obj):
return dict(WorkoutGoal.GOALS).get(obj.goal)
class BlockSerializer(serializers.ModelSerializer):
workout_count = serializers.IntegerField(required=False)
completed_workouts = serializers.IntegerField(required=False)
goal = WorkoutGoalSerializer()
class Meta:
model = apps.get_model('backend', 'Block')
read_only_fields = ('workout_count', 'completed_workouts')
fields = read_only_fields + ('id', 'name', 'user', 'created', 'goal')
The above code returns the correct choice, but I can't save under it. Remove the goal = WorkoutGoalSerializer() and it saves but doesn't return the mapped choice.
I think this will work like a charm,
class WorkoutGoalSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if 'request' in self.context and self.context['request'].method == 'GET':
self.fields['goal'] = serializers.SerializerMethodField(read_only=True, source='get_goal')
class Meta:
model = apps.get_model('backend', 'WorkoutGoal')
fields = ('goal',)
goal = serializers.SerializerMethodField(read_only=True, source='get_goal') # remove this line
def get_goal(self, obj):
return dict(WorkoutGoal.GOALS).get(obj.goal)
How this Work?
It will re-initiate the goal field with SerializerMethodField, if the reuested method is GET.
Remember one thing, you should remove the line,
goal = serializers.SerializerMethodField(read_only=True, source='get_goal')
serializers.py
class BlockCreateSerializer(serializers.ModelSerializer):
workout_count = serializers.IntegerField(required=False)
completed_workouts = serializers.IntegerField(required=False)
class Meta:
model = apps.get_model('backend', 'Block')
read_only_fields = ('workout_count', 'completed_workouts')
fields = read_only_fields + ('id', 'name', 'user', 'created', 'goal')
class BlockSerializer(serializers.ModelSerializer):
workout_count = serializers.IntegerField(required=False)
completed_workouts = serializers.IntegerField(required=False)
goal = WorkoutGoalSerializer()
class Meta:
model = apps.get_model('backend', 'Block')
read_only_fields = ('workout_count', 'completed_workouts')
fields = read_only_fields + ('id', 'name', 'user', 'created', 'goal')
views.py
class BlockViewSet(ModelViewSet):
model = apps.get_model('backend', 'Block')
queryset = model.objects.all()
serializer_class = serializers.BlockSerializer
permissions = ('All',)
def get_serializer_class(self):
if self.action == 'create':
return serializers.BlockCreateSerializer
else:
return self.serializer_class
def create(self, request, format=None):
data = request.data
data['user'] = request.user.id
data['goal'] = WorkoutGoal.objects.get(goal=data['goal']).id
block = self.get_serializer(data=data)
if block.is_valid():
new_block = block.save()
return Response({'block': {'name': new_block.name, 'id': new_block.id}}, status=status.HTTP_201_CREATED)
else:
return Response(block.errors, status=status.HTTP_400_BAD_REQUEST)
override get_serializer_class to return different serializer_class for create and other action(list\retrieve\update\partial_update)

How can I optimize queries django rest-framework

I have the following serilizers
class AutoSerializer(serializers.ModelSerializer):
class Meta:
model = Auto
fields = ("nombre",)
class MarcaSerializer(WritableNestedModelSerializer):
autos = AutoSerializer(many=True)
class Meta:
model = Marca
fields = ("codigo", "descripcion", "autos")
ModelViewSet
class MarcaViewSet(viewsets.ModelViewSet):
queryset = Marca.objects.all()
serializer_class = MarcaSerializer
def list(self, request, *args, **kwargs):
queryset = self.queryset
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
Querys
How can you optimize access to the base, that is, make fewer inquiries
By using a .prefetch_related(..) that fetches the related Auto instances in one fetch:
class MarcaViewSet(viewsets.ModelViewSet):
queryset = Marca.objects.prefetch_related('autos').all()
serializer_class = MarcaSerializer
def list(self, request, *args, **kwargs):
queryset = self.queryset
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
This will first fetch the Marca objects, and then look with a JOIN for all the Auto objects that are related, and fetch all these objects with a single fetch into memory as well.
So the Auto objects are loaded in bulk instead of lazily each time fetch the Autos for a specific Marca object.
This kind of optization is documented in the article mentioned by #Jerin Peter George: "Optimizing slow Django REST Framework performance".
This article also discusses how to specify such prefetches at the side of the serializer, such that in case other tasks are done, the prefetch is not done. So we could for example write:
class AutoSerializer(serializers.ModelSerializer):
class Meta:
model = Auto
fields = ("nombre",)
class MarcaSerializer(WritableNestedModelSerializer):
autos = AutoSerializer(many=True)
#classmethod
def setup_eager_loading(cls, queryset):
return queryset.prefetch_related('autos')
class Meta:
model = Marca
fields = ("codigo", "descripcion", "autos")
and then write:
class MarcaViewSet(viewsets.ModelViewSet):
queryset = Marca.objects.all()
serializer_class = MarcaSerializer
def list(self, request, *args, **kwargs):
serializer = self.get_serializer
queryset = serializer.setup_eager_loading(self.queryset)
serializer = serializer(queryset, many=True)
return Response(serializer.data)

How to make change in request.data keeping check on serializer validataion in django rest framework?

So I am making quiz app which has following structure
class UserQuizRecord(models.Model):
user = models.OneToOneField(User)
quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE, related_name='userrecord')
score = models.FloatField(default=0.00)
I have serializer class of it
class UserQuizRecordSerializer(serializers.ModelSerializer):
class Meta:
model = UserQuizRecord
fields = ('user', 'quiz','score')
Here is the views.py file and the detail route I am providing with data {quiz:1,score:7} I wish to add user to the request.data or serializer at in the views function. I am failing to get the right way to do it.
class QuizView(viewsets.ModelViewSet):
serializer_class = QuizSerializer
queryset = Quiz.objects.all()
model = Quiz
#detail_route(methods=['post'], permission_classes=[AllowAny], url_path='rank')
def rank(self, request, pk=None):
request.data.quiz = Quiz.objects.get(pk=pk)
serializer = UserQuizRecordSerializer(data = request.data )
if serializer.is_valid():
serializer.save()
rank = UserQuizRecord.objects.filter(Q(quiz=request.data.quiz.id),Q(score__gte=request.data.score)).count()
return Response({'rank':rank}, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
What is the best way to perform this operation?