drf-extensions
drf-extensions copied to clipboard
Nested routers/urls are not filtering
Hi all,
I can't get the my Router to filter my requests based on the "parents_query_lookup".
Here's my code:
urls.py:
from rest_framework_extensions.routers import ExtendedSimpleRouter
from .views import OrganizationViewSet, GroupViewSet, BootGroupViewSet
router = ExtendedSimpleRouter()
(router.register(r'organizations', OrganizationViewSet,
base_name='organization')
.register(r'groups', GroupViewSet, base_name='organizations-group',
parents_query_lookups=['resource__organization'])
.register(r'boot_groups', BootGroupViewSet,
base_name='organizations-groups-boot_group',
parents_query_lookups=['group__resource__organization', 'group']))
urlpatterns = router.urls
views.py:
from rest_framework.viewsets import ModelViewSet
from rest_framework_extensions.mixins import NestedViewSetMixin
from .models import Organization, OrganizationSerializer, \
Group, GroupSerializer, BootGroup, BootGroupSerializer
class OrganizationViewSet(NestedViewSetMixin, ModelViewSet):
queryset = Organization.objects.all()
serializer_class = OrganizationSerializer
class GroupViewSet(NestedViewSetMixin, ModelViewSet):
queryset = Group.objects.all()
serializer_class = GroupSerializer
class BootGroupViewSet(NestedViewSetMixin, ModelViewSet):
queryset = BootGroup.objects.all()
serializer_class = BootGroupSerializer
enums.py:
class ResourceTypeEnum:
RESOURCE_TYPE_GROUP = 'group'
RESOURCE_TYPE_VM = 'vm'
RESOURCE_TYPE_CHOICES = (
(RESOURCE_TYPE_GROUP, RESOURCE_TYPE_GROUP),
(RESOURCE_TYPE_VM, RESOURCE_TYPE_VM)
)
models.py:
from django.db.models import Model
from rest_framework.serializers import ModelSerializer
from .enums import ResourceTypeEnum
class Organization(Model):
organization_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
parent = models.ForeignKey('self', blank=True, null=True)
class Meta:
unique_together = (("name", "parent"))
verbose_name = "Organization"
verbose_name_plural = "Organizations"
app_label = 'api_manager'
db_table = 'organization'
def __unicode__(self):
return self.name
class OrganizationSerializer(ModelSerializer):
class Meta:
model = Organization
fields = ('name', 'parent')
depth = 2
class Resource(Model):
resource_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=55)
type = models.CharField(
max_length=5, choices=ResourceTypeEnum.RESOURCE_TYPE_CHOICES)
organization = models.ForeignKey(Organization)
class Meta:
verbose_name = "Resource"
verbose_name_plural = "Resources"
app_label = 'api_manager'
db_table = 'resource'
def __unicode__(self):
return self.name
class ResourceSerializer(ModelSerializer):
class Meta:
model = Resource
fields = ('name', 'type', 'organization')
depth = 2
class Group(Model):
resource = models.OneToOneField(Resource, primary_key=True)
is_consistent = models.BooleanField()
parent = models.ForeignKey('self', blank=True, null=True)
class Meta:
verbose_name = "Group"
verbose_name_plural = "Groups"
app_label = 'api_manager'
db_table = 'group'
def __unicode__(self):
return "%s: %s" % (self.resource.organization, self.resource)
class GroupSerializer(ModelSerializer):
class Meta:
model = Group
fields = ('resource', 'is_consistent', 'parent')
depth = 2
class BootGroup(Model):
boot_group_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
boot_order = models.PositiveSmallIntegerField(default=1)
group = models.ForeignKey(Group)
class Meta:
unique_together = (("boot_order", "group"))
verbose_name = "BootGroup"
verbose_name_plural = "BootGroups"
app_label = 'api_manager'
db_table = 'boot_group'
def __unicode__(self):
return "%s: %s" % (self.group.resource, self.name)
class BootGroupSerializer(ModelSerializer):
class Meta:
model = BootGroup
fields = ('name', 'boot_order', 'group')
depth = 2
class Vm(Model):
resource = models.OneToOneField(Resource, primary_key=True)
hostname = models.CharField(max_length=200, blank=True, null=True)
group = models.ForeignKey(Group, blank=True, null=True)
boot_group = models.ForeignKey(BootGroup, blank=True, null=True)
class Meta:
verbose_name = "Vm"
verbose_name_plural = "Vms"
app_label = 'api_manager'
db_table = 'vm'
def __unicode__(self):
return "%s: %s" % (self.resource.organization, self.resource)
class VmSerializer(ModelSerializer):
class Meta:
model = Vm
fields = ('resource', 'hostname', 'group', 'boot_group')
depth = 2
No matter what I try, something like "organizations/1/groups" returns all of the Group models, regardless of Organization. Am I missing something? Thanks in advance for the help.
@krunal10 I know this is an old issue, but since I just ran into this myself I'll post the answer. You need to define the queryset for BootGroupViewSet
and GroupViewSet
to actually filter based on the parent lookup. Both of them are just doing a .all()
on the tables for the queryset. If you implement get_queryset(self)
in the viewset instead of setting the class property queryset
, and return your filtered objects based on that key (you can use self.kwargs['parent_lookup_<replace_key_from_router>']) to get at the url parameter.
Why should self.kwargs['parent_lookup_<replace_key_from_router>']
be used when NestedViewSetMixin
has code to alter the queryset
? I don't think using the key and filtering the data yourself in your ViewSet
is how this feature should be used.
This is how I got the filtering to work:
def get_queryset(self):
queryset = Submission.objects.filter(user=self.request.user)
return self.filter_queryset_by_parents_lookups(queryset)
This is essentially what the get_queryset
method in NestedViewSetMixin
does.
Also, I don't understand why the get_queryset
method in NestedViewSetMixin
calls super().get_queryset
. It isn't extending any class.