Django REST User Creation/Authentication - python

This question is based on the one here. I am setting up a Django REST Framework for my web app and am trying to set up User accounts. Based on the REST documentation, they put all of their account code in their example in the main project directory and a separate application so did that as well. Here is what I have:
urls.py
from django.contrib import admin
from django.urls import include, path
from django.conf.urls import url
from rest_framework import routers
from . import views
router = routers.DefaultRouter()
router.register('users', views.UserViewSet)
urlpatterns = [
path('admin/', admin.site.urls),
url('', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
]
serializers.py
from django.contrib.auth.models import User
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True)
def create(self, validated_data):
user = User.objects.create(
username=validated_data['username']
)
user.set_password(validated_data['password'])
user.save()
return user
class Meta:
model = User
# Tuple of serialized model fields (see link [2])
fields = ( "id", "username", "password", )
views.py
from rest_framework import viewsets, permissions
from rest_framework.generics import CreateAPIView
from django.contrib.auth.models import User
from .serializers import UserSerializer
# Create your views here.
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [permissions.IsAuthenticated]
class CreateUserView(CreateAPIView):
model = User
permission_classes = [
permissions.AllowAny
]
serializer_class = UserSerializer
I have tried using the Boomerang REST Client in Chrome to POST data to this API, but it always returns a 403 Error saying "Invalid username/password." Specifically I am POSTing to http://127.0.0.1:8000/users/create/ with a Query String and 2 parameters: username and password. I also tried sending it as JSON and it returned the same. Any help would be appreciated.

It doesn't look like CreateUserView was registered in your urls.py. You should be able to register it and access it normally. I think this should work for you:
urlpatterns = [
...
url(r'^users/create/', views.CreateUserView.as_view()),
]
That said, I'd like to suggest adding an extra action for your UserViewSet instead:
# Create your views here.
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [permissions.IsAuthenticated
#action(methods=['post'], detail=False, permission_classes=[permissions.AllowAny])
def register(self, request, *args, **kwargs):
# This logic was taken from the `create` on `ModelViewSet`. Alter as needed.
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
Then you should be able to post via /users/register/. You can also specify your own url name and path on the decorator.

Maybe you are posting in the wrong url, try POST the same on http://127.0.0.1:8000/users/,
because ModelViewSet adds POST, PATCH, PUT, DELETE and GET methods automatically.
Also because you are asking for authentication (permission_classes = [permissions.IsAuthenticated]), you should send the headers for this in the request. There is a tutorial for this in the DRF site (https://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/)

based on django-rest-framework documents it's better to use viewset for create user api. therefor you need to send a POST request to http://127.0.0.1:8000/api-auth/users and no need to CreateUserView function.
But if you want to have a custom user create api do you need something like below:
class UserViewSet(viewsets.ModelViewSet):
"""
A viewset that provides the standard actions
"""
queryset = User.objects.all()
serializer_class = UserSerializer
#action(detail=True, methods=['post'], permission_classes=[permissions.AllowAny])
def create_user(self, request, pk=None):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
To have custom serializers in your ViewSet you can specify them in get_serializer_class function in your ViewSet like this:
class UserViewSet(viewsets.ModelViewSet):
# example viewset
def get_serializer_class(self):
if self.action == 'list':
return ListUserSerializer
elif self.action == 'create':
return CreateUserSerializer
elif self.action == 'update':
return UpdateUserSerializer
return DetailUserSerializer

Related

self.request.user not returning in queryset

In my views, queryset is returning all the users when I want it to be only returning the user that is currently logged. I have a get self method which has the serializer set to the user but it is not being used. When I tried get_queryset, self.request.user still doesn't return the user.
views.py:
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from rest_framework import status
from rsm_app.api.v1 import serializer as serializers
from rsm_app.users.models import User
class CurrentUserView(viewsets.ModelViewSet):
permission_classes = (IsAuthenticated,)
serializer_class = serializers.UserSerializer
#queryset = User.objects.filter(name=request.user.name)
def get_queryset(self):
return self.request.user
def put(self, request):
serializer = serializers.UserSerializer(
request.user, data=request.data)
if request.data and serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response({}, status=status.HTTP_400_BAD_REQUEST)
Url.py:
from rest_framework import routers
from django.urls import path, re_path, include
from graphene_django.views import GraphQLView
from rsm_app.api.v1 import views
app_name = "api.v1"
# Routers provide an easy way of automatically determining the URL conf.
router = routers.DefaultRouter()
router.register(r"user", views.CurrentUserView, basename="user")
# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
path("graphql", GraphQLView.as_view(graphiql=True)),
re_path(r"^", include(router.urls)),
re_path(r"user/", views.CurrentUserView, name='user'),
re_path(r"^api-auth/", include("rest_framework.urls",
namespace="rest_framework")),
]
Edit FIXED: It was a session token not being saved issue.
They mentioned in the docs that you could access the current user by user = self.get_object()
You could see the full example from the official docs.
Can you try with:
queryset = User.objects.filter(name=request.user.name).first()
or
queryset = User.objects.get(name=request.user.name)

Authentication credentials weere not provided when signing up

Building Django APIs to help me handle both the signup and login process for my React app.
I am able to successfully login (get an access and refresh token from Django upon login), but having difficulties connecting my React signup process to Django to create a new user.
This is the error I am getting on my console:
POST http://127.0.0.1:8000/users/ 401 (Unauthorized)
onSubmit # Signup.js:32
and on my network:
Authentication credentials were not provided.
This is what I have in my serializers.py
from django.contrib.auth.models import User
from rest_framework import serializers
class RegisterSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'email', 'password']
extra_kwargs = {
'password': {'write_only':True},
}
def create(self,validated_data):
user = User.objects.create_user(validated_data['username'],
password=validated_data['password'],
email=validated_data['email'])
return user
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
class UserSerializerWithToken(serializers.ModelSerializer):
token = serializers.SerializerMethodField()
password = serializers.CharField(write_only=True)
class PasswordSerializer(serializers.Serializer):
old_password = serializers.CharField(required=True)
new_password = serializers.CharField(required=True)
accounts.urls.py:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('signup.urls')),
]
signup.urls.py
from django.contrib import admin
from django.urls import path, include
from rest_framework import routers
from . import views
from rest_framework_simplejwt.views import (TokenObtainPairView, TokenRefreshView, TokenVerifyView)
from .api import RegisterApi
router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
urlpatterns = [
path('', include(router.urls)),
path('admin/', admin.site.urls),
path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
path('api/register', RegisterApi.as_view()),
]
and finally signup.views.py:
from django.contrib.auth.models import User, Group
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import viewsets, status
from rest_framework import permissions
from .serializers import RegisterSerializer, PasswordSerializer
class UserViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = User.objects.all().order_by('-date_joined')
#changed name from UserSerializer to RegisterSerializer
serializer_class = RegisterSerializer
# permission_classes = [permissions.IsAuthenticated]
#action(detail=True, methods=['POST'])
def set_password(self, request, pk=None):
user = self.get_object()
serializer = PasswordSerializer(data=request.data)
if serializer.is_valid():
user.set_password(serializer.validated_data['new_password'])
user.save()
return Response({'status': 'password set'})
else:
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
Here is my api.py
from rest_framework import generics, permissions, mixins
from rest_framework.response import Response
from .serializers import RegisterSerializer, UserSerializer
from django.contrib.auth.models import User
from rest_framework.permissions import AllowAny
#Register API
class RegisterApi(generics.GenericAPIView):
serializer_class = RegisterSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
return Response({
"user": UserSerializer(user, context=self.get_serializer_context()).data,
"message": "User Created Successfully. Now perform Login to get your token",
})
def get_permissions(self):
if self == 'create':
return [AllowAny()]
else:
return super().get_permissions()
def get_authenticators(self):
if self == 'create':
return []
else:
return super().get_authenticators()
I am posting to this URL in the signup process:
'http://127.0.0.1:8000/api/register'
The reason for this is that you have a default authentication/permission scheme which applies to all your views. However, your signup process (perhaps understandably) is not authenticated, since there is not user account to authorize at this point.
One way to get around this would be to allow anonymous signup by not requiring permissions to create a user, assuming that is a desired behavior for your app.
You can do this by overriding get_permissions on the ViewSet, making the required permission classes to be AllowAny only when the viewset action is create.
from rest_framework.permissions import AllowAny
class UserViewSet(viewsets.ModelViewSet):
...
def get_permissions(self):
"""
dont require permissions for create actions
otherwise, follow normal required permissions
"""
if self.action == 'create':
return [AllowAny()]
else:
return super().get_permissions()
Alternatively, you can develop an authentication/authorization mechanism for creating new users. For example, you may choose to explore email-based token authentication for your user signup process.
You may also need to override get_authenticators(self):
...
def get_authenticators(self):
if self.action == 'create':
return []
else:
return super().get_authenticators()

Django rest framework : Prevent one user from deleting/Editing/Viewing other users in ModelViewSet

I was using Django users model for my Django rest framework. For this I used Django's ModelViewSet for my User class.
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
Serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'password']
extra_kwargs = {
'password' : {
'write_only':True,
'required': True
}
}
def create(self, validated_data):
user = User.objects.create_user(**validated_data)
Token.objects.create(user=user) # create token for the user
return user
But currently from postman when I make the request using the token of one user to view, delete, edit other users
http://127.0.0.1:8000/api/users/4/
Its able to edit/delete/view other users. I don't want that to happen and one user can make request on itself only is all I want.
This is my apps urls.py
urls.py
from django.urls import path, include
from .views import ArticleViewSet, UserViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register('articles', ArticleViewSet, basename='articles')
router.register('users', UserViewSet, basename = 'users')
urlpatterns = [
path('api/', include(router.urls)),
]
How can I prevent one user from accessing other users when they make GET/POST/PUT/DELETE request.
EDIT 1: After adding the IsOwnerOfObject class as provided in he answers below, now when I am requesting the detail of the user himself, I am getting
Authentication credentials were not provided.
Building from Ene's answer, authentication and permission classes needs to be provided.
Create a file named permissions.py.
from rest_framework import permissions
class IsOwnerOfObject(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
return obj == request.user
next add the permission and authentication class to ModelViewSet:
from api.permissions import IsOwnerOfObject
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsAuthenticated, IsOwnerOfObject]
authentication_classes = (TokenAuthentication,)
Create a file named permissions.py.
from rest_framework import permissions
class IsOwnerOfObject(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
return obj == request.user
next add the permission class to you ModelViewSet:
from yourapp.permissions import IsOwnerOfObject
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsOwnerOfObject, <other permission classes you want to use>]
More info here:
https://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/#object-level-permissions
If you want to disable delete completely (Which is probably correct since if you want to "delete" a User you should deactivate it instead.) Then you can replace your view with this:
from rest_framework import viewsets
from rest_framework import generics
class UserViewSet(
generics.CreateModelMixin,
generics.ListModelMixin,
generics.RetrieveModelMixin,
generics.UpdateModelMixin,
generics.viewsets.GenericViewSet
):
queryset = User.objects.all()
serializer_class = UserSerializer
And then you can use Ene Paul's answer to limit who can edit.
Another all-in-one solution could be to use a queryset filter to directly narrow the queryset results. This will prevent an user to delete other users, but also prevent any unauthorized retrieving or listing as the only accessible data will be the user itself only.
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
"""
The get_queryset function is used to get the queryset of the user data corresponding to the logged in user.
It is called when the view is instantiated, and it returns a list containing this user only.
"""
# after get all products on DB it will be filtered by its owner and return the queryset
owner_queryset = self.queryset.filter(username=self.request.user.username)
return owner_queryset
And this can also be used with other objects to allow retrieving only data related to this user.

Creat REST Api Using Django Rest Framework

I am trying to create a Restful Api for following methods to run jenkins jobs to run on saucelabs. I wanna queue jobs using restful API. I am using Django Restful Framework.
CreateMethod :
Accepts two fileds: ProjectName and URL
and returns a Token ID.
VerifyStatus:
Accepts Token ID and returns three fields. TokenID, running:True/False and
no_of_jobs: integervalue (0 if Not specified)
relseaseMethod:
Accepts release token call and returns true if success.
I am new to Restful API with, I am trying to run Jenkins job on sauce lab and queue them using a restful api on python Djano restframework. Guide me through.
Views.py
class RegisterTestMethodView(APIView):
authentication_classes = [SessionAuthentication, TokenAuthentication, BasicAuthentication]
permission_classes = [IsAuthenticated] #No access (not even read if not authorized)
def post(self, request, format=None):
serializer = RegisterTestMethodSerializers(data=request.data)
if serializer.is_valid():
serializer.save()
return Response({'tokenid':serializer.data['id']}, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class CheckStatusView(APIView):
def get_object(self, pk):
try:
return Jobs.objects.get(pk=pk)
except Jobs.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
snippet = self.get_object(pk)
serializer = RegisterTestMethodSerializers(snippet)
return Response({"tokenid":serializer.data["id"], "Runtestnow" : False, "VMcount" : 0 })
class ReleaseTokenView(APIView):
def get_object(self, pk):
try:
return Jobs.objects.get(pk=pk)
except Jobs.DoesNotExist:
raise Http404
def delete(self, request, pk, format=None):
snippet = self.get_object(pk)
snippet.delete()
return Response(data={'Deleted':True}, status=status.HTTP_204_NO_CONTENT)
Serailizers.py
rom rest_framework import serializers
from .models import Jobs
from random import random
RegisterTestMethodSerializers(serializers.HyperlinkedModelSerializer):
class Meta:
model = Jobs
fields = ('id','name','url')
Models.py
from django.db import models
# Create your models here.
class Jobs(models.Model):
name = models.CharField(max_length=100)
url = models.URLField()
def __str__(self):
return self.name
Urls.py
from django.urls import path, include
from . import views
from .views import (RegisterTestMethodView,
RegisterTestMethodViewDetail,
CheckStatusView,
ReleaseTokenView
)
from rest_framework import routers
from rest_framework.authtoken.views import obtain_auth_token
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
urlpatterns = [
path('', include(router.urls)),
path('registertestmethod/',RegisterTestMethodView.as_view()),
path('registertestmethod/<int:pk>/',
RegisterTestMethodViewDetail.as_view()),
path('checkstatus/<int:pk>', CheckStatusView.as_view()),
path('releasetoken/<int:pk>', ReleaseTokenView.as_view()),
]
I have addded my Code here. I have other classes and function in my project as well.I tried to delete all of those. You might see extra import as a result of that. My code does following things.
POST --> http://localhost:8000/registertestmethod/
{
"name": "Name",
"url": "https://www.google.com"
}
returns
{
"tokenid": 32 #unique token ID return
}
This tokenid should be long I am using id as of now.
GET --> http://localhost:8000/checkstatus/32
is returning
{
"tokenid": 32, #unique tokenid refering to register info
"Runtestnow": false, #if job is running
"VMcount": 0 # number of VM used in sauce lab by the Jobs
}
DELETE --> http://localhost:8000/releasetoken/32 #should delete the jobs after done.
is deleting and returning
{
"Deleted": true
}
I want it to be dynamic and store info in database. Token should return everything on checkstatus.
You can use the ModelViewSet approach for this, this here is a very simple example for an API endpoint.
views.py
from rest_framework.viewsets import ModelViewSet
from api.serializers import SaucelabsSerializer
from rest_framework.response import Response
class SaucelabModelViewSet(ModelViewSet):
serializer_class = SaucelabSerializer
queryset = Sauselab.objects.all()
http_method_names = ['get', 'head', 'options', 'post']
def create(self, request):
pnam = request.data.get('project_name', None)
url = request.data.get('url', None)
if pnam and url:
# do something here
return Response({'success': 'Your success message'}, status=status.HTTP_200_OK)
else:
return Response({"error": "Your error message"}, status=status.HTTP_400_BAD_REQUEST)
serializers.py
from rest_framework.serializer import ModelSerializer
from appname.models import Saucelab
class SaucelabSerializer(ModelSerializer):
class Meta:
model = Saucelab
fields = '__all__'
appname/models.py
from django.db import models
class Saucelab(models.Model)
project_name = models.CharField(max_length=255)
url = models.URLField()
urls.py
from rest_framework.routers import DefaultRouter
from api import views
router = DefaultRouter()
router.register('your-endpoint-name', views.SaucelabModelViewSet, basename='your-url-name')
urlpatterns = []
urlpatterns += router.urls
This is a very basic example, where you create a model called saucelab which has the two fields you require, ie.project_name and url.
We create a app called api with two files inside it that aren't auto generated, serializers.py and urls.py. We create the most basic model serializer and ask it to serialize all fields of model Saucelab. Then we create a simple modelviewset which out of the box gives you a CRUD functionality. You can override the create method if you need to run some specific conditions otherwise dont override any methods and just make request to the endpoint with appropriate HTTP methods.
Here are a few links you can read
https://www.django-rest-framework.org/api-guide/viewsets/#modelviewset
https://www.django-rest-framework.org/api-guide/serializers/#modelserializer
To generate a random token
from django.utils.crypto import get_random_string
print(get_random_string(length=25))
output
u'rRXVe68NO7m3mHoBS488KdHaqQPD6Ofv'

Django Rest Framework - adding JOIN endpoints

I'm making a REST API with Django Rest Framework (DRF) which has the following endpoints:
/users/
/users/<pk>/
/items/
/items/<pk>/
but I'd like to add the endpoint:
/users/<pk>/items/
which would of course return the items that belong (have a foreign key) to that user.
Currently my code is:
#########################
##### myapp/urls.py #####
#########################
from django.conf.urls import url, include
from rest_framework.routers import DefaultRouter
from rest_framework.decorators import api_view, renderer_classes
from rest_framework import response, schemas
from rest_framework_swagger.renderers import OpenAPIRenderer, SwaggerUIRenderer
from myapp.views import ItemViewSet, UserViewSet
# Create a router and register our viewsets with it.
router = DefaultRouter()
router.register(r'users', UserViewSet)
router.register(r'items', ItemViewSet)
#api_view()
#renderer_classes([OpenAPIRenderer, SwaggerUIRenderer])
def schema_view(request):
generator = schemas.SchemaGenerator(title='My API')
return response.Response(generator.get_schema(request=request))
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
]
##########################
##### myapp/views.py #####
##########################
from django.contrib.auth import get_user_model
from rest_framework import viewsets, permissions
from myapp.serializers import MyUserSerializer, ItemSerializer
from myapp.models import Item
class UserViewSet(viewsets.ReadOnlyModelViewSet):
queryset = get_user_model().objects.all()
serializer_class = MyUserSerializer
permission_classes = (permissions.IsAuthenticated,)
class ItemViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Item.objects.all()
serializer_class = ItemSerializer
permission_classes = (permissions.IsAuthenticated,)
################################
##### myapp/serializers.py #####
################################
from rest_framework import serializers
from django.contrib.auth import get_user_model
from myapp.models import Item
class MyUserSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = ('pk', 'email',)
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = ('pk', 'name',)
Is there a good way to add this endpoint in DRF, given how I'm using DRF?
I could just add a function view in urls.py like so:
from myapp.views import items_for_user
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^users/(?P<pk>[0-9]+)/items/$', items_for_user),
]
but I want to leverage DRF, get the browsable API, and make use of ViewSets instead of coding one-off function views like this.
Any ideas?
Took me a while to figure this out. I've been using view sets, so I'll give this answer within that setting.
First, URLConf and registered routes remain unchanged, i.e.,
router = DefaultRouter()
router.register(r'users', UserViewSet)
router.register(r'items', ItemViewSet)
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^api-auth/',
include('rest_framework.urls', namespace='rest_framework')
),
]
Your items will still be at /items/<pk>/, with permissions crafted for each of them depending on who requests them, by creating custom permissions, for example:
class IsItemOwnerPermissions(permissions.DjangoObjectPermissions):
"""
The current user is the owner of the item.
"""
def has_object_permission(self, request, view, obj):
# A superuser?
if request.user.is_superuser:
return True
# Owner
if obj.owner.pk == request.user.pk:
return True
return False
Next, for /user/<pk>/items/ you need to define #detail_route, like this:
class UserViewSet(viewsets.ReadOnlyModelViewSet):
# Your view set properties and methods
#detail_route(
methods=['GET', 'POST'],
permission_classes=[IsItemOwnerPermissions],
)
def items(self, request, pk=None):
"""
Returns a list of all the items belonging to `/user/<pk>`.
"""
user = get_user_model().objects.get(pk=pk)
items = user.items.all()
page = self.paginate_queryset(items)
if page is None:
serializer = ItemSerializer(
objs, context={'request': request}, many=True
)
return Response(serializer.data)
else:
serializer = ItemSerializer(
page, context={'request': request}, many=True
)
return self.get_paginated_response(serializer.data)
A detailed route named xyz corresponds to the route user/<pk>/xyz. There are also list routes (#list_route); one named xyz would correspond to user/xyz (for example user/add_item).
The above structure will give you: /user, /user/<pk>, user/<pk>/items, /items, and /items/<pk>, but not (as I wrongly tried to achieve) user/<user_pk>/items/<items_pk>. Instead, user/<pk>/items will give you a list of user's items, but their individual properties will still be accessible only via /items/<pk>.
I just got this to work on my project, and the above code is a quick adaptation to your case. I hope it helps you, but there might still be problems there.
Update: What you wanted can be done using custom hyperlinked fields. I didn't try it (yet), so I cannot say how to use these, but there are nice examples on that link.

Categories