Serializing NotificationQuerySet from django-notifications-hq not working - python

So, i'm trying to add to my API made with DRF (Django REST Framework) the notifications Model, but i'm getting this error:
AttributeError: 'NotificationQuerySet' object has no attribute 'recipient'
I'm trying to serialize a django app model, Notification. It's from this app:
https://github.com/django-notifications/django-notifications
My ViewSet class is this:
class NotificationsViewSet(viewsets.ViewSet):
serializer_class = NotificationsSerializer
def list(self, request):
queryset = Notification.objects.all()
return Response(NotificationsSerializer(queryset).data)
And here my serializer:
class NotificationsSerializer(serializers.ModelSerializer):
class Meta:
model = Notification
fields = ('recipient','description')
depth = 0
So, when data pass to serializer, it becomes "Void" or without any data.
Doing something like into the list method:
print queryset[0] returns a Notification object normaly. But when passing this queryset to the serializer, seems to be null, and the AttributeError comes.
Also, tried this with the console:
notifications = Notification.objects.all()
That returns a NotificationQuerySet object (iterable). Then I can:
for noti in notifications:
print noti
That would output all the unicode methods of every notification.
With every Notification instance, i can also access to Model propierties:
for noti in notifications:
print noti.recipient
And works very well.
Why is not working when passing this to the serializer? Its weird...

You need to pass many=True when initializing a serializer with a queryset. DRF will assume you are passing a single object and try to get the fields directly from it if you do not tell it that you are passing in multiple objects.

Heres a full implementation where the readme leaves off for drf
urls.py
...
import notifications.urls
urlpatterns = [
...
path("inbox/notifications/", views.NotificationViewSet.as_view({'get': 'list'}), name='notifications'),
]
serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
class NotificationSerializer(serializers.Serializer):
recipient = UserSerializer(read_only=True)
unread = serializers.BooleanField(read_only=True)
target = GenericNotificationRelatedField(read_only=True)
verb = serializers.CharField()
views.py
from notifications.models import Notification
from .serializers import NotificationSerializer
NotificationViewSet(viewsets.ViewSet):
serializer_class = NotificationSerializer
def list(self, request):
queryset = Notification.objects.all()
return Response(NotificationSerializer(queryset, many=True).data)

Related

How to put annotation in rest-auth user

I am trying to add some annotation in authenticated User using rest-auth
Here is my code in serializers.py
class CustomUserSerializer(UserDetailsSerializer):
test = serializers.IntegerField()
class Meta:
model=User
fields='__all__'
And here is my code in views.py
class CustomUserView(UserDetailsView):
queryset= User.objects.annotate(test=Sum('logs__work_hours'))
serializer_class = CustomUserSerializer
but I am having this error after running the system
**
Got AttributeError when attempting to get a value for field test on
serializer CustomUserSerializer. The serializer field might be named
incorrectly and not match any attribute or key on the User instance.
Original exception text was: 'User' object has no attribute 'test'.
**
In this context, queryset is ignored.
Why? In your program, you're subclassing UserDetailsView from django-rest-auth. That implements get_object() like so:
def get_object(self):
return self.request.user
With no reference to the queryset variable.
You need to override get_object() in CustomUserView, copying the above implementation, and setting a test attribute on the user.
def get_object(self):
user = self.request.user
user.test = ... # put query to get value of test here
return user

Django REST Framework custom permission causes JSON Parse error

I have the following classes:
class SimpleArtistTrackSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = ArtistTrack
fields = (...fields...)
class ArtistProfileSerializer(serializers.HyperlinkedModelSerializer):
tracks = SimpleArtistTrackSerializer(many=True, required=False)
class Meta:
model = ArtistProfile
fields = (..fields...)
def create(self, validated_data):
user = get_user_or_explode(self.context)
profile = ArtistProfile.objects.create(**validated_data)
profile.owners.add(user)
class IsAnOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
return True
class ArtistProfileViewSet(viewsets.ModelViewSet):
queryset = ArtistProfile.objects.all()
serializer_class = ArtistProfileSerializer
permission_classes = [IsAnOwnerOrReadOnly]
Just by adding this permission class, all my requests now return a Parse error:
{"detail":"JSON parse error - Extra data: line 1 column 19 (char 18)"}
Debugging shows that the object permission method is running. Using a standard DRF IsAuthenticated permission does not cause this error, and removing the permission_classes gets rid of this error. For some reason, using the custom BasePermission seems to be causing.
Am I missing something? How can I debug this better to find the problem?

Need help implementing a base serializer in DJango

I am trying to implement a base serializer and I am following the "http://www.django-rest-framework.org/api-guide/serializers/#baseserializer".
My urls.py:
url(r'^auth/myuser/(?P<pk>[0-9]+)/profile/$', UserProfileViewSet.as_view({'patch':'update'}), name='user-profile'),
Views.py:
class UserProfileViewSet(viewsets.ModelViewSet):
queryset = CustomUser.objects.all()
serializer_class = UserProfileSerializer
def get(self,request,pk,*args,**kwargs):
user_instance = CustomUser.objects.get(pk=pk)
dashboard_data = UserProfileSerializer(user_instance)
content = {'result': dashboard_data}
return Response(content)
Serializers.py:
class UserProfileSerializer(serializers.BaseSerializer):
def to_representation(self,obj):
return{
'email':obj.email,
'first_name':obj.first_name,
'last_name':obj.last_name,
'date_of_birth':obj.date_of_birth,
'gender':obj.get_gender_display(),
'location':obj.location,
'calling_code':obj.callingcode,
'phone_primary':obj.phone_primary,
'phone_secondary':obj.phone_secondary,
'website':obj.website
}
But I am getting the error "User object is not JSON serializable", and I don't find any attributes of the User obj that are not serializable.
I already found some answers on SO, but I don't find any similar steps in the django rest framework api guide. So looking for a solution that is in sync with the api-guide.
Serializer:
It seems what you need is a ModelSerializer
The ModelSerializer class provides a shortcut that lets you automatically create a Serializer class with fields that correspond to the Model fields.
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = CustomerUser
fields = '__all__'
I have set fields attribute to __all__ to indicate that all the fields in the model are serialized. But, you can also specify which fields to include exaclty:
fields = ('email', 'first_name', 'last_name',) # etc.
ViewSet:
The second point is about the ModelViewSet where you don't really need to implement the get method because it is already implemented for you. You just need to declare the queryset and the serializer_class just like you have already done:
class UserProfileViewSet(viewsets.ModelViewSet):
queryset = CustomUser.objects.all()
serializer_class = UserProfileSerializer
I guess you'll have to render the response in JSON format before returning it.
class JSONResponse(HttpResponse):
"""
An HttpResponse that renders its content into JSON.
"""
def __init__(self, data, **kwargs):
content = JSONRenderer().render(data)
kwargs['content_type'] = 'application/json'
super(JSONResponse, self).__init__(content, **kwargs)
class UserProfileViewSet(viewsets.ModelViewSet):
queryset = CustomUser.objects.all()
serializer_class = UserProfileSerializer
def get(self,request,pk,*args,**kwargs):
user_instance = CustomUser.objects.get(pk=pk)
dashboard_data = UserProfileSerializer(user_instance)
content = {'result': dashboard_data}
return JSONResponse(content, status=200)
You'll need following imports for this,
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
If this doesn't work just try passing dashboard_data to the JSONResponse function

Cannot POST using Rest API

So I can retrieve my data perfectly fine but when I try to post I get
{"detail":"Method \"POST\" not allowed."}
views.py
class ClubFullList(generics.ListAPIView):
serializer_class = ClubSerializer
def get_queryset(self):
return Club.objects.all()
class ClubList(generics.ListAPIView):
serializer_class = ClubSerializer
def get_queryset(self):
username = self.kwargs['username']
return Club.objects.filter(abv=username)
models.py
class Club(models.Model):
name = models.CharField(max_length=255)
abv = models.CharField(max_length=255)
serializers.py
class ClubSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Club
fields = ['name', 'abv']
How can I solve this?
You are sending POST request on an endpoint which only allows GET request.
ListAPIView is a read-only generic view. To create model objects using POST request, use CreateAPIView or ListCreateAPIView.
From the docs for ListApiView:
Used for read-only endpoints to represent a collection of model instances.
If you want to post to your endpoint, you'll need to use a different view class.

How to include in queryset details fields of a foreign key (django and rest_api)

I use rest_api in django in order to display a queryset of "chats".
I tried to get it done for a while, without success...
in angularjs controller I call a function which do the following:
$scope.conversations = $http.get('/api/chats/').then(function(response){
return response.data;
});
in urls.py of the rest_api app I put this:
url(r'^chats/$', login_required(views.chatsViewSet.as_view()) ),
in view.py of the rest_api I put this:
from rest_framework.generics import ListCreateAPIView
from serializers import ChatsSerializer
class ChatsViewSet(ListCreateAPIView):
serializer_class = ChatsSerializer
def get_queryset(self):
return Message.objects._folder(('sender', 'recipient'), {},order_by='-sent_at')
and in serializers.py in the rest_api I put this:
from postman.models import Message
from rest_framework import serializers
class ChatsSerializer(serializers.ModelSerializer):
class Meta:
model = Message
fields = ('id', 'sender', 'recipient','thread','subject','moderation_reason','body')
ordering =['-thread']
The 'sender' and the 'recipient' fields in the Message of postman model are foreign keys.
Here is the definition of sender, for instance, in the Message Model in postman:
sender = models.ForeignKey(get_user_model(), related_name='sent_messages', null=True, blank=True, verbose_name=_("sender"))
I would like that the queryset in rest_api include not only the ID of the sender but also its username field...
I tried to read some posts like using 'extra' in rest_api but I didn't figure it out.
I will be grateful if somebody could write me explicit instruction how to do it...
You can make a property on your model:
class Message(models.Model):
# some code...
#property
def sender_name(self):
return self.sender.name # username or whatever
Then is you serializer you create a custom field:
class ChatsSerializer(serializers.ModelSerializer):
# some code...
sender_name = serializers.Field(source = 'sender_name') # source = 'name of property or method' you dont have to pass it in this example becouse model property has the same name as this serializer attribute
class Meta:
fields = ('sender_name', 'id', 'sender', 'recipient','thread','subject','moderation_reason','body')
Now you have an 'sender_name' object in your JSON response.
That's just one method I hope it helps :)
The second one is to add an UserModelSerializer:
class UserModelSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
class ChatsSerializer(serializers.ModelSerializer):
# some code...
sender = UserModelSerializer()
recipient = UserModelSerializer()
class Meta:
fields = ('sender_name', 'id', 'sender', 'recipient','thread','subject','moderation_reason','body')
Retriving is ok. But creating and updating with it is a hard piece of coding.
You can always create an Angular service for your 'sender' object and after reciving your chat messages with only foreign keys, request data from a 'UserModelView' with those 'FK' and bind them together. That's a third method.

Categories