How to remember objects to be treated after QuerySet.update() - django

I want to update the bar attribute of a bunch of Foo objects using the QuerySet's update() method. Then I want to do something with the modified objects, but I can no more identify them using the bar attribute.
Somehow it doesn't work by remembering the ids.
old_bar = Bar.objects.get(id=1)
new_bar = Bar.objects.get(id=2)
foo_query = Foo.objects.filter(bar=old_bar)
foo_ids = foo_query.values_list('id', flat=True)
print len(foo_ids), foo_query.count(), Foo.objects.filter(id__in=foo_ids).count()
# outputs "42 42 42"
print foo_query.update(bar=new_bar)
# outputs "42"
print len(foo_ids), foo_query.count(), Foo.objects.filter(id__in=foo_ids).count()
# outputs "42 0 0"
Does the update() method modify the ids or what am I doing wrong?

Querysets are lazy, so they are evaluated everytime you use them! Therefore in your example foo_query will be evaluated more than once and return something different depending on the actual objects in your database. foo_ids is therefore not a list of ids, but a ValuesQueryset that evaluates to a list.
Forcing it evaluate to a list should make your example work, as the ids do not change to reflect the actual state of the database: foo_ids = list(foo_query.values_list('id', flat=True)).

Related

Django, Queryset exists, but accessing [0] results in index error

creator_payouts = self.filter(paid=False)
processed_user_ids = []
if not creator_payouts.exists(): // here I check the existence
return
for creator_payout in creator_payouts:
do_something()
creator_payouts.update(paid=True) // maybe this does something?
CreatorRecord.objects.filter(
user__in=processed_user_ids,
created_for__lte=creator_payouts[0].created_for // error here
).update(processed_for_payout=True)
I'm getting index error at creator_payouts[0] at the end of the code above.
Why am I getting the error, especially I rule out the empty list case by creator_payouts.exists() condition above
Well when you update the creator_payouts, then the database is updated with paid=True. As creator_payouts it a lazy queryset and and when you call creator_payouts[0] to evaluate it, it gets empty queryset. So you need to store the intial values somewhere with forceful evaluation. Like this:
if not creator_payouts.exists(): // here I check the existence
return
for creator_payout in creator_payouts:
# or store the first creator_payout in a variable here
do_something()
unpaid_creator_payouts_list = list(creator_payouts) # force evaluation
creator_payouts.update(paid=True)
CreatorRecord.objects.filter(
user__in=processed_user_ids,
created_for__lte=unpaid_creator_payouts_list[0].created_for // or use the first stored payout_creator in previous loop
).update(processed_for_payout=True)
Or run the update operation after CreatorRecord filter.
creator_payouts is queryset. If want get first better try creator_payouts.first() instead of creator_payouts[0].
CreatorRecord.objects.filter(
user__in=processed_user_ids,
created_for__lte=creator_payouts.first().created_for
).update(processed_for_payout=True)

Removing a word from a list which appears one after the other

Lets say i have a list of words as shown below,
A=['ab','bc','cd','de','de','ef','bc']
I tried to use sets as seen below,
def remove_similar_words(self,sent):
original_set = set()
result = []
for item in sent:
if item not in original_set:
original_set.add(item)
result.append(item)
return result
sent is list A.
The result of the above method would be
result=['ab','bc','cd','de','ef']
But, i need the result to be,
needed_result=['ab','bc','cd','de','ef','bc']
Question : How can i change my code to cater the requirement of producing the list needed_result?
Also i would like to avoid sets since i need to preserve the order.
def remove_similar_words(sent):
last = ""
result = []
for item in sent:
if item != last:
last=item
result.append(item)
return result
A=['ab','bc','cd','de','de','ef','bc']
print (", ".join(remove_similar_words(A)))
It would seem you just need to check against the prior value in your iterator, not build a complete original_set.

Why does `get_or_create` execute expression from defaults when object is found?

lang_group = 'en'
for place_category in place['categories']:
translation, created = \
PlaceTypesTranslations.objects.get_or_create(
name=place_category, lang_group=lang_group,
defaults={'place_type_group': PlaceTypesGroups.objects.create()})
In this case if the loop has 1000 iterations and, for example, 500 times created=True and other 500 times created=False, anyway will be created 1000 PlaceTypesGroups, so for some reason even if get_or_create returns get, defaults anyway is executed.
The same algorithm, but different approach:
lang_group = 'en'
for place_category in place['categories']:
if PlaceTypesTranslations.objects.filter(name=place_category, lang_group=lang_group).exists():
place_cat_trans = PlaceTypesTranslations.objects.get(name=place_category, lang_group=lang_group)
place_group = place_cat_trans.place_type_group
else:
place_group = PlaceTypesGroups.objects.create()
place_cat_trans = PlaceTypesTranslations.objects.create(name=place_category,
lang_group=lang_group,
place_type_group=place_group)
In this case just 500 times will be created PlaceTypesGroups as expected.
Why is that? What I do not see in the 1st case? Why does get_or_create creates 1000 PlaceTypesGroups?
That's just the way Python expressions always work. Anything inside an expression must always be fully evaluated before the expression itself can be passed to a function or method.
However, Django specifically lets you pass a callable, rather than a value, in the defaults hash. So you can do:
PlaceTypesTranslations.objects.get_or_create(
name=place_category, lang_group=lang_group,
defaults={'place_type_group': PlaceTypesGroups.objects.create})
and it will call the create method as required.
it's called 1000x, because you are assigning the returned value from the function. I'll start with a simple example:
place_type_group = some_function()
The variable now contains whatever the function returns, right?
Now if you wrap it into a dictionary, it's still the same thing, just wrapper into a dictionary:
dict(place_type_group = some_function())
the element in a dict still contains the value returned from some_function()
The dictionary above is just equal for the following code, which is what you do in your code (ie. assigning function return value into a variable)
{'place_type_group': some_function() }

Dictionary error in Python 2.7

I have a file in the format:
0000 | a1_1,a3_2 | b2_1, b3_2
0001 | a1_3 | b4_1
and I'm trying to create a dictionary which has
{ 'a1' : set(['b2', 'b3', 'b4']), 'a3': set(['b2', 'b3']) }
and this is how my code looks like:
def get_ids(row, col):
ids = set()
x = row.strip().split('|')
for a in x[col].split(','):
ids.add(a.split('_')[0])
return ids
def add_to_dictionary(funky_dictionary,key, values):
if key in funky_dictionary:
funky_dictionary[key].update(values)
else:
funky_dictionary[key] = values
def get_dict(input_file):
funky_dictionary = {}
with open(input_file,'r') as ip:
for row in ip:
a_ids = get_ids(row,1)
b_ids = get_ids(row,2)
for key in a_ids:
add_to_dictionary(funky_dictionary,key,b_ids)
return funky_dictionary
So my problem is this when I lookup values for certain key in the dictionary, it returns me with way more values than expected. E.g.
For the above example the expected value of a3 would be set(['b2', ' b3'])
However with the code, I'm getting set(['b2', ' b3', 'b4'])
I cant figure out whats wrong with the code. Any help?
The issue you have is that many of your dictionary's values are in fact references to the same set instances. In your example data, when the first line is processed, 'a1' and 'a3' both get mapped to the same set object (containing 'b2' and 'b3'). When you process the second line and call update on that set via the key 'a1', you'll see the added value through 'a3' too, since both values are references to the same set.
You need to change the code so that each value is a separate set object. I'd suggest getting rid of add_to_dictionary and just using the dictionary's own setdefault method, like this:
for key in a_ids:
funky_dictionary.setdefault(key, set()).update(b_ids)
This code always starts with a new empty set for a new key, and always updates it with new values (rather than adding a reference to the b_ids set to the dictionary directly).

Django OR query

How would I do:
FinancialStatements.objects.get(statement_id=statement_id)
or SalesStatements.objects.get(statement_id=statement_id)
The result will always yield one result.
I ended up using the try/except route here:
try:
statement_object = FinancialStatements.objects.get(statement_id=statement_id)
except FinancialStatements.DoesNotExist:
statement_object = SalesStatements.objects.get(statement_id=statement_id)
Why not simply do:
result = (FinancialStatements.objects.filter(statement_id=statement_id) or
SalesStatements.objects.filter(statement_id=statement_id))
This should work, because filter returns a list - and an empty list if no entries match. An empty list evaluates to false in python's boolean logic, e.g. try running:
print [] or "hello"
(Just as a check, compare print ["Hi"] or "hello")
So, if the first query returns empty, the second will then be run. However, if the first matches anything, this will be result and the second query will be ignored.
Addendum: result will then be of a list type - you'll need to extract the (one and only) element with result[0].