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

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)

Related

Finding and returning the value at location index in a list

Could my current code work for this function? Here is the goal of what I am trying to do first and then my current attempt
Look at list of values, xs. Find and return the value at location index. When index is invalid for xs, return response instead.
Current code:
def get(xs, index, response=None):
xs=[]
i=0
for i in xs:
try:
while i<len(xs):
index=xs.index(xs,index)
index = i
return i
except:
return response
Thanks any help is appreciated
You seem to be very confused about what you are trying to do...
So if I understand correctly you want to return xs[index] if it exists otherwise response, a simple way to do that following the EAFP principle is:
def get(xs, index, response=None):
try:
return xs[index]
except IndexError:
return response
Plenty of issues here, let's break them down by segments:
You re-assign the name of the list you pass in thereby effectively losing the list you're searching in:
xs=[]
there's no need to do that, that line can be removed.
You assign a value to i (reminiscent of C) which gets overwritten when the loop begins and you use a for-loop when you don't really need to:
i=0
for i in xs:
again, this too can be removed.
You use a try-except with a bare except; you should specify the exception which could be raised here which is ValueError.
try:
# content
except ValueError:
return response
You're using a while loop getting the index (and specifying start), re-assigning it, and then returning it; it doesn't make much sense. You're really looking for return xs.index(index).
Again, you could really replace all that code with xs.index(index)
All in all, your function could look like this:
def get(xs, index, response=None):
try:
return xs.index(index)
except ValueError:
return response

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() }

TypeError during executemany() INSERT statement using a list of strings

I am trying to just do a basic INSERT operation to a PostgreSQL database through Python via the Psycopg2 module. I have read a great many of the questions already posted regarding this subject as well as the documentation but I seem to have done something uniquely wrong and none of the fixes seem to work for my code.
#API CALL + JSON decoding here
x = 0
for item in ulist:
idValue = list['members'][x]['name']
activeUsers.append(str(idValue))
x += 1
dbShell.executemany("""INSERT INTO slickusers (username) VALUES (%s)""", activeUsers
)
The loop creates a list of strings that looks like this when printed:
['b2ong', 'dune', 'drble', 'drars', 'feman', 'got', 'urbo']
I am just trying to have the code INSERT these strings as 1 row each into the table.
The error specified when running is:
TypeError: not all arguments converted during string formatting
I tried changing the INSERT to:
dbShell.executemany("INSERT INTO slackusers (username) VALUES (%s)", (activeUsers,) )
But that seems like it's merely treating the entire list as a single string as it yields:
psycopg2.DataError: value too long for type character varying(30)
What am I missing?
First in the code you pasted:
x = 0
for item in ulist:
idValue = list['members'][x]['name']
activeUsers.append(str(idValue))
x += 1
Is not the right way to accomplish what you are trying to do.
first list is a reserved word in python and you shouldn't use it as a variable name. I am assuming you meant ulist.
if you really need access to the index of an item in python you can use enumerate:
for x, item in enumerate(ulist):
but, the best way to do what you are trying to do is something like
for item in ulist: # or list['members'] Your example is kinda broken here
activeUsers.append(str(item['name']))
Your first try was:
['b2ong', 'dune', 'drble', 'drars', 'feman', 'got', 'urbo']
Your second attempt was:
(['b2ong', 'dune', 'drble', 'drars', 'feman', 'got', 'urbo'], )
What I think you want is:
[['b2ong'], ['dune'], ['drble'], ['drars'], ['feman'], ['got'], ['urbo']]
You could get this many ways:
dbShell.executemany("INSERT INTO slackusers (username) VALUES (%s)", [ [a] for a in activeUsers] )
or event better:
for item in ulist: # or list['members'] Your example is kinda broken here
activeUsers.append([str(item['name'])])
dbShell.executemany("""INSERT INTO slickusers (username) VALUES (%s)""", activeUsers)

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].

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

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)).