Reputation: 12592
https://gist.github.com/ranman/3d97ea9054c984bca75e
Desired Behavior
User lookup happens by the username: /api/users/randall
Speaker lookup happens by the username as well: /api/speakers/randall
Constraints
Not all users are speakers. All speakers are users.
models.py
from django.contrib.auth.models import User
class Speaker(models.Model):
user = models.OneToOneField(User)
serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'email', 'groups')
lookup_field = 'username'
class SpeakerSerializer(serializers.HyperlinkedModelSerializer):
user = serializers.HyperlinkedRelatedField(
view_name='user-detail',
read_only=True,
lookup_field='username'
)
class Meta:
model = Speaker
lookup_field = 'user'
views.py
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
lookup_field = 'username'
class SpeakerViewSet(viewsets.ModelViewSet):
queryset = Speaker.objects.all().select_related('user')
serializer_class = SpeakerSerializer
lookup_field = "user"
I've tried various different invocations of lookup_field and serializer types to get this working to no avail. It may not be possible without a lot more code. I'm just wondering what direction I can take.
Upvotes: 23
Views: 34007
Reputation: 54
I stumbled across this question and none of the answers provided managed to solve my issue, the API request was still showing the 'pk' in the user field instead of the username.
I managed to get it working just adding a field to serializer.py
class PublicationSerializer(serializers.ModelSerializer):
#Field I had to add
author = serializers.SlugRelatedField(
many=False,
read_only=True,
slug_field='username'
)
class Meta:
model = Publication
fields = '__all__'
My viewsets.py
which didn't require anything new:
class PublicationViewSet(viewsets.ModelViewSet):
queryset = Publication.objects.all()
serializer_class = PublicationSerializer
permission_classes = [IsAuthenticated]
And my models.py
:
class Publication(models.Model):
title= models.CharField(max_length=100)
message = models.CharField(max_length=1000)
author = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)
Upvotes: 0
Reputation: 311
The only thing I changed from your code is to override the get_object
method by filtering with the username instead of the default pk
. I also changed the lookup_field
to a descriptive name and used ModelSerializer
and StringRelated
in the serializer.py
.
models.py
class Speaker(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
serializer.py
class SpeakerSerializer(serializers.ModelSerializer):
user = serializers.StringRelatedField(read_only=True)
class Meta:
model = Speaker
lookup_field = "username"
fields = "__all__"
views.py
class SpeakerViewSet(ModelViewSet):
queryset = Speaker.objects.all().select_related("user")
serializer_class = SpeakerSerializer
lookup_field = "username"
def get_object(self):
"""Return the object for this view."""
return get_object_or_404(self.queryset, user__username=self.kwargs["username"])
urlconf
api/ ^speaker/$ [name='speaker-list']
api/ ^speaker\.(?P<format>[a-z0-9]+)/?$ [name='speaker-list']
api/ ^speaker/(?P<username>[^/.]+)/$ [name='speaker-detail']
api/ ^speaker/(?P<username>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='speaker-detail']
Upvotes: 1
Reputation: 562
urls.py
path('user/<int:user_id>/settings/preferences/', UserPreferenceSettingsView.as_view(), name="settings_preferences")
models.py
class UserSetting(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
adult_lock = models.BooleanField(default=False)
child_lock = models.BooleanField(default=False)
promotional_email = models.BooleanField(default=True)
update_email = models.BooleanField(default=True)
updated_at = models.DateTimeField(auto_now=True)
views.py
class UserPreferenceSettingsView(generics.RetrieveUpdateAPIView):
http_method_names = ['get', 'patch']
serializer_class = UserPreferenceSettingsSerializer
def get_object(self):
lookup_field = self.kwargs["user_id"]
return get_object_or_404(UserSetting, user__pk=lookup_field)
If you need to fetch from username
just replace user_id to username
and url <int:user_id>
to <username> or <str:username>
Upvotes: 0
Reputation: 16050
This is how I managed to hack it
from django.db import models
from django.contrib.auth.models import User
class Speaker(models.Model):
user = models.OneToOneField(User)
@property
def user__username(self):
return self.user.username
def __unicode__(self):
return self.user.username
from .models import Speaker
from rest_framework import serializers
from django.contrib.auth.models import User
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'email', 'groups')
lookup_field = 'username'
class SpeakerSerializer(serializers.HyperlinkedModelSerializer):
user = serializers.HyperlinkedRelatedField(
view_name='user-detail',
read_only=True,
lookup_field='username'
)
class Meta:
model = Speaker
fields = ('url', 'user')
lookup_field = 'user__username'
from .models import Speaker
from .serializers import SpeakerSerializer, UserSerializer
from rest_framework import viewsets
from django.contrib.auth.models import User
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
lookup_field = 'username'
class SpeakerViewSet(viewsets.ModelViewSet):
queryset = Speaker.objects.all().select_related('user')
serializer_class = SpeakerSerializer
lookup_field = 'user__username'
Upvotes: 13
Reputation: 15594
Have you tried this approach?
class SpeakerViewSet(viewsets.ModelViewSet):
queryset = Speaker.objects.all().select_related('user')
serializer_class = SpeakerSerializer
filter_backends = (filters.DjangoFilterBackend,)
filter_fields = ('user', 'user__username',)
Upvotes: 0