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
Related
I have a model like this:
class Professional(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
dummy_text = models.CharField(max_length=300)
a serializer like this:
class ProfessionalSerializer(serializers.ModelSerializer):
class Meta:
model = Professional
fields = '__all__'
and a view like this one:
class CreateProfessional(generics.CreateAPIView):
serializer_class = ProfessionalSerializer
The thing is, I need to pass the current authenticated user for a given request as the user for my serializer, I'm getting an error because obviously the user field is required as stated in my model, but I can't find an elegant way to do so, how could I go about it?
Set the user as a read_only_fields in the serializer meta. This will prevent accepting the user data from the payload.
class ProfessionalSerializer(serializers.ModelSerializer):
class Meta:
model = Professional
fields = '__all__'
read_only_fields = ["user"]
Then, override the perform_create(...) method of the view class
class CreateProfessional(generics.CreateAPIView):
serializer_class = ProfessionalSerializer
def perform_create(self, serializer):
serializer.save(user=self.request.user)
You can make validate method with like validate_user and do following code inside.
self.context['view'].request.user()
class CreateProfessional(generics.CreateAPIView):
serializer_class = ProfessionalSerializer
def create(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data, context={'request': request})
serializer.is_valid(raise_exception=True)
serializer.save()
class ProfessionalSerializer(serializers.ModelSerializer):
class Meta:
model = Professional
fields = '__all__'
def create(self, validated_data):
user = self.context['request'].user
like this you can get your current user
I found a very nice way to do it, I dont wan't to return custom responses, I will let that to the framework, what I did was the following in my view:
def get_serializer(self, *args, **kwargs):
# redefine method to parameterize the serializer
# while leaving the response handling to the
# framework
serializer_class = self.get_serializer_class()
kwargs['context'] = self.get_serializer_context()
kwargs['context']['user'] = self.request.user
return serializer_class(*args, **kwargs)
and in my serializer:
class EntitySerializer(serializers.ModelSerializer):
class Meta:
model = Entity
fields = '__all__'
extra_kwargs = {'user': {'required': False}}
def create(self, validated_data):
user = self.context['request'].user
entity = Entity.objects.create(user=user, **validated_data)
return entity
You can use ModelViewSet instead of single API Views. ModelViewSet handles all the crud operations automatically.
Let's say you have the model
class Professional(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
dummy_text = models.CharField(max_length=300)
and the serializer
class ProfessionalSerializer(serializers.ModelSerializer):
class Meta:
model = Professional
fields = '__all__'
So you can add a ModelViewSet
class ProfessionalViewSet(ModelViewSet):
queryset = Professional.objects.all()
permission_classes = [IsAuthenticated]
serializer_class = ProfessionalSerializer
this will handle all the crud operations for your model. Following example class will show you the methods that a viewset has.
class UserViewSet(viewsets.ViewSet):
"""
Example empty viewset demonstrating the standard
actions that will be handled by a router class.
If you're using format suffixes, make sure to also include
the `format=None` keyword argument for each action.
"""
def list(self, request):
pass
def create(self, request):
pass
def retrieve(self, request, pk=None):
pass
def update(self, request, pk=None):
pass
def partial_update(self, request, pk=None):
pass
def destroy(self, request, pk=None):
pass
these will handle all the actions. checkout the documentation for more
you'll also have to add the router to handle all the urls automatically which is also covered in the documentation (above link). Following is an example(urls.py).
from myapp.views import UserViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'users', UserViewSet, basename='user')
urlpatterns = router.urls
I need to return some computed value using request param.
It seems that I need to get access to request param from inside ModelSerializer.
How can I do this?
class PostSerializer(serializers.ModelSerializer):
owner = UserSerializer()
spot = SpotSerializer()
is_login_user_favorite = serializers.SerializerMethodField()
class Meta:
model = Post
fields = '__all__'
read_only_fields = ('owner',)
def get_is_login_user_favorite(self, validated_date):
return True # I need change this dynamically
class PostListAPIView(generics.ListAPIView):
serializer_class = serializers.PostSerializer
queryset = Post.objects.all().order_by('-pk')
permission_classes = [IsAuthenticated]
You can access it through context. Like this:
class PostSerializer(serializers.ModelSerializer):
...
def get_is_login_user_favorite(self, validated_date):
request = self.context['request'] # <-- Here
....
return value # I need change this dynamically
Basically, request is sent to Serializer through context automatically when you are using Generic API Views. This is also explained in the methods section(at the bottom named as 'other methods') of documentation.
I am assuming by the error in the title, once more here for clarity
'CityListViewSet' should either include a `serializer_class` attribute,
or override the `get_serializer_class()` method.
that my serializer isn't connected to my view, which in my code it should be. I'm not really sure where the bug is in this one. I wonder if any of you have seen something similar?
Here is the code.
Router:
router.register(r'city-list', CityListViewSet, base_name='city-list')
view:
class CityListViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Venue.objects.values('city').distinct()
serializer = CitySerializer(queryset, many=True)
ordering_fields = ('city',)
ordering = ('city',)
serializer:
class CitySerializer(serializers.ModelSerializer):
class Meta:
model = City
fields =('city',)
what is it that would be causing such an assertion error with the code seemly wired up correctly?
The exception says it itself. You need a serializer_class attribute. You have serializer.
Add this code snippet to your views.py file
class CityListViewSet(viewsets.ReadOnlyModelViewSet): # (viewsets.ModelViewSet)
serializer_class = CitySerializer
queryset = City.objects.values('city').distinct()
serializer = CitySerializer(queryset, many=True)
ordering_fields = ('city',)
ordering = ('city',)
error says you define a serializer attribute, you need to correct with writing
serializer_class attribute in your code,
serializer_class = yourCreatedSerializer
serializer = CitySerializer(queryset, many=True)
The above line should be replaced with
serializer_class = CitySerializer(queryset, many=True)
Here you used a different model name:
view:
class CityListViewSet(viewsets.ReadOnlyModelViewSet): #(viewsets.ModelViewSet)
queryset = City.objects.values('city').distinct()
serializer = CitySerializer(queryset, many=True)
ordering_fields = ('city',)
ordering = ('city',)
import -> from .serializers import TaskSerializers,CitySerializer
serializer:
class CitySerializer(serializers.ModelSerializer):
class Meta:
model = City
fields =('city',)
i got this error when declared post method in view and trying to send post data without serialize, if you are doing the request from javascript i solved it using JSON.stringify()
you have to override the user just add
from django.contrib.auth.models import User
from rest_framework.permissions import IsAdminUser
and in createViewList
permission_classes = [IsAdminUser]
Rename this to
serializer = CitySerializer(queryset, many=True)
This
serializer_class = yourCreatedSerializer
Your job is done
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.
I'm using the Django Rest Framework and I'd like to be able to add extra detail to the serializer when a single object is returned, which would be left out of the list view.
In the code below I add the celery_state field to the TestModelSerializer, but I'd only like this field to be added when its returning a single object, not when it's returning the list of TestModel data.
I've looked at the list_serializer_class option but it seems to just use the original model serializer so it will always still include the field even if I try to exclude from there.
What are my options?
class TestModelSerializer(serializers.HyperlinkedModelSerializer):
celery_state = serializers.CharField(source='celery_state', read_only=True)
class Meta:
model = TestModel
class TestModelViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows TestModels to be viewed or edited.
"""
authentication_classes = (SessionAuthentication, BasicAuthentication)
permission_classes = (IsAuthenticatedOrReadOnly,)
queryset = TestModel.objects.all()
serializer_class = TestModelSerializer
Since the serializer class (used by the viewsets) passes many argument, you can use that to control the fields output:
class TestModelSerializer(serializers.HyperlinkedModelSerializer):
# ...
def __init__(self, *args, **kwargs):
super(TestModelSerializer, self).__init__(*args, **kwargs)
if kwargs.get('many', False):
self.fields.pop('celery_state')
Inspired by #mariodev answer:
The other possibility is to override many_init static method in serializer. Acording comments in thie code (https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/serializers.py#L128 ) it is suggested variant.
from rest_framework import serializers
class ExtendedSerializer(serializers.Serializer):
...
#classmethod
def many_init(cls, *args, **kwargs):
kwargs['child'] = cls()
kwargs['child'].fields.pop('extractedFiled')
return serializers.ListSerializer(*args, **kwargs)
You can have an extra serializer called ExtendedTestModelSerializer which would contain the extra fields that you want.
After that, you can use the get_serializer_class method to decide which serializer is used based on request.action -
class TestModelViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows TestModels to be viewed or edited.
"""
authentication_classes = (SessionAuthentication, BasicAuthentication)
permission_classes = (IsAuthenticatedOrReadOnly,)
queryset = TestModel.objects.all()
# serializer_class = TestModelSerializer
get_serializer_class(self):
if self.request.action == 'list':
return TestModelSerializer
return ExtendedTestModelSerializer