How to exclude empty values when querying a DRF endpoint URL [Django] - django

Similar to an exclude filter that looks like this:
MyObject.objects.exclude(my_field="")
I know that I can modify the view to exclude the value in the queryset but that's not dynamic:
def get_queryset(self):
return Client.objects.exclude(my_field="")
But I'm looking for a less static way of querying via URL. So something like this:
/api/object?my_field__isempty=False

#Hashem's answer is good, but you could also use Q statements
from django.db.models import Q
# Q allows for complex queries with:
# & (AND)
# | (OR)
# ^ (XOR)
# Exclude method
ModelName.objects.exclude(Q(field_name__isnull=True) | Q(field_name__exact=''))
# Filter Method with ~ (NOT)
ModelName.objects.filter(~Q(field_name__isnull=True) & ~Q(field_name__exact=''))
Just throwing out another option, and knowing about Q statements is really beneficial
Docs: https://docs.djangoproject.com/en/4.1/topics/db/queries/#complex-lookups-with-q
Edit
Here's an extra tidbit, you'll find this handy, you can use pointers and filter by a dictionary. The dictionary can be created dynamically
filterDict = {'my_field':''}
Client.objects.exclude(**filterDict)
# Client.objects.exclude(**filterDict) === Client.objects.exclude(my_field=='')
I'm not sure how you are doing Views, but if you have a "normal" one with the request object you you can fetch the GET parameters as a QueryDict:
def myview_with_dynamic_pointer(request):
print(request.GET)
# Should work
Client.objects.filter(**request.GET)
# will work
Client.objects.filter(**{k:v for k,v in request.GET.items()})
def myview_with_dynamic_Q(request):
print(request.GET)
from django.db.models import Q
dynamicquery = Q()
for key, value in request.GET.items():
dynamicquery = dynamicquery & Q({k:v})
# Can also do OR:
# dynamicquery = dynamicquery | Q({k:v})
Client.objects.filter(dynamicquery)
If you are using class based views, it'll be more like:
class MyListView(ListView):
model = MyModel
def get_queryset(self):
print(self.request.GET)
return self.model.objects.filter(**self.request.GET)
It might be a good idea to look over the GET before shoving it right into a filter.. Safety wise

If I got you correctly,
If you need to exclude null values and empty strings
ModelName.objects.exclude(field_name__isnull=True).exclude(field_name__exact='')
Or you can use one of them, this equal to
NOT field_name='' AND field_name IS NOT NULL

Related

Best practice for validating a date in url in django?

I have a logging system, where users can log several symptoms for any given day. Im getting the date as slug in my url, but I need to validate it. Which one is best practice, and why?
make a validator function in the class view and use it there
add a hidden form field, and write a custom DateValidator for it?
You can define a path converter that will parse date objects. You can define a custom pattern with:
# app_name/converters.py
class DateConverter:
regex = '[0-9]{4}-[0-9]{2}-[0-9]{2}'
format = '%Y-%m-%d'
def to_python(self, value):
return datetime.strptime(value, self.format).date()
def to_url(self, value):
return value.strftime(self.format)
Next we can register that path converter [Django-doc] and work with:
from app_name.converters import DateConverter
from django.urls import path, register_converter
register_converter(DateConverter, 'date')
urlpatterns = [
# …
path('some/path/<date:date>/', some_view),
# …
]
This will pass a single date parameter to the view, which is a date object, you thus can work with:
def some_view(request, date):
# …
If you thus visit the path /some/path/2021-10-17, date will be a date(2021, 10, 17) object.

How can I update a specific key on a HStoreField in django?

I have a Django model with a HStoreField and I'm trying to update a specific key on the data in this field. I'm trying not to load the model into memory as there's a fair bit of data and it would be for a lot of object instances.
I have my Result object which has 'data' field and I'm trying to update an 'existingkey', I've tried:
Result.objects.update(data__existingkey='new_value')
But I just get FieldDoesNotExist: ResultsContainer has no field named 'data__existingkey'
I thought this would work as Result.objects.filter(data__existingkey='value') works fine. Any suggestions would be appreciated many thanks
The first place I would look is django HStoreField docs. Finding nothing relevant there I'd assume that such feature, if it exists, is not implemented by Django.
Thus, my search are now aiming at PostgreSQL hstore docs. After browsing this document, I can't find any function which purpose is clearly to update a specific key of a hstore. So I check each function in details to figure out if any could be used for such purpose.
hstore || hstore → hstore
Concatenates two hstores.
'a=>b, c=>d'::hstore || 'c=>x, d=>q'::hstore → "a"=>"b", "c"=>"x", "d"=>"q"
That's a bingo! Using the || operator, we can do something like UPDATE "a" SET "data" = "a"."data" || hstore('existing_key=>new_value').
Now, as it's not implemented by Django, let's implement it ourselves:
import re
from django.contrib.postgres.fields import HStoreField
from django.db.models import Func, Value
def hstore_def_escape(s):
return re.sub(r'([\\"])', r'\\\1', s)
class HStore(Func)
function = 'hstore'
output_field = HStoreField()
def __init__(self, expression, **kwargs):
if isinstance(expression, dict):
expression = Value(
','.join(
'"{}" => "{}"'.format(hstore_def_escape(k), hstore_def_escape(v))
for k, v in expression.items()
)
)
super().__init__(expression, **kwargs)
class ConcatHStore(Func):
arg_joiner = ' || '
template = '%(expressions)s'
output_field = HStoreField()
def __init__(self, *expressions, **kwargs):
expressions = [
HStore(e) if isinstance(e, dict) else e
for e in expressions
]
super().__init__(*expressions, **kwargs)
Now you can do:
from django.db.models import F
Result.objects.update(data=ConcatHStore(F('data'), {'existing_key': 'new_value'}))

Django URLs: Can't get the keyword arg into my view?

The URL pattern having issues is:
url(r'^$', business_list, name='business_list_home'),
url(r'^(?P<param>\w+)$', business_list, name='business_list_results'),
My view is:
#render_to('app/business_list.html')
def business_list(request, param=None):
queryset = Business.objects.all()
search_form = SearchForm
print request.GET
if param in request.GET:
param = request.GET.get('param')
if queryset.filter(city__iexact=param).exists():
queryset = queryset.filter(city__iexact=param)
elif queryset.filter(category__iexact=param).exists():
queryset = queryset.filter(category__iexact=param)
print queryset
else:
queryset = None
print queryset
return {'business_list': queryset, 'search_form': search_form}
Essentially, I don't understand why when I go to /Miami I don't have access to it via request.GET['param'] in the view? print request.GET prints <QueryDict: {}>
The reason I want to do this is to have a nice URL scheme for displaying results of businesses for the city or category (and that's why I check if it's a city or category in the view too) in the url. Let me know if there's a better way to accomplish this like
url(r'^$', business_list),
url(r'^(?P<city>\w+)$', business_list),
url(r'^(?P<category>\w+)$', business_list),
url(r'^(?P<category>\w+)/(?P<city>\w+)$', business_list),
Thanks for any help!
business_list is a function that takes a request and a parameter called "param" (should probably give it a better name as to make it a little less generic). In Django, the URL routes can define what values get passed to the parameters of the view functions.
In the situation above, when a user goes to /Miami, Django will try to match the string "Miami" with any of the regular expressions defined in the URL routes. In this case, the matching expression is ^(?P<param>\w+)$. When this match is made, the string Miami is captured into param.
Django will then call business_list(request, param="Miami"). Note that there were no query parameters passed in the URL (e.g., /Miami?color=blue).
The issue in the code you've written above is that you're checking to see not if param exists, but rather that param was passed in the query parameters. Try going to the URL /Miami?param=test and it'll probably work the way that you expected.
The real fix here is to not reference request.GET, because using GET parameters is exactly what you're trying to avoid. So, instead of
if param in request.GET:
param = request.GET.get('param')
if queryset.filter(city__iexact=param).exists():
queryset = queryset.filter(city__iexact=param)
elif queryset.filter(category__iexact=param).exists():
queryset = queryset.filter(category__iexact=param)
print queryset
Just do:
if param:
if queryset.filter(city__iexact=param).exists():
queryset = queryset.filter(city__iexact=param)
elif queryset.filter(category__iexact=param).exists():
queryset = queryset.filter(category__iexact=param)

How to use annotate() with Haystack in Django?

I have a function that search with hatstack, and I need to get the comments of each object that haystack get in the array, I have this:
def search(request):
if 'q' in request.GET and request.GET['q']:
q = request.GET['q']
results = SearchQuerySet().auto_query(q)
things = []
for r in results:
things.append(r.object)
return render_to_response('resultados.html',
{'things': things, 'query': q}, context_instance=RequestContext(request))
How I append to the results the number of comments that each object have?
If I add annotate, debugger throw me: SearchQuerySet has not 'annotate' attribute
SearchQuerySet isn't the ORM query set you're familiar with. It only imitates it. Annotations doesn't make sense with search engines as well. You need to put already prepared data to an index.
Just make another query using ORM.

Next previous links from a query set / generic views

I have a quite simple query set and a related generic views:
f_detail = {
'queryset': Foto.objects.all(),
'template_name': 'foto_dettaglio.html',
"template_object_name" : "foto",
}
urlpatterns = patterns('',
# This very include
(r'^foto/(?P<object_id>\d+)/$', list_detail.object_detail, f_detail, ),
)
Just a template for generating a detail page of a photo: so there's no view.
Is there an easy way to have a link to previous | next element in the template
without manualy coding a view ?
Somthing like a:
{% if foto.next_item %}
Next
{% endif}
class Foto(model):
...
def get_next(self):
next = Foto.objects.filter(id__gt=self.id)
if next:
return next.first()
return False
def get_prev(self):
prev = Foto.objects.filter(id__lt=self.id).order_by('-id')
if prev:
return prev.first()
return False
you can tweak these to your liking. i just looked at your question again... to make it easier than having the if statement, you could make the methods return the markup for the link to the next/prev if there is one, otherwise return nothing. then you'd just do foto.get_next etc. also remember that querysets are lazy so you're not actually getting tons of items in next/prev.
The Foto version above has a couple of shortcomings:
Doing a boolean evaluation like if next: can be slow since it basically loads the entire QuerySet result. Use next.exists() or the try/except like in my version.
The get_prev() result is wrong because you need to reverse the ordering in this case.
So FWIW here is my version, which is for a generic primary key:
def get_next(self):
"""
Get the next object by primary key order
"""
next = self.__class__.objects.filter(pk__gt=self.pk)
try:
return next[0]
except IndexError:
return False
def get_prev(self):
"""
Get the previous object by primary key order
"""
prev = self.__class__.objects.filter(pk__lt=self.pk).order_by('-pk')
try:
return prev[0]
except IndexError:
return False
If you'll accept Model.objects.all() as your queryset, and you are ok with grabbing next / previous items by a date field (usually a 'created' field with auto_now_add=True will give the same order as object id's), you can use get_next_by_foo() and get_previous_by_foo(), where 'foo' is the date field.
For next / previous links from a more complicated QuerySet, using the Paginator with threshold set to one seems like it might be the best option.