I am getting this error after serializing a many-to-many relationship, which works fine without the URL but for some reason I am getting this error when I try to include it.
`InitiativeUrlHyperlinkedIdentityField` requires the request in the serializer context. Add `context={'request': request}` when instantiating the serializer.
Here are my views and serializers.
Views.py
class CreateInitiativeAPIView(generics.CreateAPIView):
serializer_class = CreateInitiativeSerializer
class InitiativeListAPIView(generics.ListAPIView):
authentication_classes = [SessionAuthentication, BasicAuthentication, JSONWebTokenAuthentication]
serializer_class = InitiativeListSerializer
permission_classes = [permissions.IsAuthenticated]
queryset = Initiative.objects.all()
class InitiativeDetailAPIView(generics.RetrieveAPIView):
authentication_classes = [SessionAuthentication, BasicAuthentication, JSONWebTokenAuthentication]
serializer_class = InitiativeFullSerializer
permission_classes = [permissions.IsAuthenticated]
lookup_field = 'id'
def get_object(self):
initiative_id = self.kwargs["initiative_id"]
obj = get_object_or_404(Initiative, id=initiative_id)
return obj
def put(self, request, *args,**kwargs):
return self.update(request, *args, **kwargs)
Serializers.py
class InitiativeUrlHyperlinkedIdentityField(serializers.HyperlinkedIdentityField):
def get_url(self, obj, view_name, request, format):
kwargs = {
'initiative_id': obj.id,
}
return reverse(view_name, kwargs=kwargs, request=request, format=format)
class CreateInitiativeSerializer(serializers.ModelSerializer):
class Meta:
model = Initiative
fields = ['name', 'description', 'image', 'goal']
class InitiativeListSerializer(serializers.ModelSerializer):
url = InitiativeUrlHyperlinkedIdentityField("initiative_detail_api", lookup_field='id')
class Meta:
model = Initiative
fields = [
'url',
'name',
]
class InitiativeFullSerializer(serializers.ModelSerializer):
url = InitiativeUrlHyperlinkedIdentityField("initiative_detail_api", lookup_field='id')
class Meta:
model = Initiative
fields = [
'url',
'id',
'name',
'description',
'image',
'goal']
This is the serializer that gets the information from InitiativeFullSerializer.
class ChapterInitiativePartialListSerializer(serializers.ModelSerializer):
initiative = InitiativeFullSerializer(many=False, read_only=True)
class Meta:
model = ChapterInitiative
fields = [
'initiative'
]
The initial serializer/s which is called via the URL.
class ChapterFullListSerializer(serializers.ModelSerializer):
url = ChapterUrlHyperlinkedIdentityField("chapter_detail_api", lookup_field='id')
active_initiativeset = serializers.SerializerMethodField('get_active')
school = serializers.CharField(source='school.name', read_only=True)
class Meta:
model = Chapter
fields = [
'url',
'id',
'school',
'name',
'payee',
'active_initiativeset'
]
def get_active(self, chapter):
initiatives = chapter.chapterinitiative_set.filter(active=True)
serializer = ChapterInitiativePartialListSerializer(instance=initiatives, many=True)
return serializer.data
Where in my code should I add context={'request': request}?
Hm. You have to check your view classes. There is a method called get_serializer_context() which should add the request by default. See here. I would check this first.
Related
I'm trying to make a rest API for a blog app using Django-rest-framework. I'm a beginner and it's hard for me to understand how to implement that system. I made an intermediary model for making connections between followers and followings and serializers for users. But my API showing absolutely wrong following and followers for each user and endpoints for following and unfollowing not working also. https://github.com/romon267/blog_api - my code
Models.py:
class UserFollowing(models.Model):
class Meta:
constraints= [
models.UniqueConstraint(fields=['user_id', 'following_user_id'], name='unique_following')
]
ordering = ['-created']
user_id = models.ForeignKey('auth.User', related_name='following', on_delete=models.SET_NULL, null=True,blank=True)
following_user_id = models.ForeignKey('auth.User', related_name='followers', on_delete=models.SET_NULL, null=True,blank=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'{self.user_id} is following {self.following_user_id}'
serializers.py:
class UserSerializer(serializers.HyperlinkedModelSerializer):
following = serializers.HyperlinkedRelatedField(many=True, view_name='user-detail', read_only=True)
followers = serializers.HyperlinkedRelatedField(many=True, view_name='user-detail', read_only=True)
posts = serializers.HyperlinkedRelatedField(many=True, view_name='post-detail', read_only=True)
class Meta:
model = User
fields = ['url', 'id', 'username', 'posts', 'following', 'followers']
def get_following(self, obj):
return FollowingSerializer(obj.following.all(), many=True).data
def get_followers(self, obj):
return FollowersSerializer(obj.followers.all(), many=True).data
class UserFollowingSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = UserFollowing
fields = '__all__'
class FollowingSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = UserFollowing
fields = ['id', 'following_user_id', 'created']
class FollowersSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = UserFollowing
fields = ['id', 'user_id', 'created']
views.py:
class UserViewSet(viewsets.ReadOnlyModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserFollowingViewSet(viewsets.ModelViewSet):
queryset = UserFollowing.objects.all()
serializer_class = UserFollowingSerializer
class UserFollow(APIView):
"""
Retrieve, update or delete a snippet instance.
"""
def get_object(self, pk):
try:
return User.objects.get(pk=pk)
except User.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
user = self.get_object(pk)
serializer = UserSerializer(user)
return Response(serializer.data)
def post(self, request, pk, format=None):
user = request.user
follow = self.get_object(pk)
UserFollowing.objects.create(user_id=user.id, following_user_id = follow.id)
serializer = UserSerializer(follow)
return Response(serializer.data)
def delete(self, request, pk, format=None):
user = request.user
follow = self.get_object(pk)
UserFollowing.objects.delete(user_id=user.id, following_user_id = follow.id)
serializer = UserSerializer(follow)
return Response(serializer.data)
I have a field in my ModelSerializer which I've set as SerializerMethodField to modify the get behaviour for the field. I could update the data before, now I can't. How can I solve this?
Initially, without using SerializerMethodField, I got data like this:
{
...
"members": [2,3],
...
}
but I added SerializerMethodField to modify the data, then update stopped working.
models.py
# Create your models here.
class Company(models.Model):
members = ArrayField(models.IntegerField(blank=True), blank=True)
...
serializers.py
class AccountSerializer(serializers.ModelSerializer):
user=serializers.StringRelatedField(read_only=False)
class Meta:
model=Account
fields='__all__'
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
class CompanySerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=False)
members = serializers.SerializerMethodField()
class Meta:
model = Company
fields = '__all__' #('id', 'name', 'description', 'date_created', 'user', 'status', 'theme', 'members')
def get_members(self, obj):
accounts = Account.objects.filter(id__in=obj.members)
return AccountSerializer(accounts, many=True).data
...
You need to use different serializers for update and create. This serializer works for get only.
Or, you can create a custom field. Django Rest Framework How to update SerializerMethodField
Or, there can be other simpler hooks. If 'create' and 'update' worked as you wanted before modifiying members, then you can do as follow to get everything to default for create and update requests.
Instead of using SerializerMethodField, override serializer representation.
class CompanySerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=False)
class Meta:
model = Company
fields = ('id', 'name', 'description', 'date_created', 'user', 'status', 'theme', 'members', 'members_data')
def to_representation(self, obj)
ret = super().to_representation(obj)
ret["members"] = AccountSerializer(accounts, many=True).data
return ret
Override the __init__ method .
.
class CompanySerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=False)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
try:
if self.context['request'].method in ['GET']:
self.fields['members'] = SerializerMethodField()
except KeyError:
pass
class Meta:
model = Company
fields = '__all__' #('id', 'name', 'description', 'date_created', 'user', 'status', 'theme', 'members')
def get_members(self, obj):
accounts = Account.objects.filter(id__in=obj.members)
return AccountSerializer(accounts, many=True).data
...
Or, you can create different field for getting members.
class CompanySerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=False)
members_data = SerializerMethodField()
class Meta:
model = Company
fields = ('id', 'name', 'description', 'date_created', 'user', 'status', 'theme', 'members', 'members_data')
def get_members_data(self, obj):
accounts = Account.objects.filter(id__in=obj.members)
return AccountSerializer(accounts, many=True).data
...
I need viewset to return always absolute url for avatar / avatar_thumbnail image fields in response, but I'm getting relative paths. I have two such cases: 1st) is in image field in nested serializer, 2nd) is in viewset where I want to use two serializers in retrieve method.
models.py
class CustomUser(AbstractUser):
avatar = ProcessedImageField(upload_to='users/',format='JPEG',options={'quality': 60})
avatar_thumbnail = ImageSpecField(source='avatar',processors=[ResizeToFill(50, 50)],format='JPEG')
settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
urls.py
if settings.DEBUG:
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)here
forum/api/serializers.py (nested serializer)
class UserSerializer(serializers.ModelSerializer):
avatar_thumbnail = serializers.ImageField(read_only=True)
class Meta:
model = CustomUser
fields = ['id', 'username', 'avatar_thumbnail']
class ThreadSerializer(serializers.ModelSerializer):
class Meta:
model = Thread
fields = ['id', 'title', 'subject', 'user']
def to_representation(self, instance):
representation = super().to_representation(instance)
representation['user'] = UserSerializer(instance.user).data
return representation
forum/api/views.py
class ThreadViewSet(viewsets.ModelViewSet):
serializer_class = ThreadSerializer
queryset = Thread.objects.all()
For UserViewSet I have also similar problem with urls for "avatar". I need absolute urls. I get relative url, but only when I overwrite retrieve method in viewset. (I overwrite it to use different serializer "UserPrivateSerializer" for user that is owner of the profile) .For list I always get absolute url.
users/api/serializers.py
class UserPublicSerializer(serializers.ModelSerializer):
class Meta:
model = CustomUser
fields = ['id', 'username', 'avatar']
class UserPrivateSerializer(serializers.ModelSerializer):
email = serializers.EmailField(required=True)
class Meta:
model = CustomUser
fields = ['id', 'username', 'email', 'avatar']
users/api/views.py
(Here the problem is with additional serializer, that I want to use in retrieve method)
class UserViewSet(viewsets.ModelViewSet):
queryset = CustomUser.objects.exclude(username='user_deleted')
serializer_class = UserPublicSerializer
permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
def retrieve(self, request, *args, **kwargs):
"""Custom retrieve method use UserPrivateSerializer if the user object is requested by it's owner.
"""
super().retrieve(request, *args, **kwargs)
instance = self.get_object()
if request.user == instance:
serializer = UserPrivateSerializer(instance)
else:
serializer = UserPublicSerializer(instance)
return Response(serializer.data, status=status.HTTP_200_OK)
I have found out that the problem is solved when I add context to serializers.
forum/api/serializers.py
class ThreadSerializer(serializers.ModelSerializer):
class Meta:
model = Thread
fields = ['id', 'title', 'subject', 'user']
def to_representation(self, instance):
representation = super().to_representation(instance)
representation['user'] = UserSerializer(instance.user, context=self.context).data
return representation
users/api/views.py
class UserViewSet(viewsets.ModelViewSet):
queryset = CustomUser.objects.exclude(username='user_deleted')
serializer_class = UserPublicSerializer
permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
def retrieve(self, request, *args, **kwargs):
super().retrieve(request, *args, **kwargs)
instance = self.get_object()
if request.user == instance:
serializer = UserPrivateSerializer(instance, context={'request': request})
else:
serializer = UserPublicSerializer(instance, context={'request': request})
return Response(serializer.data, status=status.HTTP_200_OK)
I have found my answer here: Django REST Framework and FileField absolute url
"The request must available to the serializer, so it can build the full absolute URL for you. One way is to explicitly pass it in when the serializer is created"
I have some nested serializers, and I'm going to get the users objects in a JSON format through http requests, using following snippet:
import requests
r = requests.get('http://127.0.0.1:8000/accounts/users',
auth=("admin", "mat"))
But here is what it returns:
PrimaryKeyRelatedField(allow_empty=False, many=True, queryset=App.objects.all()) is not JSON serializabl
I tried a lot of approached include using to_representation method and inheriting from serializers.RelatedField. But always I get the same result.
It seems that I'm doing something wrong. I'd be appreciate if you have any suggestion regarding this?
Here are serializers:
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ('name',
'popularity')
class CategorySerializer(serializers.ModelSerializer):
tags = TagSerializer(many=True)
class Meta:
model = Category
fields = ('en_name',
'fa_name',
'tags')
class CpSerializer(serializers.ModelSerializer):
class Meta:
model = CP
fields = ('en_name',
'fa_name',
'id_number')
class AppSerializer(serializers.ModelSerializer):
category = CategorySerializer()
cp = CpSerializer()
class Meta:
model = App
fields = ('en_name',
'fa_name',
'package_name',
'build_number',
'downloads',
'cp',
'category')
class UserAppSerializer(serializers.ModelSerializer):
class Meta:
model = UserApps
app = AppSerializer() # or even serializers.StringRelatedField()
fields = ('status', 'user_rate', 'comment', 'app')
def to_representation(self, instance):
return None
class UserSerializer(serializers.HyperlinkedModelSerializer):
id_number = serializers.CharField(read_only=True, source='userprofile.id_number')
apps = serializers.SerializerMethodField()
class Meta:
model = User
fields = ('username',
'password',
'first_name',
'last_name',
'email',
'id_number',
'apps')
def get_apps(self, obj):
if obj.username != "admin":
return
else:
apps = UserAppSerializer(read_only=True,
many=True)
return apps
class UserProfileSerializer(serializers.HyperlinkedModelSerializer):
user = UserSerializer()
class Meta:
model = UserProfile
fields = ('user', 'id_number')
And here is the get method of my view:
def get(self, request):
print("call get method on {}".format(self.__class__.__name__))
if request.user.username == 'admin':
users = User.objects.all()
lookup_field = 'username'
serializer = UserSerializer(users, many=True)
return Response(serializer.data)
I've defined the apps field in UserProfile as following:
apps = models.ManyToManyField('UserApps')
And in UserApps the app as following:
app = models.ManyToManyField(Application)
And in Application the cp and category as:
cp = models.ForeignKey('cp.CP')
category = models.ForeignKey(Category)
And in category the tags has been defined as:
tags = models.ManyToManyField('Tag')
def get_apps(self, obj):
if obj.username != "admin":
return
else:
apps = UserAppSerializer(read_only=True,
many=True)
return apps
This function is incorrect.
that function is getting user instance as parameter obj and is supposed to return value for app field.
However the function doesn't do anything with the user instance and instead of returning value return an empty instance of UserAppSerializer.
also
app = models.ManyToManyField(Application)
This should've been called apps for clarity
apps = models.ManyToManyField(Application)
Then your serializer function can be re-written like this
def get_apps(self, obj):
if obj.username != "admin":
return
else:
return UserAppSerializer(instance=obj.apps,
read_only=True, many=True).data
first, in models.py
class UserComment(models.Model):
user = models.ForeignKey(User)
rate = models.IntegerField()
description = models.CharField(max_length=512)
createTime = models.DateTimeField(auto_now=True)
def __unicode__(self):
return '<UserComment {%s %d}>' % (self.user.username, self.rate)
then, serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email', )
class UserCommentSerializer(serializers.ModelSerializer):
user = UserSerializer(required=False)
class Meta:
model = UserComment
views.py
class UserCommentViewSet(viewsets.ModelViewSet):
queryset = UserComment.objects.all()
serializer_class = UserCommentSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly, )
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.DATA, files=request.FILES)
serializer.is_valid()
print serializer.errors
print serializer.data
return super(UserCommentViewSet, self).create(request, *args, **kwargs)
then i post json data
{"user":{"id":"1","username":"watsy"},"rate":"5","description":"hello"}
i think,it will work. and insert it to db, but i get errors.
{"user": [{"username": ["User with this Username already exists."]}]}
>_<, I have no idea.
You need to make few changes to your serializer:
class UserCommentSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = UserComment
depth = 1
Now pass this JSON dict in your request:
{"user":"1", "rate":"5", "description":"hello"}