Discussion:
Custom Mixin for Mulitple Field Object Lookup in Django REST
Foobar
2018-10-29 18:50:02 UTC
Permalink
I have the following code which doesn't work:

In models.py:

class Unit(models.Model):
unit_name = models.CharField(max_length=255)

In views.py

class MultipleFieldLookupMixin(object):"""
Apply this mixin to any view or viewset to get multiple field filtering
based on a `lookup_fields` attribute, instead of the default single field filtering.
"""def get_object(self):
queryset = self.get_queryset() # Get the base queryset
queryset = self.filter_queryset(queryset) # Apply any filter backends
filter = {}
for field in self.lookup_fields:
if self.kwargs[field]: # Ignore empty fields.
filter[field] = self.kwargs[field]
obj = get_object_or_404(queryset, **filter) # Lookup the object
self.check_object_permissions(self.request, obj)
return obj
class UnitViewSet(viewsets.ReadOnlyModelViewSet, MultipleFieldLookupMixin):
serializer_class = UnitSerializer
queryset = Unit.objects.all()
lookup_fields = ('pk', 'unit_name')

In router.py

router.register(r'units', UnitViewSet)

I am able to access units/pk but units/unit_name gives a 404 error. Do I
have to write a custom URL to make this work? If so, then what is the point
of using Routers with ViewSets?


I even tried adding the following directly to urls.py but it didn't make a
difference (perhaps because this needs to be defined in router.py?)


url(r'^units/(?P<unit_name>[^/.]+)$', views.UnitViewSet, name='unit-detail')


Any help would be appreciated. Thanks.
--
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-framework+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Jason
2018-10-30 11:59:03 UTC
Permalink
Due to python's method resolution order, you need to have mixins first in
the class inheritance order. Specifically

class UnitViewSet(MultipleFieldLookupMixin, viewsets.ReadOnlyModelViewset):
...

See
https://stackoverflow.com/questions/1848474/method-resolution-order-mro-in-new-style-classes for
more information about MROs
--
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-framework+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Foobar
2018-10-30 17:52:54 UTC
Permalink
Thanks Jason. Now, the get_object function in class
MultipleFieldLookupMixin is actually being called but it's throwing an
error as follows:

if self.kwargs[field]: # Ignore empty fields.
KeyError: 'unit_name'

The ViewSet is defined as follows:

class UnitViewSet(CountModelMixin, MultipleFieldLookupMixin, NestedViewSetMixin, viewsets.ReadOnlyModelViewSet):
serializer_class = UnitSerializer
queryset = Unit.objects.all()
lookup_fields = ('pk', 'unit_name')


Any clue as to why this is happening? Thanks.
Post by Jason
Due to python's method resolution order, you need to have mixins first in
the class inheritance order. Specifically
...
See
https://stackoverflow.com/questions/1848474/method-resolution-order-mro-in-new-style-classes for
more information about MROs
--
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-framework+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Alan Crosswell
2018-10-30 12:23:13 UTC
Permalink
This seems like you are reinventing SearchFilter. Maybe I’m
misunderstanding.
https://www.django-rest-framework.org/api-guide/filtering/
Post by Foobar
unit_name = models.CharField(max_length=255)
In views.py
class MultipleFieldLookupMixin(object):"""
Apply this mixin to any view or viewset to get multiple field filtering
based on a `lookup_fields` attribute, instead of the default single field filtering.
queryset = self.get_queryset() # Get the base queryset
queryset = self.filter_queryset(queryset) # Apply any filter backends
filter = {}
if self.kwargs[field]: # Ignore empty fields.
filter[field] = self.kwargs[field]
obj = get_object_or_404(queryset, **filter) # Lookup the object
self.check_object_permissions(self.request, obj)
return obj
serializer_class = UnitSerializer
queryset = Unit.objects.all()
lookup_fields = ('pk', 'unit_name')
In router.py
router.register(r'units', UnitViewSet)
I am able to access units/pk but units/unit_name gives a 404 error. Do I
have to write a custom URL to make this work? If so, then what is the point
of using Routers with ViewSets?
I even tried adding the following directly to urls.py but it didn't make a
difference (perhaps because this needs to be defined in router.py?)
url(r'^units/(?P<unit_name>[^/.]+)$', views.UnitViewSet, name='unit-detail')
Any help would be appreciated. Thanks.
--
You received this message because you are subscribed to the Google Groups
"Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an
For more options, visit https://groups.google.com/d/optout.
--
Alan Crosswell
Associate VP & CTO
--
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-framework+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Asad Habib
2018-10-30 18:05:20 UTC
Permalink
Alan, appreciate your input. Basically, I want to be able to retrieve units
based on either a primary key or other attribute. Is this possible using
filtering if URLs are being defined using routers?
Post by Alan Crosswell
This seems like you are reinventing SearchFilter. Maybe I’m
misunderstanding.
https://www.django-rest-framework.org/api-guide/filtering/
Post by Foobar
unit_name = models.CharField(max_length=255)
In views.py
class MultipleFieldLookupMixin(object):"""
Apply this mixin to any view or viewset to get multiple field filtering
based on a `lookup_fields` attribute, instead of the default single field filtering.
queryset = self.get_queryset() # Get the base queryset
queryset = self.filter_queryset(queryset) # Apply any filter backends
filter = {}
if self.kwargs[field]: # Ignore empty fields.
filter[field] = self.kwargs[field]
obj = get_object_or_404(queryset, **filter) # Lookup the object
self.check_object_permissions(self.request, obj)
return obj
serializer_class = UnitSerializer
queryset = Unit.objects.all()
lookup_fields = ('pk', 'unit_name')
In router.py
router.register(r'units', UnitViewSet)
I am able to access units/pk but units/unit_name gives a 404 error. Do I
have to write a custom URL to make this work? If so, then what is the point
of using Routers with ViewSets?
I even tried adding the following directly to urls.py but it didn't make
a difference (perhaps because this needs to be defined in router.py?)
url(r'^units/(?P<unit_name>[^/.]+)$', views.UnitViewSet, name='unit-detail')
Any help would be appreciated. Thanks.
--
You received this message because you are subscribed to the Google Groups
"Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an
For more options, visit https://groups.google.com/d/optout.
--
Alan Crosswell
Associate VP & CTO
--
You received this message because you are subscribed to the Google Groups
"Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-framework+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Alan Crosswell
2018-10-30 18:15:02 UTC
Permalink
Asad:

I believe what you want should work with SearchFilter. Something like this:

from rest_framework.filters import SearchFilter

class UnitViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = UnitSerializer
queryset = Unit.objects.all()
filter_backends = (SearchFilter, )
search_fields = ('pk', 'unit_name')
Post by Asad Habib
Alan, appreciate your input. Basically, I want to be able to retrieve
units based on either a primary key or other attribute. Is this possible
using filtering if URLs are being defined using routers?
Post by Alan Crosswell
This seems like you are reinventing SearchFilter. Maybe I’m
misunderstanding.
https://www.django-rest-framework.org/api-guide/filtering/
Post by Foobar
unit_name = models.CharField(max_length=255)
In views.py
class MultipleFieldLookupMixin(object):"""
Apply this mixin to any view or viewset to get multiple field filtering
based on a `lookup_fields` attribute, instead of the default single field filtering.
queryset = self.get_queryset() # Get the base queryset
queryset = self.filter_queryset(queryset) # Apply any filter backends
filter = {}
if self.kwargs[field]: # Ignore empty fields.
filter[field] = self.kwargs[field]
obj = get_object_or_404(queryset, **filter) # Lookup the object
self.check_object_permissions(self.request, obj)
return obj
serializer_class = UnitSerializer
queryset = Unit.objects.all()
lookup_fields = ('pk', 'unit_name')
In router.py
router.register(r'units', UnitViewSet)
I am able to access units/pk but units/unit_name gives a 404 error. Do I
have to write a custom URL to make this work? If so, then what is the point
of using Routers with ViewSets?
I even tried adding the following directly to urls.py but it didn't make
a difference (perhaps because this needs to be defined in router.py?)
url(r'^units/(?P<unit_name>[^/.]+)$', views.UnitViewSet, name='unit-detail')
Any help would be appreciated. Thanks.
--
You received this message because you are subscribed to the Google
Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send
For more options, visit https://groups.google.com/d/optout.
--
Alan Crosswell
Associate VP & CTO
--
You received this message because you are subscribed to the Google Groups
"Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups
"Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an
For more options, visit https://groups.google.com/d/optout.
--
Alan Crosswell
Associate VP & CTO
--
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-framework+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Asad Habib
2018-10-30 19:33:44 UTC
Permalink
Alan, thanks. With this approach do I have to explicitly define URLs in
urls.py or are these generated? For the above, would the URL look like the
following?

localhost/api/units?search=string

Where string is the literal string to be searched?

If this is the case, how does it know whether I want to search on pk or
unit_name? I feel I'm missing something.
Post by Alan Crosswell
from rest_framework.filters import SearchFilter
serializer_class = UnitSerializer
queryset = Unit.objects.all()
filter_backends = (SearchFilter, )
search_fields = ('pk', 'unit_name')
Post by Asad Habib
Alan, appreciate your input. Basically, I want to be able to retrieve
units based on either a primary key or other attribute. Is this possible
using filtering if URLs are being defined using routers?
Post by Alan Crosswell
This seems like you are reinventing SearchFilter. Maybe I’m
misunderstanding.
https://www.django-rest-framework.org/api-guide/filtering/
Post by Foobar
unit_name = models.CharField(max_length=255)
In views.py
class MultipleFieldLookupMixin(object):"""
Apply this mixin to any view or viewset to get multiple field filtering
based on a `lookup_fields` attribute, instead of the default single field filtering.
queryset = self.get_queryset() # Get the base queryset
queryset = self.filter_queryset(queryset) # Apply any filter backends
filter = {}
if self.kwargs[field]: # Ignore empty fields.
filter[field] = self.kwargs[field]
obj = get_object_or_404(queryset, **filter) # Lookup the object
self.check_object_permissions(self.request, obj)
return obj
serializer_class = UnitSerializer
queryset = Unit.objects.all()
lookup_fields = ('pk', 'unit_name')
In router.py
router.register(r'units', UnitViewSet)
I am able to access units/pk but units/unit_name gives a 404 error. Do
I have to write a custom URL to make this work? If so, then what is the
point of using Routers with ViewSets?
I even tried adding the following directly to urls.py but it didn't
make a difference (perhaps because this needs to be defined in router.py?)
url(r'^units/(?P<unit_name>[^/.]+)$', views.UnitViewSet, name='unit-detail')
Any help would be appreciated. Thanks.
--
You received this message because you are subscribed to the Google
Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send
For more options, visit https://groups.google.com/d/optout.
--
Alan Crosswell
Associate VP & CTO
--
You received this message because you are subscribed to the Google
Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups
"Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an
For more options, visit https://groups.google.com/d/optout.
--
Alan Crosswell
Associate VP & CTO
--
You received this message because you are subscribed to the Google Groups
"Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-framework+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Alan Crosswell
2018-10-30 19:43:50 UTC
Permalink
Yes, I believe the default search keyword is `search`. It searches for a
keyword match across all the `search_fields`. I believe that's what you
indicated. If you want to search individual fields with a variety of
possible relationships, take a look at `DjangoFilterBackend`.
Post by Asad Habib
Alan, thanks. With this approach do I have to explicitly define URLs in
urls.py or are these generated? For the above, would the URL look like the
following?
localhost/api/units?search=string
Where string is the literal string to be searched?
If this is the case, how does it know whether I want to search on pk or
unit_name? I feel I'm missing something.
Post by Alan Crosswell
from rest_framework.filters import SearchFilter
serializer_class = UnitSerializer
queryset = Unit.objects.all()
filter_backends = (SearchFilter, )
search_fields = ('pk', 'unit_name')
Post by Asad Habib
Alan, appreciate your input. Basically, I want to be able to retrieve
units based on either a primary key or other attribute. Is this possible
using filtering if URLs are being defined using routers?
Post by Alan Crosswell
This seems like you are reinventing SearchFilter. Maybe I’m
misunderstanding.
https://www.django-rest-framework.org/api-guide/filtering/
Post by Foobar
unit_name = models.CharField(max_length=255)
In views.py
class MultipleFieldLookupMixin(object):"""
Apply this mixin to any view or viewset to get multiple field filtering
based on a `lookup_fields` attribute, instead of the default single field filtering.
queryset = self.get_queryset() # Get the base queryset
queryset = self.filter_queryset(queryset) # Apply any filter backends
filter = {}
if self.kwargs[field]: # Ignore empty fields.
filter[field] = self.kwargs[field]
obj = get_object_or_404(queryset, **filter) # Lookup the object
self.check_object_permissions(self.request, obj)
return obj
serializer_class = UnitSerializer
queryset = Unit.objects.all()
lookup_fields = ('pk', 'unit_name')
In router.py
router.register(r'units', UnitViewSet)
I am able to access units/pk but units/unit_name gives a 404 error. Do
I have to write a custom URL to make this work? If so, then what is the
point of using Routers with ViewSets?
I even tried adding the following directly to urls.py but it didn't
make a difference (perhaps because this needs to be defined in router.py?)
url(r'^units/(?P<unit_name>[^/.]+)$', views.UnitViewSet, name='unit-detail')
Any help would be appreciated. Thanks.
--
You received this message because you are subscribed to the Google
Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send
For more options, visit https://groups.google.com/d/optout.
--
Alan Crosswell
Associate VP & CTO
--
You received this message because you are subscribed to the Google
Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google
Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send
For more options, visit https://groups.google.com/d/optout.
--
Alan Crosswell
Associate VP & CTO
--
You received this message because you are subscribed to the Google Groups
"Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups
"Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an
For more options, visit https://groups.google.com/d/optout.
--
Alan Crosswell
Associate VP & CTO
--
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-framework+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Foobar
2018-10-31 17:18:28 UTC
Permalink
Great, this worked for me. Thanks Alan.
Post by Alan Crosswell
Yes, I believe the default search keyword is `search`. It searches for a
keyword match across all the `search_fields`. I believe that's what you
indicated. If you want to search individual fields with a variety of
possible relationships, take a look at `DjangoFilterBackend`.
Post by Asad Habib
Alan, thanks. With this approach do I have to explicitly define URLs in
urls.py or are these generated? For the above, would the URL look like the
following?
localhost/api/units?search=string
Where string is the literal string to be searched?
If this is the case, how does it know whether I want to search on pk or
unit_name? I feel I'm missing something.
Post by Alan Crosswell
from rest_framework.filters import SearchFilter
serializer_class = UnitSerializer
queryset = Unit.objects.all()
filter_backends = (SearchFilter, )
search_fields = ('pk', 'unit_name')
Post by Asad Habib
Alan, appreciate your input. Basically, I want to be able to retrieve
units based on either a primary key or other attribute. Is this possible
using filtering if URLs are being defined using routers?
Post by Alan Crosswell
This seems like you are reinventing SearchFilter. Maybe I’m
misunderstanding.
https://www.django-rest-framework.org/api-guide/filtering/
Post by Foobar
unit_name = models.CharField(max_length=255)
In views.py
class MultipleFieldLookupMixin(object):"""
Apply this mixin to any view or viewset to get multiple field filtering
based on a `lookup_fields` attribute, instead of the default single field filtering.
queryset = self.get_queryset() # Get the base queryset
queryset = self.filter_queryset(queryset) # Apply any filter backends
filter = {}
if self.kwargs[field]: # Ignore empty fields.
filter[field] = self.kwargs[field]
obj = get_object_or_404(queryset, **filter) # Lookup the object
self.check_object_permissions(self.request, obj)
return obj
serializer_class = UnitSerializer
queryset = Unit.objects.all()
lookup_fields = ('pk', 'unit_name')
In router.py
router.register(r'units', UnitViewSet)
I am able to access units/pk but units/unit_name gives a 404 error.
Do I have to write a custom URL to make this work? If so, then what is the
point of using Routers with ViewSets?
I even tried adding the following directly to urls.py but it didn't
make a difference (perhaps because this needs to be defined in router.py?)
url(r'^units/(?P<unit_name>[^/.]+)$', views.UnitViewSet, name='unit-detail')
Any help would be appreciated. Thanks.
--
You received this message because you are subscribed to the Google
Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it,
<javascript:>.
For more options, visit https://groups.google.com/d/optout.
--
Alan Crosswell
Associate VP & CTO
--
You received this message because you are subscribed to the Google
Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send
<javascript:>.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google
Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send
<javascript:>.
For more options, visit https://groups.google.com/d/optout.
--
Alan Crosswell
Associate VP & CTO
--
You received this message because you are subscribed to the Google
Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send
<javascript:>.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups
"Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an
.
For more options, visit https://groups.google.com/d/optout.
--
Alan Crosswell
Associate VP & CTO
--
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-framework+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Loading...