Get object by field other than primary key

2019-02-06 17:41发布

Hi I'm new to both Django and the Django-Rest-Framework. I've gone through the tutorials. What I'm trying to do (as a learning exercise) is return an object based off a field other than the primary key.

  • myserver:8000/videos returns a list of all videos.
  • myserver:8000/videos/1 returns the video with primary key of 1

What I would like to do is:

  • myserver:8000/videos/:videoname returns the video where videoname = videoname

I have the following Videos model:

class Videos (models.Model):
    videoID = models.IntegerField(blank=True, null=True)
    videoName = models.CharField(max_length=20)
    class Meta:
        app_label="quickstart"

My router is configured as:

video_detail = views.VideoDetailView.as_view({
    'get':'list'
})

urlpatterns = patterns('',
    url(r'^', include(router.urls)),
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
    url(r'^videos/(?P<videoName>[^/]+)/$', video_detail)
)

And my view is defined as:

class VideoDetailView(viewsets.ModelViewSet):
    serializer_class = VideosSerializer
    def get_queryset(self):
        videoName = self.kwargs.get(videoName, None)
        queryset = super (VideoDetailView,self).get_queryset()
        if videoName:
            queryset = queryset.filter(videoName=videoName)
        return queryset

The api runs but when I hit: myserver:8000/videos/SecondVideo/ (where "SecondVideo" is the name of the video) I get a 404 error.

Any help?

4条回答
Ridiculous、
2楼-- · 2019-02-06 18:11

Try setting the lookup_field attribute on your view class. That is the field that will be used to look up an individual model instance. It defaults to 'pk' but you can change it to 'videoName'.

class VideoDetailView(viewsets.ModelViewSet):
    serializer_class = VideosSerializer
    lookup_field = 'videoName'
查看更多
我想做一个坏孩纸
3楼-- · 2019-02-06 18:12

So I figured it out. What was going on was the

router.register(r'videos', views.VideosViewSet)

Was handling myserver:8000/videos/1 and so my a new url pattern url(r'^videos/(?P<videoName>.+)/$', views.VideoDetailView.as_view()) was being overridden by the registered route. The code that works is:

urls.py

url(r'^video/(?P<videoName>.+)/$', views.VideoDetailView.as_view())

views.py

class VideoDetailView(generics.ListAPIView):
    serializer_class = VideosSerializer

    def get_queryset(self):
        videoName = self.kwargs['videoName']
        return Videos.objects.filter(videoName=videoName)

This documentation page on filtering against the URL helped me piece together what was going on.

查看更多
三岁会撩人
4楼-- · 2019-02-06 18:20

What about a solution just like this:

views.py

class VideoDetailView(generics.RetrieveAPIView):
    serializer_class = VideosSerializer
    lookup_field = 'videoName'

reasoning: you want a detailview, so there is no need for ListView but RetriveAPIView

if some furthere manipulation will be needed just override get_object method like this:

def get_object(self):
    obj = super(VideoDetailView, self).get_object()
    # perform some extra checks on obj, e.g custom permissions
    return obj
查看更多
对你真心纯属浪费
5楼-- · 2019-02-06 18:27

Credit https://www.youtube.com/watch?v=dWZB_F32BDg

Use lookup_field to define the field used for querying the table and look_up_kwargs for the field in the url

url(r'^videos/(?P<videoName>[^/]+)/$', video_detail)

class VideoDetailView(viewsets.ModelViewSet):
    serializer_class = VideosSerializer
    queryset = Videos.objects.all()
    lookup_field = 'videoName'
    lookup_url_kwarg = 'videoName'
查看更多
登录 后发表回答