URL query parameters are not processed in django rest - python

Here is my views.py:
class my4appCompanyData(generics.ListAPIView):
serializer_class = my4appSerializer
def get_queryset(self,request):
"""Optionally restricts the returned data to ofa company,
by filtering against a `id` query parameter in the URL. """
queryset = companies_csrhub.objects.all()
#url_id = self.request.query_params.get('id', None)
url_id = request.GET.get('id', None)
if id is not None:
queryset = queryset.filter(id=url_id)
elif id is ALL:
queryset = companies_csrhub.objects.all()
else:
queryset = "Error data not found"
return queryset
And my urls.py:
router.register(r'api/my4app/company/$', views.my4appCompanyData.as_view(),base_name="company")
URL used for checking: mywebsite/api/my4app/company/?id=100227
Planning to add multiple filters with default values but not working. Please help.

class my4appCompanyData(generics.ListAPIView):
serializer_class = my4appSerializer
def get_queryset(self,request):
"""Optionally restricts the returned data to ofa company,
by filtering against a `id` query parameter in the URL. """
queryset = companies_csrhub.objects.all()
url_id = request.query_params.get('id', None)
if id is not None:
queryset = queryset.filter(id=url_id)
elif id is ALL:
queryset = companies_csrhub.objects.all()
else:
queryset = []
return queryset
Delete the return id since id is not a queryset, therefore it'd give an error. Also in the else part of if statement, you return string but you can't do that also since string is not a queryset.

According the official docs (http://www.django-rest-framework.org/api-guide/filtering/#filtering-against-query-parameters)
I think your code is not working because you are using:
url_id = request.query_params.get('id', None)
Instead of:
url_id = self.request.query_params.get('id', None)
In the documentation you can find that get_queryset function just receives self param, you must remove request param.

Related

How to remove items from queryset based on condition and then return as json response in django

I am doing join operation using select_related() and filtering records using below code
class ActiveclientViewSet(viewsets.ModelViewSet):
queryset = Ruledefinitions.objects.select_related('pmdclinicalruleid').filter(pmdclinicalruleid__effectivedate__lt = timezone.now(),pmdclinicalruleid__retireddate__gt = timezone.now())
serializer_class = RuledefinitionsSerializer
In the above code, is it possible to check whether the first item from queryset has rulename field value as empty and if it is empty i need to return remaining queryset items in json response if not empty return all items as json response.
What's wrong with checking the first element ?
class ActiveclientViewSet(viewsets.ModelViewSet):
queryset = Ruledefinitions.objects.select_related('pmdclinicalruleid')
serializer_class = RuledefinitionsSerializer
def get_queryset(self):
now = timezone.now
queryset = super().get_queryset().filter(
pmdclinicalruleid__effectivedate__lt=now,
pmdclinicalruleid__retireddate__gt=now,
)
first_item = queryset.first()
if first_item is not None and not first_item.rulename:
queryset = queryset[1:]
return queryset
Your filter on timezone.now() is only executed once: when your classe is defined. So any call to this method should not be in the classe definition, but called every request.
In your actual implementation, now would be called as soon as you start the server. Two weeks later, the filter would still be at the same date.

Django Model reverse relationship having a string with the name of it

I want to implement a Django Rest Framework view that returns the dependencies tree of a model instance object. This is the code for such view:
class RoomTypeDependencies(viewsets.ViewSet):
def list(self, request, pk):
room_type = models.RoomType.objects.get(pk=pk)
dependency_tree = self.get_object_dependencies(room_type)
return Response(dependency_tree)
def get_object_dependencies(self, instance):
fields = instance.__class__._meta.get_fields()
dependencies_to_return = []
for field in fields:
print(field.name)
if field.__class__.__name__ == 'ManyToOneRel':
dependency_to_return = []
dependent_instances = getattr(instance, field.name)
for dependent_instance in dependent_instances:
dependency_to_return.append(self.get_object_dependencies(dependent_instance))
dependencies_to_return.append({field.__class__.__name__: dependency_to_return})
return Response({str(instance): dependencies_to_return})
Everything seems to work, but I expected getattr(instance, field.name) to return the dependent instances corresponding to the reverse relationship, just like using model_object_instance.reverse_relationshio_name pattern, but it returns a RelatedManager object instead. The problem in my case is that I have the reverse relationship name in a string variable (field.name).

Django View Return Queryset with extra information

I have a normal Django view that returns the API for a query set. It takes query params from the URL and filters the database based on the parameters. It also outputs a maximum length of 3 "Part" objects.
I would like to add something so that it returns information on whether the queryset is clipped by the maximum length of 3. The idea is that since the inputs the query parameters, if the parameters are too vague, then there will be too much data being queried from the database. So it is clipped but then the user needs to know that it was clipped.
The current code looks like this
class PartList(generics.ListAPIView):
serializer_class = PartSerializer
def get_queryset(self):
"""
Optionally restricts the returned purchases to a given user,
by filtering against a `username` query parameter in the URL.
"""
queryset = Part.objects.all()
querydict = self.request.query_params
for (k, value) in querydict.items():
search_type = 'contains'
filter = k + '__' + search_type
queryset = queryset.filter(**{filter: value})
query_max_limit = 3
return queryset[:min(len(queryset), query_max_limit)]
You can try to fetch four elements, and in case it returns four, you display the first three, and specify that the data is clipped, like:
def get_queryset(self):
"""
Optionally restricts the returned purchases to a given user,
by filtering against a `username` query parameter in the URL.
"""
queryset = Part.objects.all()
querydict = self.request.query_params
for (k, value) in querydict.items():
search_type = 'contains'
filter = k + '__' + search_type
queryset = queryset.filter(**{filter: value})
query_max_limit = 3
qs = queryset[:query_max_limit+1]
self.clipped = clipped = len(qs) > query_max_limit
if clipped:
return list(qs)[:query_max_limit]
else:
return qs
So here the get_queryset will return a collection (not per se a QuerySet), containing at most three elements, and it will set an attribute self.clipped that specifies if the data was clipped.
Or a more elegant approach would be to first count, and then slice:
def get_queryset(self):
"""
Optionally restricts the returned purchases to a given user,
by filtering against a `username` query parameter in the URL.
"""
queryset = Part.objects.all()
querydict = self.request.query_params
for (k, value) in querydict.items():
search_type = 'contains'
filter = k + '__' + search_type
queryset = queryset.filter(**{filter: value})
query_max_limit = 3
qs = queryset[:query_max_limit+1]
self.clipped = clipped = qs.count() > query_max_limit
if clipped:
return queryset[:query_max_limit]
else:
return qs
It might be better to move this "clipping" logic to a dedicated function, and return if it is clipped, instead of setting an attribute.
It's perfectly fine to pass metadata along with your results, like so:
{
"is_clipped": true,
"results": [
…
]
}
Willem's answer is a good way to set is_clipped.
But I think you are interested in pagination, which is a standard way to communicate to clients that the results are clipped. It's possible combine your queryset filering with pagination. By the way, I suggest you use django-filter instead of rolling your own filtering.

Django filter multiple value in the same column

I have set up the column where one of the table is called Establishment_Type
Now, I am trying to filter according to Establishment_Type.
Here is my view.py code
class ShopDetailAPIView(ListAPIView):
serializer_class = ShopDetailSerializer
def get_queryset(self):
queryset = Shop.objects.all()
type = self.request.query_params.get('type', None)
type2 = self.request.query_params.get('type2', None)
if type is not None and type2 is None:
queryset = queryset.filter(Establishment_Type = type)
elif type is not None and type2 is not None:
queryset = queryset.filter(Q(Establishment_Type = type) | Q(Establishment_Type = type2))
return queryset
In the url, I query by typing:
http://127.0.0.1:8000/shop/search/?type=Restaurant&type2=Petrol%20Station
Which only filter Establishment_Type = Restaurant but not include Establishment_Type = Petrol Station
Here is my urls.py within my app called shop:
urlpatterns = [
url(r'^$', ShopListAPIView.as_view(), name = 'list' ),
#####
url(r'^create/$', ShopCreateAPIView.as_view(), name = 'create' ),
url(r'^search/$', ShopDetailAPIView.as_view(), name = 'detail'),
]
Was my url for filtering 2 Establishment type wrong?
Do I need to change something in my code in order to filter 2 values in column Establishment_Type?
Thanks #Thyrst' for advising me to use Establishment_Type__in=types
I have modify my code this way for my filter to work
class ShopDetailAPIView(ListAPIView):
serializer_class = ShopDetailSerializer
def get_queryset(self):
queryset = Shop.objects.all()
type = self.request.query_params.get('type', None)
type = type.split(',')
if type is not None:
queryset = queryset.filter(Establishment_Type__in = type)
return queryset
so type is now list, therefore when entered the url:
http://127.0.0.1:8000/shop/search/?type=Restaurant,Petrol%20Station
it filters according to Restaurant and Petrol Station.
it also works when entering just 1 value or more.
This is good for now but I feel like there might be a better way to implement this.
There is an even simpler way to achieve this using the django-filter package. Deep within the django-filter documentation, it mentions that you can use "a dictionary of field names mapped to a list of lookups".
Your code would be updated like so:
# views.py
from django_filters.rest_framework import DjangoFilterBackend
class ShopDetailAPIView(ListAPIView):
queryset = Shop.objects.all()
serializer_class = ShopDetailSerializer
filter_backends = [DjangoFilterBackend]
filter_fields = {
'type': ["in", "exact"]
}
Now in the URL you would add __in to the filter before supplying your list of parameters and it would work as you expect:
http://127.0.0.1:8000/shop/search/?type__in=Restaurant,Petrol%20Station
The django-filter documentation on what lookup filters are available is quite poor, but the in lookup filter is mentioned in the Django documentation itself.

Feeding a current filter selection to another custom SimpleListFilter in Django

I am trying to make the prompts of one filter change in response to the current selection made in another filter. I am pretty lost as to how to get the currently selected value of the AttributeCategoryFilter passed into the AttributeFilter. I am using Django 1.4-dev. Trying to figure out if I should be using RelatedFieldListFilter for this purpose. It looks like these features are so young as to not have (m)any examples floating around in the wild yet.
class AttributeCategoryFilter(SimpleListFilter):
title = _('Attribute Category')
parameter_name = 'attribute_category'
def lookups(self, request, model_admin):
attributes = Attribute.objects.filter(parent_attribute=None)
prompts = []
for attribute in attributes:
prompts.append((attribute.title, _(str(attribute.title))))
return prompts
def queryset(self, request, queryset):
if self.value():
return queryset.filter(attribute__category=self.value())
else:
return queryset
class AttributeFilter(SimpleListFilter):
title = _('Attribute Title')
parameter_name = 'attribute_title'
def lookups(self, request, model_admin):
desired_category = # Needs to be a reference to the selected value in the AttributeCategoryFilter above
attributes = Attribute.objects.filter(category=desired_category).exclude(parent_attribute=None)
prompts = []
for attribute in attributes:
prompts.append((attribute.title, _(str(attribute.title))))
return prompts
def queryset(self, request, queryset):
if self.value():
return queryset.filter(attribute__title=self.value())
else:
return queryset
class ValueAdmin(admin.ModelAdmin):
list_display = ('package', 'attribute', 'presence', 'text', 'modified', 'created')
list_filter = ('package', AttributeCategoryFilter, AttributeFilter, 'presence',
'attribute__admin_approved', 'attribute__dtype', 'modified')
search_fields = ('package', 'attribute', 'text')
list_display_links = ('package', )
list_editable = ('presence', 'text')
list_per_page = 20000
admin.site.register(Value, ValueAdmin)
Here is what worked for me... The "TypeListFilter" is only visible once the "Category" filter is used and then displays all the entries that are a "subTypeOf" the selected category. The "special case" hack further down ensures that the filter disappears when the user selects another Category.
The "_class" parameter adds some extra flexibility. I am using the same filter with different but related Type classes and just have to override this one parameter. Just replace it by the admin.Model class that you want to filter.
class TypeListFilter( admin.SimpleListFilter):
"""
Provide filter for DnaComponentType (the actual "second level" type).
This filter has one cosmetic problem, which is that it's setting is not
automatically deleted if the category filter is changed. I tried but the
request and queryset are all immutable. Instead, the queryset method is
checking for any missmatch between category and filter name and filtering
is ignored if the category name doesn't match the current subType name.
"""
title = 'Type'
parameter_name = 'type'
_class = None
def lookups(self, request, model_admin):
"""
Returns a list of tuples. The first element in each
tuple is the coded value for the option that will
appear in the URL query. The second element is the
human-readable name for the option that will appear
in the right sidebar.
"""
if not u'category' in request.GET:
return ()
category_name = request.GET[u'category']
types = self._class.objects.filter(subTypeOf__name=category_name)
return ( (t.name, t.name) for t in types )
def queryset(self, request, queryset):
"""
Returns the filtered queryset based on the value
provided in the query string and retrievable via
`self.value()`.
"""
if not u'category' in request.GET:
return queryset
category = request.GET[u'category']
subtypes = self._class.objects.filter(subTypeOf__name=category)
r = queryset.filter(componentType__subTypeOf__name=category)
if not self.value():
return r
## special case: missmatch between subtype and category
## which happens after switching the category
if len(subtypes.filter(name=self.value())) == 0:
return r
return r.filter(componentType__name=self.value())
Found your question as I was looking for something else.
Here's what I did to only show players that had characters in a selected game:
def lookups(self, request, model_admin):
game_id = request.GET.get('game', None)
players = Player.objects.all()
if game_id:
players = players.filter(character__game_id=game_id)
return [(p.id, p.__unicode__()) for p in players]
Looks like this is what dan-klasson suggested too.
Hint: Putting ids into query parameters is generally considered a no-no for security reasons.

Categories

Resources