`HyperlinkedIdentityField` requires the request in the serializer context - nested

I've already tried everything I could, but nothing works for me
My models:
model Parent():
...
model Child():
owner = models.ForeignKey(Parent, on_delete=models.CASCADE,
related_name='children')
...
My Urls:
router = routers.DefaultRouter()
router.register(r'parents', ParentViewSet, base_name="parents")
router.register(r'childs', ChildViewSet, base_name="childs")
urlpatterns = [
path('api/', include(router.urls)),
]
My serializers:
class ParentSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name="parent-detail", lookup_field='pk')
children = serializers.SerializerMethodField()
class Meta:
model = User
fields = (
...
'children')
def get_children(self, obj):
queryset = Child.objects.filter(owner=obj)
children = ChildSerializer(queryset, many=True).data
return children
class ChildSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name="child-detail", lookup_field='pk')
...
My Viewsets:
class ParentViewSet(SerializerByActionMixin, LoggingMixin, viewsets.ModelViewSet):
queryset = Parent.objects.all().prefetch_related('children').order_by('-date_joined')
serializers = {
'default': ParentSerializer,
'create': ParentCreateSerializer,
'update': ParentUpdateSerializer,
}
class ChildViewSet(LoggingMixin, viewsets.ModelViewSet):
queryset = Child.objects.all().order_by('-timestamp')
serializer_class = ChildSerializer
http_method_names = ['get', 'put', 'patch', 'head']
My problem:
HyperlinkedIdentityField requires the request in the serializer context. Add context={'request': request} when instantiating the serializer.
I read this, but don't understand where in my code paste this context.
Help please. Thanks!

As the documentation says, you'll need the request in the context:
class ParentSerializer(serializers.ModelSerializer):
...
def get_children(self, obj):
queryset = Child.objects.filter(owner=obj)
children = ChildSerializer(queryset, many=True, context=self.context).data
return children

Related

access URL data in serializers class in Django Rest Framework

I'm getting None while accessing the data of the request.
views.py
def get(self, request, post_id, post_language_id, user_id):
...
paginator = CustomPageNumberPagination()
response = paginator.generate_response(language_liked_data, PostLanguageLikedSerializer, request)
return response
serializers.py
class PostLanguageLikedSerializer(serializers.ModelSerializer):
is_following = serializers.SerializerMethodField()
...
class Meta:
model = PostLanguageLike
fields = [...,'is_following']
def get_is_following(self, obj):
# here I'm getting None
user_id = self.context.get("user_id")
user_followings = UserFollowing.objects.filter(user_id=user_id, following_user_id=obj.user.id)
is_following = True if len(user_followings) > 0 else False
return is_following
but I need user_id from the URL so I found a way to access data through context. so I can access the value in the serializer.
views.py
def get(self, request, post_id, post_language_id, user_id):
...
language_liked_data = PostLanguageLike.objects.filter(post_language_id=post_in_lang.id)
post_language_like_serializer = PostLanguageLikedSerializer(language_liked_data, context={'user_id': user_id}, many=True)
return Response({"response": True, "return_code": "success", "result": {"liked_users": post_language_like_serializer.data}, "message": success["success"]}, status=200)
serializers.py
class PostLanguageLikedSerializer(serializers.ModelSerializer):
is_following = serializers.SerializerMethodField()
...
class Meta:
model = PostLanguageLike
fields = [...,'is_following']
def get_is_following(self, obj):
# here I want to access URL data.
user_id = self.context.get("user_id")
user_followings = UserFollowing.objects.filter(user_id=user_id, following_user_id=obj.user.id)
is_following = True if len(user_followings) > 0 else False
return is_following
the issue is I'm not able to use context={'user_id': user_id} with paginator.generate_response
is there any better way to get URL data in the serializer?
I got the answer on how to possibly get context data in the serializers.
views.py
paginator = CustomPageNumberPagination()
page = paginator.paginate_queryset(language_liked_data, request)
serializer = PostLanguageLikedSerializer(page, context={'user_id': user_id}, many=True)
response = paginator.get_paginated_response(serializer.data)
return response
paginator.py
def get_paginated_response(self, data):
# customize your response here "data" = "serializer.data"
return Response({
'links': {
'next': self.get_next_link(),
'previous': self.get_previous_link()
},
'count': self.page.paginator.count,
'results': data
})
serializers.py
class PostLanguageLikedSerializer(serializers.ModelSerializer):
is_following = serializers.SerializerMethodField()
...
class Meta:
model = PostLanguageLike
fields = [...,'is_following']
def get_is_following(self, obj):
# got context data here
user_id = self.context.get("user_id")
user_followings = UserFollowing.objects.filter(user_id=user_id, following_user_id=obj.user.id)
is_following = True if len(user_followings) > 0 else False
return is_following

How to select related objects with only one query

Models:
class Tag(BaseModel):
tag_name = models.CharField(max_length=250)
slug = models.SlugField()
def save(self, *args, **kwargs):
self.slug = slugify(self.tag_name)
super(Tag, self).save(*args, **kwargs)
def __str__(self):
return str(self.tag_name)
class Tags(BaseModel):
filtertype = models.CharField(max_length=250)
tags = models.ManyToManyField(Tag)
My current solution:
def get(self,request,version):
filtertype = request.query_params.get('filtertype', '')
filtertypes = filtertype.split(",")
tagsList = Tags.objects.filter(filtertype__in=filtertypes).values_list('tags', flat=True).distinct()
queryset = Tag.objects.filter(id__in=tagsList)
context = {"request": request}
serialized = TagListSerializers(queryset, many=True, context=context)
return Response(serialized.data)
Im trying to get all the relevant Tag base on Tags/filtertype. Im wondering how can I do it with only one query.
you can do the query part with this code:
queryset = Tag.objects.filter(tags__filtertype__in=filtertypes)

Django duplicate file to all the records with M2M which is self referencing

I have a Serializer & APIview where i upload a file it should be uploaded to other related records as well.
class Record(models.Model):
name = models.CharField(max_length = 122)
related_records = models.ManyToManyField('self', null = True)
class RecordDocument(models.Model):
record = models.ForeignKey(Record, related_name = 'documents', null = True,
blank= True )
file = models.FileField(max_length=255, blank=True, null=True)
class RecordDocumentSerializer(serializers.ModelSerializer):
save_documents_to_related_records = serializers.BooleanField()
class Meta:
model = RecordDocument
fields = ( 'save_documents_to_related_records', 'file',
'record' )
class RecordDocumentUploadView(APIView):
def post(self, request, *args, **kwargs):
'how do i get serializers field **save_documents_to_related_records** and save
the file on **related_records** which is not used in serializer till now if
there are any inside it'
You can try this.
class RecordDocumentUploadView(APIView):
def post(self, request, *args, **kwargs):
serializer = RecordDocumentSerializer(data = request.data)
if serializer.is_valid():
input_data = serializer.validated_data
flag = input_data.pop('save_documents_to_related_records')
new_document = RecordDocument.objects.create(**input_data)
if flag:
...
else:
return Response(status = status.HTTP_400_BAD_REQUEST)

How to overwrite data from a model that is linked to the main model through a o-t-m relationship?

I have three models that are related to each other, namely:
models.py
class Shop(models.Model):
number = models.PositiveSmallIntegerField()
name = models.CharField(db_index=True)
city = models.ForeignKey(ShopCity, on_delete=models.CASCADE)
class Product(models.Model):
name = models.CharField(db_index=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
class ProductQuantity(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
shop = models.ForeignKey(Shop, on_delete=models.CASCADE)
quantity = models.PositiveSmallIntegerField(default=None)
In the admin panel they are linked in this way:
admin.py
class ProductQuantityInline(admin.TabularInline):
model = ProductQuantity
extra = 0
#admin.register(Product)
class ProductAdmin(ImportExportActionModelAdmin):
fields = ['name', 'price']
list_display = ['name', 'price']
inlines = [ProductQuantityInline]
There is a need to overwrite data with REST API
serializers.py
class QuantitySerializer(serializers.ModelSerializer):
class Meta:
model = ProductQuantity
fields = ('shop', 'quantity')
class ProductSerializer(serializers.ModelSerializer):
productquantity = serializers.SerializerMethodField(read_only=False)
class Meta:
model = Product
fields = ('name', 'price', 'productquantity')
def get_productquantity(self, obj):
return [QuantitySerializer(s).data for s in obj.productquantity_set.all()]
And finally my handler for REST API:
views.py
#api_view(['GET', 'PATCH', 'PUT', 'DELETE'])
def api_product_detail(request, pk):
product = Product.objects.get(pk=pk)
if request.method == 'GET':
serializer = ProductSerializer(product)
return Response(serializer.data)
elif request.method == 'PUT' or request.method == 'PATCH':
serializer = ProductSerializer(product, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
product.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
As a result, data such as the name and price are overwritten, and the productquantity is not overwritten.
What am I doing wrong? Thanks for any help.

filter ManyToManyField(**), by user in form selection

I need to add songs to my Playlist - but I want that only the user can add your registered songs, not all songs.
I have this:
models
class Song(models.Model):
user = models.ForeignKey(User, default=1)
title=models.CharField(max_length=500)
artist = models.CharField(max_length=250)
audio = models.FileField(default='')
def __str__(self):
return self.title
class List(models.Model):
user_list = models.ForeignKey(User, default=User)
title_list=models.CharField(max_length=500)
songs = models.ManyToManyField(Song)
def __str__(self):
return self.title_list
forms
class SongForm(forms.ModelForm):
class Meta:
model = Song
fields = ['title', 'artist', 'audio']
class ListForm(forms.ModelForm):
#songs=forms.MultipleChoiceField(Song.objects.all(), widget=forms.CheckboxSelectMultiple)
#songs= forms.MultipleChoiceField( widget=forms.CheckboxSelectMultiple)
#songs=forms.ModelMultipleChoiceField(queryset=Song.objects.all(), widget=forms.CheckboxSelectMultiple) #here i dont know why the form dont save data
#I want this
songs=forms.ModelMultipleChoiceField(queryset=Song.objects.filter(user=actualuser),widget=forms.CheckboxSelectMultiple)
class Meta:
model = Lista
fields = ['title_list','songs']
#views
def new_list(request):
form=ListForm(request.POST or None, request.FILES or None)
if form.is_valid():
lista = form.save(commit=False)
lista.user_list = request.user
lista.save()
context = {
'username': request.user.username,
'lista': lista,
}
return render(request,'MyMusic/list_detail.html',context)
context={
'form':form,
'error_message': 'Error ',
}
return render(request,'MyMusic/list_form.html',context)
in forms view I need only see and I want select only my own registered songs, also I want can see a check box not the actual (widget=forms.CheckboxSelectMultiple), but actully this does not save the data.
the solution for me.
**Forms**
class ListForm(forms.ModelForm):
def __init__(self,user, *args, **kwargs):
super(ListForm, self).__init__(*args, **kwargs)
self.fields['songs'] = forms.ModelMultipleChoiceField(queryset=Song.objects.filter(user=user)
,required=False,widget=forms.CheckboxSelectMultiple)
class Meta:
model = Lista
fields = ['title_list','songs']
in views
def new_list(request):
form=ListForm(request.user,request.POST or None, request.FILES or None)
if form.is_valid():
lista = form.save(commit=False)
lista.user_list = request.user
lista.save()
lista.canciones = form.cleaned_data['songs'] # avoid conflict to save checkbox
context = {
'username': request.user.username,
'lista': lista,
}
return render(request,'MyMusic/list_detail.html',context)
context={
'form':form,
'error_message': 'Error ',
}
return render(request,'MyMusic/list_form.html',context)

Resources