I am trying to use the django rest framework to expose my models as APIs.
serializers
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
viewset
class UserViewSet(viewsets.ModelViewSet):
"""
API end point for User details and list
"""
serializer_class = UserSerializer
queryset = User.objects.all()
routers
router.register(r'users',views.UserViewSet)
While this exposes /users/ and users/, I want my URLs to include a user-slug as well, something like /users/1/xyz-user-name.
Has anyone solved this problem? Does this need changes in both the viewset and router code or is it something that can be configured only in the router code? MY "slug" isn't really used for determining url routing, it is only for URL readability.
Any pointers?
I was able to get this to work by using the approach posted here.
django-rest-framework HyperlinkedIdentityField with multiple lookup args
The second error I was receiving was becuase I was including the url definition inside the meta section. It should be before the meta section instead. I also had to specify the lookup field in the viewset code. Here are the relevant parts of my code.
urls.py
from user.views import UserViewSet
user_list = UserViewSet.as_view({'get':'list'})
user_detail = UserViewSet.as_view({'get':'retrieve'})
urlpatterns= [
url(r'^users/$', user_list, name='user-list'),
url(r'^user/(?P<id>\d+)/(?P<slug>[-\w\d]+)/$', user_detail, name='user-detail'),
url(r'^api-auth/', include('rest_framework.urls',namespace = 'rest_framework'))
]
views.py:
class UserViewSet(viewsets.ModelViewSet):
"""
API end point for user details and user list
"""
lookup_field = 'id'
serializer_class = UserSerializer
queryset = user.objects.all()
serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
url = ParameterisedHyperlinkedIdentityField(view_name='user-detail', lookup_fields=(('id', 'id'), ('slug', 'slug')), read_only=True)
class Meta:
model = user
fields = ('url','name','cover_photo')
You should set the lookup_field property in the serializers and viewsets.
In the serializers.py:
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'email', 'user_slug')
lookup_field = 'user_slug'
extra_kwargs = {
'url': {'lookup_field': 'user_slug'}
}
In the viewsets.py:
class UserViewSet(viewsets.ModelViewSet):
serializer_class = UserSerializer
queryset = User.objects.all()
lookup_field = ('user_slug')
Related
I want to pass the username of the logged in user from ListCreateAPIView to ModelSerializer to use object with same PrimaryKey from Clients model as a default, but i don't understand how to do it correctly.
In views.py:
class CartAPIList(generics.ListCreateAPIView):
queryset = Clientcarts.objects.all()
serializer_class = CartSerializer
permission_classes = (IsAuthenticatedOrReadOnly, )
In serializers.py:
class CartSerializer(serializers.ModelSerializer):
client_id = serializers.HiddenField(default=Clients.objects.get(pk="username"))
class Meta:
model = Clientcarts
fields = '__all__'
Can you guys help me?
I have 3 models: User , Tag and Recipe.
User model is so basic and it's not important.
Here is the Tag model:
class Tag(models.Model):
name = models.CharField(max_length=255)
user = models.ForeignKey(settings.AUTH_USER_MODEL , on_delete=CASCADE)
And here is the Recipe model:
class Recipe(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL , on_delete=CASCADE)
title = models.CharField(max_length=255)
tags = models.ManyToManyField('Tag',)
I made endpoint for all of these models but there is one problem!
When I try to create a Recipe object, all of Tag objects will be listed, But I want to list just the logged in user tags.
Here is my Tag serializer:
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = models.Tag
fields = ('id' , 'name')
extra_kwargs = {
'id' : {
'read_only' : True,
}
}
And here is my Tag viewset:
class TagsView(viewsets.GenericViewSet,mixins.CreateModelMixin,mixins.ListModelMixin):
queryset = models.Tag.objects.all()
serializer_class = serializers.TagSerializer
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.IsAuthenticated,)
def get_queryset(self):
return self.queryset.filter(user=self.request.user)
def perform_create(self, serializer):
serializer.save(user=self.request.user)
How can I filter tags, so that all listed tag objects belong to the logged in user?
A queryset is created only once, that is when you start the server. So the queryset attribute is evaluated when the process starts that is when your page renders which causes the class TagsView view to execute.
A get_queryset method is called upon every request, which can be particularly useful if you want to adjust the query dynamically, which I think you are trying to do here.
So the get_queryset is a method that is supposed to be used in place of queryset variable as an alternative to it, providing more flexibility.
You really don't need both of them together, which might be causing your problem.
You can just do it this way.
class TagsView(RecipeAttrsView):
serializer_class = serializers.TagSerializer
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.IsAuthenticated,)
def get_queryset(self):
return models.Tags.objects.filter(user=self.request.user)
def perform_create(self, serializer):
serializer.save(user=self.request.user)
I have a viewset in rest framework that is not behaving like I would expect. If I login with a non-staff user and navigate to the api-url/users I can see all the users listed there.
The IsAuthenticated permission is working, because if I logout I get an error saying that I am not authenticated.
Am I using these permissions wrong? I have done the tutorial and looked through the docs, but I can't find anything to tell me why this shouldn't work
views:
class UserViewSet(viewsets.ModelViewSet):
"""Viewset for viewing users. Only to be used by admins"""
queryset = LangaLangUserProfile.objects.all()
serializer_class = UserSerializer
filter_backends = (filters.DjangoFilterBackend, )
filter_fields = '__all__'
permissions_classes = (permissions.IsAdminUser, )
class LanguageViewSet(viewsets.ReadOnlyModelViewSet):
"""Viewset for Language objects, use the proper HTTP methods to modify them"""
queryset = Language.objects.all()
serializer_class = LanguageSerializer
filter_backends = (filters.DjangoFilterBackend, )
filter_fields = '__all__'
permissions_classes = (permissions.IsAuthenticated, )
urls:
router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'language', views.LanguageViewSet)
serializers:
class UserSerializer(serializers.ModelSerializer):
"""Serializer for User objects"""
class Meta:
model = LangaLangUserProfile
fields = '__all__'
class LanguageSerializer(serializers.ModelSerializer):
"""Serializer for the Language model"""
class Meta:
model = Language
fields = '__all__'
depth = 2
Typo!
It's permission_classes, not permissions_classes.
About this part:
The IsAuthenticated permission is working, because if I logout I cget an error saysing that I am not authenticated.
I'm not sure why this is happening but I'd blame DEFAULT_PERMISSION_CLASSES in your Django settings - maybe you have IsAuthenticated specified there?
I've extensively researched this fairly common issue, but none of the fixes worked for me. I'm building a Django project in REST framework and want to use hyperlinked relations. The User can have many Cars and Routes, which are independent. A Route is a collection of Positions.
These are my serializers:
class CarSerializer(serializers.HyperlinkedModelSerializer):
user = serializers.Field(source='user.username')
class Meta:
model = Car
fields = ('url', 'make', 'year', 'car_model', 'user')
class PositionSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Position
fields = ('url', 'drive_route', 'timestamp', 'latitude', 'longitude', 'altitude','speed','heading', 'accuracy', 'altitude_accuracy')
class DrivingRouteSerializer(serializers.HyperlinkedModelSerializer):
position = serializers.HyperlinkedRelatedField(view_name='position', many=True)
user = serializers.Field(source='user.username')
class Meta:
model = DrivingRoute
fields = ('url', 'id', 'route', 'position', 'user')
class UserSerializer(serializers.HyperlinkedModelSerializer):
routes = serializers.HyperlinkedRelatedField(view_name='routes-detail', many=True)
car = serializers.HyperlinkedRelatedField(view_name='car-detail', many=True)
class Meta:
model = User
fields = ('url', 'username', 'routes', 'car')
And here are the views:
class CarViewSet(viewsets.ModelViewSet):
queryset = Car.objects.all()
serializer_class = CarSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def pre_save(self, obj):
obj.user = self.request.user
class DrivingRouteViewSet(viewsets.ModelViewSet):
queryset = DrivingRoute.objects.all()
serializer_class = DrivingRouteSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def pre_save(self, obj):
obj.user = self.request.user
class PositionViewSet(viewsets.ModelViewSet):
queryset = Position.objects.all()
serializer_class = PositionSerializer
class UserViewSet(viewsets.ReadOnlyModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
And, for what it's worth, the URLs. I am using the Default Router, just as in the Django REST Framwork tutorial.
router = DefaultRouter()
router.register(r'car', views.CarViewSet)
router.register(r'routes', views.DrivingRouteViewSet)
router.register(r'position', views.PositionViewSet)
router.register(r'users', views.UserViewSet)
Overall, this is almost exactly the same as in the tutorial. Loading the 'routes', 'car', and 'position' URLS works fine, but the 'users' URL throws the error "Could not resolve URL for hyperlinked relationship using view name 'routes-detail'."
The view_name should typically be [route]-detail for routers, where [route] is the name of the model you registered the ViewSet under.
In your case, the view_name should be position-detail, not just position. You are also using routes-detail instead of drivingroutes-detail, which is using the long name because your model is DrivingRoute and not Route. You can override this by setting a base_name (third parameter) when using register on the router.
router = DefaultRouter()
router.register(r'car', views.CarViewSet)
router.register(r'routes', views.DrivingRouteViewSet, "routes")
router.register(r'position', views.PositionViewSet)
router.register(r'users', views.UserViewSet)
You have the following views:
car-list
car-detail
drivingroute-list
drivingroute-detail
position-list
position-detail
user-list
user-detail
If we take for example the following route:
router.register(r'car', views.CarViewSet)
It means that it is accessible under http://yourapi.com/car/ for car-list and under http://yourapi.com/car/1/ for car-detail (where 1 is the id).
The view names get built out of the class name minus suffix ViewSet plus list or detail.
Change your code according to these rules and please try again.
Cheers!
I am following the quick Quick Start Tutorial(http://www.django-rest-framework.org/tutorial/quickstart#quickstart) Its possible to create/delete/update a user in database if we know its "id", But is it possible to do the same for a user with particular email ?
Please also suggest the modifications needed to make this possible and enable API to lookup by email like users/email.
Set the lookup_field and lookup_url_kwarg on your view or viewset that subclasses GenericAPIView. A basic example using a ModelViewSet and a SimpleRouter would look like this:
views.py:
class UserViewSet(viewsets.ModelViewSet):
lookup_field = 'email'
lookup_url_kwarg = 'email'
urls.py:
router = routers.SimpleRouter()
router.register(r'^users', UserViewSet)
urlpatterns = router.urls
If you are using HyperlinkedModelSerializer, you must set lookup_field in the serializer too.
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username')
lookup_field = 'email'