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?
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?
so I am trying to create a todo app using the Django rest Framework, and have my models.py as...
class Task(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
task = models.CharField(max_length=150)
date_created = models.DateTimeField(auto_now=True)
completed = models.BooleanField(default=False)
What I want is that when a user inputs their task, I want the completed attribute to be hidden to the user and automatically set that to false. But then allow the user to change to true or false later on.
With my current serializer.py..
class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = ('id', 'task', 'date_created', 'completed')
extra_kwargs = {
"date_created":{"read_only":True},
# 'completed':{"read_only":True}
}
And my views.py...
class ListView(viewsets.ModelViewSet):
serializer_class = serializers.TaskSerializer
permission_classes = (IsAuthenticated, permissions.ViewOwnTask)
def get_queryset(self):
return Task.objects.filter(user=self.request.user)
def perform_create(self, serializer):
serializer.save(user=self.request.user)
In my serializer.py, the commented code 'completed':{"read_only":True} allows the user to edit the field and write on that field when uploading, as shown in this image
However when I uncomment the 'completed':{"read_only":True} field, the option of writing the completed field disappears however I am not allowed to edit that field.
Is there anything such as {"edit_only":True}, allowing the user to edit the field only. If you get my point.
Any help would be greatly appreciated.
Thanks!
EDIT
My urls.py is...
router = DefaultRouter()
router.register('task', views.ListView, basename='task')
urlpatterns = [
path('', include(router.urls)),
]
2nd EDIT
Hey #PrakashS now I get this kind of output which I don't want, I want only the completed field to be edited.
Use different serializers for adding and editing task:
from rest_framework import serializers
from tasks.models import Task
class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = '__all__'
extra_kwargs = {
'completed':{"read_only":True}
}
class TaskEditSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = '__all__'
And in views you can select serializer based on the add or edit view:
from rest_framework.generics import RetrieveUpdateDestroyAPIView, ListCreateAPIView
from rest_framework.permissions import IsAuthenticated
from .models import Task
from .permissions import IsOwnerOrReadOnly
from .serializers import TaskSerializer, TaskEditSerializer
class ListCreateTaskAPIView(ListCreateAPIView):
serializer_class = TaskSerializer
queryset = Task.objects.all()
permission_classes = [IsAuthenticated]
def perform_create(self, serializer):
serializer.save(user=self.request.user)
class RetrieveUpdateDestroyTaskAPIView(RetrieveUpdateDestroyAPIView):
serializer_class = TaskEditSerializer
queryset = Task.objects.all()
permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
urls.py file:
from django.urls import path
from . import views
urlpatterns = [
path('', views.ListCreateTaskAPIView.as_view(), name='get_post_tasks'),
path('<int:pk>/', views.RetrieveUpdateDestroyTaskAPIView.as_view(), name='get_delete_update_task'),
]
permissions.py file:
from rest_framework import permissions
from rest_framework.exceptions import PermissionDenied
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Custom permission to only allow creator of an object to edit it.
"""
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True
# Write permissions are only allowed to the creator of the movie
return obj.user == request.user
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')
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'