I have EmberJS front-end which calls Django REST API. I am passing string array from front-end to the API as shown below:
Back-end Request
http://localhost:8000/api/v1/User/search/?active=true&city%5B%5D=newyork&city%5B%5D=chicago&city%5B%5D=boston
HTML Decoded
http://localhost:8000/api/v1/User/search/?active=true&city[]=newyork&city[]=chicago&city[]=boston
Some how I am not able to get rid of the extra %5B%5D (which is square brackets) from this request as EmberJS automatically adds that as the parameter is passed as string array. I searched over several forums but did not get a solution for it.
How to read the string array (City parameter) in Django API and get all passed values from EmberJS front-end?
You do not need to get rid of the []. The are good practice to indicate "I really do want to pass an array, i.e. multiple values for this query parameter".
The [] are presumably there because ember-data in your front end uses coalesceFindRequests. Dustin Farras (the main author of the ember-Django-adapter) has written an article about using his adapter with coalesceFindRequests: You can define a filter that covers [] and simply tell the django-rest-framework to use it:
# myapp/filters.py
from rest_framework import filters
class CoalesceFilterBackend(filters.BaseFilterBackend):
"""
Support Ember Data coalesceFindRequests.
"""
def filter_queryset(self, request, queryset, view):
id_list = request.query_params.getlist('ids[]')
if id_list:
# Disable pagination, so all records can load.
view.pagination_class = None
queryset = queryset.filter(id__in=id_list) # adapt here, see note below
return queryset
Now you just need to add this filter to filter_backends in your views, e.g.:
from myapp.filters import CoalesceFilterBackend
class UserListView(generics.ListAPIView):
queryset = User.objects.all()
serializer = UserSerializer
filter_backends = (CoalesceFilterBackend,)
Or, configure it globally in your DRF settings:
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ('myapp.filters.CoalesceFilterBackend',) }
Note: You will need to adapt the filter for cities, of course. (When filtering for primary keys, I prefer to filter for pk__in= instead of id__in= that also covers primary keys with names other than id.)
Related
I have the following model:
class News(models.Model):
news_text = models.TextField(null=True)
... # other fields
with the following view:
def news(r):
news= News.objects
values = {'news':news}
return render(r,'webapp1/news.html',values)
I want to show in the template a substring for the column news_text, till the first 'dot' occurrence, like:
{{news.news_text| split('.')[0] }}
Tried this in template but got:
"invalid filter: 'split'".
Django has a limited set of builtin template filters and there is no split filter.
Also in Django templates you can access object's attributes or methods like {{my_dict.keys}}, but you can't pass any arguments. So you can't do things like {{news.news_text.split('.')}} as well.
All of this is done with intention to force you to separate logic from templates. So for your example probably will be better to define a special context variable and pass it into template's rendering context, like:
def news(r):
news = News.objects.all().get() # don't forget to call some filters on object manager
context = {
'news': news,
'headlines': news.news_text.split('.')[0],
}
return render(r, 'webapp1/news.html', context)
Also note that plural model names may be confusing: is it an array of news in each entry, or not?
Nevertheless you can create custom template tags and filters (and in many cases you should) to solve your problem.
I am diving to a django rest-api framework that someone else wrote and configured, and I came across a problem I could not find any good solution for.
There is a model containing field of type "YAMLField". While trying to retrieve this field member, it is converted to OrderedDict (not quite sure how and where this conversion is happening...).
Now, I have a queryset of this model. I understand how to filter a queryset based on simple attributes, but how can I filter it based on this dictionary?
For example, each entry in this queryset (which is MyModel instance) contains:
MyModel.myDictionary == {'firstKey': 'firstVal', 'secondKey':'secondVal}
Now I want to get all the entries from this queryset where:
myDictionary = {'firstKey': 'Something'}
Meaning, my dictionary to filter by, may contain only a subset of the keys.
I could not find any solution or a straight forward way to do it, leading me to iterate the queryset and for each entry, iterate the dictionary.
This feels like too much overhead...
I think I had the same problem and someone told me an straightforward answer, which consists in adding "__{dictionary_key}" to your filtering request such as, in your case:
Model.objects.all().filter(myDictionary__firstKey="Something")
Though the asnwer is probably coming too late, I post it hoping that it can be useful for others in the future!
You need this is possible. For more information see django-rest-framework doc
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:
filter[field] = self.kwargs[field]
return get_object_or_404(queryset, **filter) # Lookup the object
Here's a very simple Django Rest Framework/django-filter code example:
class MyModelFilter(django_filters.FilterSet):
class Meta:
model = MyModel
fields = {'my_str_field': ['exact']}
class MyModelList(generics.ListAPIView):
queryset = MyModel.objects.all()
filter_class = MyModelFilter
def get(self, request, format=None):
items = self.filter_queryset(self.queryset) # apply filters
serializer = MyModelSerializer(items, many=True)
return Response(serializer.data)
When I make this API call, the exact lookup type works as expected, returning matched objects:
/myobjects/?my_str_field=somevalue
If I use icontains, which as you see I did not specify as one of the supported lookup types, all objects are returned, as if the filter wasn't applied:
/myobjects/?my_str_field__icontains=this_can_be_anything
Furthermore, I can even use an invalid lookup type and there will be no error, with, again, all objects returned:
/myobjects/?my_str_field__this_can_be_anything=this_can_be_anything
This can obviously be misleading because a front-end developer who doesn't have access to the back-end code can happily think everything is fine and use the returned objects. I would expect, if not an error, at least an empty result set for the latter two cases. What am I doing wrong?
UPDATE: It appears that I should be using the strictness setting like so:
from django_filters.filterset import STRICTNESS
class MyModelFilter(django_filters.FilterSet):
# throw an exception on errors instead of returning empty results
strict = STRICTNESS.RAISE_VALIDATION_ERROR
class Meta:
model = MyModel
fields = {'my_str_field': ['exact']}
Unfortunately, this still doesn't result in an error, so my original question still stands.
If the server doesn't recognize a query string parameter, the typical behavior is to ignore that parameter. There's no standard or rfc that specifies how to handle unexpected query string parameters. But very many websites and web frameworks will liberally accept requests, and not perform any validation to reject requests that contain superfluous or misspelled query parameters.
In other words, this is not specific for Django Rest Framework.
This feature makes ajax cache busting possible. jQuery will add a parameter called _ with a random value to every single ajax request to make sure that the request has a unique url and is not cached anywhere. This would not work if the server returned an error on receiving an unexpected query parameter.
I am working on an api built with Django Rest Framework. I have defined several model classes and I have also created some filters to apply on certain queries that happen in the specified api-endpoints.
I am trying to apply LIMIT in the queryset but I would prefer to not
use the Django notation e.x. Entry.objects.all()[:5]. Instead I would like to be able to apply the limit from inside the filter associated with the model.
Until now I haven't find any solution. I am thinking that I should find a way to define a filter with default value something that would result in not limiting the queryset and if a request reaches the endpoint and contains something like ?filter=10 then the queryset should be limited to the first 10.
You can use Django Rest Framework pagination. The pagination_class LimitOffsetPagination give you the ability to limit the number of returned entries in a query_param.
http://www.django-rest-framework.org/api-guide/pagination/
In ListApiView Overwrite finalize_response function. I did not find any other inbuilt method or parameter to set limit on response data.
from rest_framework.generics import ListAPIView
class BoolQSearch(ListAPIView):
queryset = Model1.objects.all()
serializer_class = Model1Serializer
def finalize_response(self, request, response, *args, **kwargs):
response.data = response.data[:5]
return super().finalize_response(request, response, *args, **kwargs)
You can extend or customize pagination classes available in drf
class UserSpecificPagination(LimitOffsetPagination):
def get_limit(self, request):
if logic_met(request.user):
self.max_limit = custom_limit
return super(UserSpecificPagination, self).get_limit(request)
set the class as pagination_class in ListAPIView or DRF settings
you should use the pagination api from django-rest
http://www.django-rest-framework.org/api-guide/pagination/
look at the limit option
I have a model which contains a ManyToMany to User to keep track of which users have 'favorited' a particular model instance.
In my API for this model, when requested by an authenticated user, I'd like to include an 'is_favorite' boolean. However, it seems that any api fields that aren't straight model attributes must be implemented as a class method, which when called in Piston does not get a reference to the request object, and therefore I have no way to know who the current user is.
From the Piston docs:
In addition to these, you may define any other methods you want. You can use these by including their names in the fields directive, and by doing so, the function will be called with a single argument: The instance of the model. It can then return anything, and the return value will be used as the value for that key.
So, if only the Piston CRUD methods get an instance of the request, how can my classmethod fields generate output which is relevant to the current authenticated user?
I am not aware of the piston API, but how about using the thread locals middleware to access the request
add this to middleware
try:
from threading import local
except ImportError:
from django.utils._threading_local import local
_thread_locals = local()
def get_request():
return getattr(_thread_locals, 'request', None)
class ThreadLocals(object):
def process_request(self, request):
_thread_locals.request = request
and update the settings with the ThreadLocals middleware
and wherever you want to access the request import get_request from middleware
if you want to just get the current user, modify the middleware to set only request.user in thread locals
From the piston wiki page it says that you may specify the contents of foreign keys and many to many fields by nesting attributes. In your case
class FriendHandler(BaseHandler):
allowed_methods = ('GET',)
model = User
fields = ('userfield_1', 'userfield_2', ('friends', ('is_friended')))
def read(self, request):
# Anything else you might want to do, but return an object of type User
# Or whatever your model happens to be called
EDIT: Another slightly hacky way to do it (if you don't want the friend to get passed at all if the is_friended is false) would be to manually create a dict object structured how you like, and then return it. piston processes the dict a works with the built in emitters (the JSON one for sure, haven't tried the others)