django rest framework lookup_field through OneToOneField

26,488

This is how I managed to hack it

models.py

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

serializers.py

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'

view.py

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'
Share:
26,488

Related videos on Youtube

Randall Hunt
Author by

Randall Hunt

Randall Hunt is a Senior Technical Evangelist and Software Engineer at Amazon Web Services in Los Angeles. Randall spends most of his time building demos and writing about new services and launches. Python is his favorite programming language but he can sometimes be found in the dark realm of C++. Prior to working at AWS, Randall launched rockets at NASA and SpaceX but he found his programming passion at MongoDB. He is a total space nerd. Randall has dealt with a wide range of both business and technical issues across many different verticals based on his experiences at SpaceX, MongoDB, AWS, and NASA. Thanks to these experiences Randall has gained a deep understanding of deploying large-scale technical solutions in cloud environments. As a Sr. Technical Evangelist, Randall often speaks at conferences and events across the world where he helps developers maximize their productivity in the cloud. He's particularly interested in AI, databases, serverless, and DevOps. He holds all AWS certifications.

Updated on July 09, 2022

Comments

  • Randall Hunt
    Randall Hunt almost 2 years

    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.

    • Kevin Brown-Silva
      Kevin Brown-Silva about 9 years
      Have you tried using double underscores in the lookup_field to see if that can work? It'd be similar to a queryset filter.
    • Randall Hunt
      Randall Hunt about 9 years
      I've tried using lookup_field = "user__username" and it doesn't work. I've tried using that on both the model and the view to no avail :( 'Speaker' object has no attribute 'user__username'
    • levi
      levi about 9 years
      @ranman what do you want ? do you want to serialize a related object ?
    • Randall Hunt
      Randall Hunt about 9 years
      I want to be able to lookup the speaker object by the username on the user associated with the speaker
  • Randall Hunt
    Randall Hunt about 9 years
    'Speaker' object has no attribute 'user__username' doesn't seem to work. I might have implemented it incorrectly.
  • Randall Hunt
    Randall Hunt about 9 years
    Actually it doesn't work -- for some reason this doesn't allow 2 things: the user field is no longer able to be written to when you create a new speaker, the speaker is no longer renderable on items that have relations to it
  • Todor
    Todor about 9 years
    How can i reproduce your errors? I mean I tried to create a new speaker via the python shell and it pass with no problems, if you try to create a speaker via the api, well the user field is marked as read-only, that's why it doesn't work. I also created a dummy model having OneToOneField to the speaker, this also worked with both ModelSerializer and HyperlinkedModelSerializer with the 2nd one i just had to redefine the speaker field with as HyperlinkedRelatedField with lookup_field='user__username'(the same thing was done in the SpeakerSerializer for the user field.