Incrementing specific parameter in all objects in the QuerySet - django

I am trying to increment the same parameter in all objects in the query set.
What I am doing right now:
q = SomeModel.objects.all()
for object in q:
object.my_parameter += 1
object.save()
I have wondered if it could be achieved in a simpler way, e.g. using update() function. To put it simply I would like to do something like this:
SomeModel.objects.all().update(my_parameter += 1)
I just can't believe that there is no shortcut for what I want to do.
Edit:
Resolved! Thank you!

You can do this by F() expressions
from django.db.models import F
SomeModel.objects.all().update(my_parameter=F('my_parameter') + 1)
Further reading https://docs.djangoproject.com/en/2.0/ref/models/expressions/#f-expressions

Certainly you could do it in a single bulk update. Check this out: Updating multiple objects at once.
I prefer to use the F expression too:
from django.db.models import F
SomeModel.objects.all().update(my_parameter =F('my_parameter') + 1)

Related

Use and, or conditions while using Case,When

Anyone know in the following query how can I use AND condition?
q = Food.objects.all().annotate(open=Case(When(days__day=today,then='blahblah'),When(start_time__lte=now and end_time__gte=now,then='blabla')))
On the second when I want to check if the now value is between start and end time but it seems like the 'and' keyword is not working there
Use Q objects so:
from django.db.models import F, Q, When
q = Food.objects.annotate(open=Case(
When(days__day=today, then='blahblah'),
When(Q(start_time__lte=now) & Q(end_time__gte=now), then='blabla'),
)
Use & to AND two Q objects and | to OR them.

Filter objects or (if 0 found) get all objects in Django with one DB hit

Is there a way in Django to achieve the following in one DB hit (Debug Toolbar shows 2 queries)?
q = SomeModel.objects.filter(name=name).order_by(some_field)
if q.count() == 0:
q = SomeModel.objects.all().order_by(some_field)
I want to check if there are objects with a given name. If yes, then return them. If not, return all objects. All done in one query.
I've checked Subquery, Q, conditional expressions but still don't see how to fit it into one query.
Ok, much as I resisted (I still think it's premature optimization), curiosity got the better of me. This is not pretty but does the trick:
from django.db.models import Q, Exists
name_qset = SomeObject.objects.filter(name=name)
q_func = Q(name_exists=True, name=name) | Q(name_exists=False)
q = SomeModel.objects.annotate(
name_exists=Exists(name_qset)
).filter(q_func).order_by(some_field)
Tried it out and definitely only one query. Interesting to see if it is actually appreciably faster for large datasets...
You best bet is to use .exists(), otherwise your code is fine
q = SomeModel.objects.filter(name=name).order_by(some_field)
if not q.exists():
q = SomeModel.objects.all().order_by(some_field)

How to create a series of variables automatically?

How can I create a series of variables automatically with python?
Like this:
tmp1=1;
tmp2=1;
tmp3=1;
tmp4=1;
tmp5=1;
Look at this SO question. you have several ways to do that:
Use dict or collection instead
Using globals
Using the exec() method
About the first solution (dict or collection) - is not actually what you asked for, which is to create a variable in the global scope.. but I would go with that anytime. I don't see really any reason why I'd need to create variables dynamically instead of using some datatype.
I would say that using both globals and exec() method for this is a bad practice.
Store them in a dictionary
d = {}
value = ['a','b','c']
for key in range(1, 3):
d[key]= value[key]
print d
> {0:'a',1:'b',2:'c'}
print d[0]
> 'a'
(Comments? I am new to python too!)
I think I have got an answer somewhere:
for i in range(100):
locals()['tmp%d'%i]=1
or:
>>> for i in range(1, 10):
... exec 'tmp' + str(i) + '=1'
I don't know if I have a ambiguity describe, the above two is exactly I want.

Concatenate queryset in django

I want to concatenate two queryset obtained from two different models and i can do it using itertools like this:
ci = ContributorImage.objects.all()
pf = Portfolio.objects.all()
cpf = itertools.chain(ci,pf)
But the real fix is paginating results.If i pass a iterator(cpf, or our concatenated queryset) to Paginator function, p = Paginator(cpf, 10), it works as well but fails at retrieving first page page1 = p.page(1) with an error which says:
TypeError: object of type 'itertools.chain' has no len()
What can i do in case like this ?
The itertools.chain() will return a generator. The Paginator class needs an object implementing __len__ (generators, of course do not support it since the size of the collection is not known).
Your problem could be resolved in a number of ways (including using list to evaluate the generator as you mention) however I recommending taking a look at the QuerySetChain mentioned in this answer:
https://stackoverflow.com/a/432666/119071
I think it fits exactly to your problem. Also take a look at the comments of that answer - they are really enlightening :)
I know it's too late, but because I encountered this error, I would answer to this question.
you should return a list of objects:
ci = ContributorImage.objects.all()
pf = Portfolio.objects.all()
cpf = itertools.chain(ci,pf)
cpf_list = list(cpf)

Django 'objects.filter()' with list?

It is possible to limiting QuerySet in this kind of way:
creators_list = ['jane', 'tarzan', 'chita']
my_model.objects.filter(creator=creators_list)
???
You mean like this?
my_model.objects.filter(creator__in=creator_list)
Docs: http://docs.djangoproject.com/en/dev/ref/models/querysets/#in
EDIT
This is now a bit outdated. If you run into problems with the original code, try this:
from django.db.models import Q
my_filter_qs = Q()
for creator in creator_list:
my_filter_qs = my_filter_qs | Q(creator=creator)
my_model.objects.filter(my_filter_qs)
There's probably a better way to do it but I'm not able to test it at the moment.
Also if you're using sqlite and running into problems, there exists a limitation for the max number of items in the list.
def divideChunks(l, n):
for i in range(0, len(l), n):
yield l[i:i + n]
for slicerange in divideChunks(objs, 10):
myobjects = my_model.objects.filter(creator__in = slicerange)
...