Django queryset interpolation for a previous / next instance function - python

Writing a dry function that returns either previous or next instances of a given instance.
This function return previous instances:
def previous(instance):
try:
return Picture.objects.filter(id__lt=instance.id).first()
except Picture.DoesNotExist:
return instance
I want to create an abstracted function which returns either the previous or the next instance using an additional gt_or_lt argument. The problem lies in interpolating that argument into the filter(id__gt_or_lt).
def seek_instance(gt_or_lt, instance):
try:
return Picture.objects.filter(id__gt_or_lt=instance.id).first()
except Picture.DoesNotExist:
return instance
I've tried:
return Picture.objects.filter(id__gt_or_lt = instance.id).first()
seek_instance("gt", instance)
return Picture.objects.filter(id__f"{gt_or_lt}" = instance.id).first()
seek_instance("gt", instance)
return Picture.objects.filter(f"{gt_or_lt}" = instance.id).first()
return Picture.objects.filter(gt_or_lt = instance.id).first()
seek("id__gt", instance)
All fail with their respective errors.

Use a dictionary with kwargs expansion.
return Picture.objects.filter(**{f"id__{gt_or_lt}": instance.id})

You can use dictionary expansion, like #DanielRoseman suggests. But that will still not per se render the previous, or next item. If for example the model has an ordering option [Django-doc], then it is possible that the order is different than on the id. Furthermore, for the previous one, you will need to reverse the ordering.
Furthermore depending on the situation, you might want to prevent that seek_instance can be given a different lookup, like 'in' for example.
We can thus use an if … elif … else here to branch on the item we wish to retrieve, and raise a ValueError in case you use some other lookup:
def seek_instance(lt_or_gt, instance):
try:
if lt_or_gt == 'lt':
return Picture.objects.filter(pk__lt=instance.pk).order_by('-pk').first()
elif lt_or_gt == 'gt':
return Picture.objects.filter(pk__gt=instance.pk).order_by('pk').first()
else:
raise ValueError("Should be 'lt' or 'gt'")
except Picture.DoesNotExist:
return instance

Related

How to rapidly check object fields for emptyness

I need to check for empty values of every field of a distinct object. And I'm tiered of typing it out.
In this case. I have the an object called signal with multiple fields, which should not be empty.
if self.is_blank(signal.provider_id):
error_response = "Signal rejected. No signal provider id given."
elif self.is_blank(signal.sequence_id):
error_response = "Signal rejected. No signal sequence id provided."
....
def is_blank (self, string):
"""Checks for None and empty values"""
return True if string and string.strip() else False
Anyhow, what is the fast way in python to check all fields for "emptiness"? How do we loop them?
You may want to use operator.attrgetter:
def is_blank(self, field_names):
for name in field_names:
if getattr(self, name) and getattr(self, name).strip():
return True, name
return False, None
...
is_blank, name = self.is_blank(['provider_id', 'sequence_id', ...])
if is_blank:
print(f'Signal rejected. No signal {name} provided.')
You can also implement is_blank with next:
def is_blank(self, field_names):
return next(
((True, name)
for name in field_names
if getattr(self, name) and getattr(self, name).strip()),
(False, None),
)
This is going to print an error message for the first field that is failing the check. All you need to do is to provide a complete list of the attributes to be checked.
As rostamn mentioned, you can convert your object into a dictionary,
after which you can loop through the (key, values) in a single line with a filter and check the result like so:
any_empty = any([True for x, y in your_obj.__dict__.items() if not y])
Change the condition in the loop to the type of empty check you need.
To loop over all instance properties you use my_instance.__dict__
see this answer for details: Explain __dict__ attribute

Python Decorator recreating Locals()

Some of my functions use a "fail_silently" flag. It is used in the following way:
def test(a, b, c=1, fail_silently = False)
try:
return int(a) + c
except:
if fail_silently:
return None
raise
Therefore, if there is an error, we catch it and fail gracefully. The key here is that it can be toggled dynamically by whoever is calling it.
I am using this for a many different functions and class methods and thought to make it a decorator.
There are a few problems:
I want to be able name the flag "raise_exception" or "fail_silently" or "whatever"...
The flag may or may not have a default value
The flag may or may not be passed in (usually not)
It needs to work with class methods too
Thus my decorator needs to look for (assuming the flag is called "fail_silently")
the flag in the following locations in this order
**kwargs (the passed in function arguments), simple dictionary get on flag
*args - get the positional argument of the flag, then scan for index in args (which might not be there)
Get the default value of the flag from the function
The problem is the code is now getting really messy and has many points of failure.
def try_except_response(parameter = 'fail_silently'):
def real_decorator(func):
#functools.wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs) # the function itself,
except: # if it raises an Exception!
# check to see if the Flag is in passed in kwargs!
if kwargs.get(parameter)== True:
return None
elif kwargs.get(parameter) == False:
raise
else:
# Flag is not in kwargs, check to see if it is in args
function_args, vargs, kewords, defaults = inspect.getargspec(func) # get the index of argument of interest
try:
argument_index = function_args.index(parameter) # get the index of argument of interest
except ValueError:
raise ValueError('The inputted decorator for "fail_silently" was not found! The behavior will not work.')
if len(args) > argument_index: # check to see if it is inputted
keyword = args[argument_index] # get the value!!
if keyword == True:
return None
elif kwargs == False:
raise
else:
raise ValueError('The "fail_silently" flag did not return a value that we understand. Must be True or False')
else:
# not in args either! let's go get the default value.
# TODO MORE STUFF!!!
raise
raise
raise
return wrapper
What is the best way this can be cleaned up? Or alternatives? I am thinking about implementing a version of locals() that will create a dictionary of all parameters passed into the function and then check from there...

Dynamically assigning variable with locals() isn't working in recursive function

I have a recursive script that's scraping a JSON file for cars. At each recursive level, it gets a new variable added, and passes that (along with the other values) on to the recursive call, each time getting more and more detailed in the information. I tried to use locals() to dynamically assign a variable, but it remains None even after the call (I recall seeing that sometimes locals() is read only).
I tried using eval() as well, and it gives me the same issue (I know eval is not ideal). I'd ideally like to avoid using a dictionary, because that would require me to load it with values first, which seems like it has some unnecessary steps, but I'm open to anything at this point.
Example:
scraper(manufacturer='Honda') would scrape a JSON file of models, set model='Accord' and then recursively call
scraper(manufacturer='Honda, model='Accord') which scrapes a file of years, set's year=2014 and recursively calls
scraper(manufacturer='Honda', model='Accord', year='2014') which is the base case
def scraper(self, manufacturers, model=None, year=None):
if year:
scrapeurl = '%s&manufacturer=%s&model=%s&year=%s' % (url, manufacturer, model, year)
return someFinalFunction()
elif model:
scrapeurl = '%s&manufacturer=%s&model=%s' % (url, manufacturer, model)
elif manufacturer:
scrapeurl = '%s&manufacturer=%s' % (url, manufacturer)
j = getJSONFromUrl(scrapeurl)
key, values = j.popitems()
for value in values:
locals()[key] = value
return self.scraper(manufacturer, model, year, color)
I'd appreciate any input on how to handle this, I know Python always seems to have some clever ways of doing things, and I'm always learning more about it, so thank you in advance! I'm using Python3 in this example too, if that changes anything
locals()['key'] = value should be locals()[key] = value
Better yet, use **kwargs:
def scraper(self, manufacturer, model=None, year=None):
kwargs = dict(manufacturer=manufacturer, model=model, year=year)
if year:
scrapeurl = '%s&manufacturer=%s&model=%s&year=%s' % (url, manufacturer, model, year)
return someFinalFunction()
elif model:
scrapeurl = '%s&manufacturer=%s&model=%s' % (url, manufacturer, model)
elif manufacturer:
scrapeurl = '%s&manufacturer=%s' % (url, manufacturer)
j = getJSONFromUrl(scrapeurl)
key, values = j.popitems()
for value in values:
kwargs[key] = value
return self.scraper(**kwargs)
It's not entirely clear what you're trying to do, but perhaps this will help:
def scraper(self, **kwargs):
if kwargs.get('year') is not None:
scrapeurl = '{0}&manufacturer={manufacturer}&model={model}&year={year}'
return someFinalFunction() # not sure why this takes no arguments
elif kwargs.get('model') is not None:
scrapeurl = '{0}&manufacturer={manufacturer}&model={model}'
elif kwargs.get('manufacturer') is not None:
scrapeurl = '{0}&manufacturer={manufacturer}'
else:
raise KeyError
j = getJSONFromUrl(scrapeurl.format(url, **kwargs))
key, values = j.popitems()
for value in values:
kwargs[key] = value
return self.scraper(**kwargs)
This uses Python's built-in functionality to treat arbitrary keyword arguments as a dictionary, along with more modern str.format string formatting, to dynamically handle the arguments you're looking for. The only difference is that you now need to call it:
instance.scraper(manufacturer='...')
rather than just
instance.scraper('...')
An example of the string formatting, mixing positional and keyword arguments:
>>> '{0}&manufacturer={manufacturer}'.format('foo', **{'manufacturer': 'bar'})
'foo&manufacturer=bar'

How to identify a column name by a string? django/python

I am writing a basic function that takes three arguments, request, field, and user_id.
The idea is, when you pass through the info, the function returns the result which would be the column (identified by argument "field"), the row (identified by the argument "user_id").
this is my function:
def get_user_field(request, user_id, field):
result = Users.objects.raw("SELECT id, %s FROM blog_users WHERE id = %s", [field, user_id])[0]
#return result.??????
what I do not know how to do is to replace those question marks with what to return the corresponding column. If i try
return result.field
It will pass a string where "field" is. And of course a string cannot be put there.
So how can i achieve a function that works pretty much exactly like this and returns one result?
Thanks!
This can be done with the getattr Python builtin:
return getattr(result, field)
But it would be better to do it entirely differently:
def get_user_field(request, user_id, field):
return User.objects.filter(id=user_id).values_list(field, flat=True)[0]
Or, to allow for the possibility of the user not existing:
def get_user_field(request, user_id, field):
result = User.objects.filter(id=user_id).values_list(field, flat=True)
if result:
return result[0]
else:
return None # or raise an exception or whatever you like

def function : if nothing selected , return a list of all the objects

i'm trying define a function that return a list when i specify an object, and it returns a list of all the objects in the scene with *_control when i don't specify anything..
that's my function but it doesn't work....
i'm working with maya then..
from maya import cmds
def correct_value(selection):
if not isinstance(selection, list):
selection = [selection]
objs = selection
return objs
if not selection :
objs = cmds.ls ('*_control')
return objs
when i don't specify anything it returns an error :
Error: line 1: TypeError: file line 1: correct_value()
takes exactly 1 argument (0 given)
what's wrong ??
def correct_value(selection=None):
if selection is None: # note that You should check this before
# You wil check whether it is list or not
objs = cmds.ls ('*_control')
return objs
if not isinstance(selection, list):
selection = [selection]
objs = selection
return objs
Well, you wrote your function with a required argument. Therefore, you have to pass the argument. You can write it so the argument is optional by specifying the value that will be used when nothing is passed:
def correct_value(selection=None):
etc.
If you want a parameter to be optional, you need to provide a default value:
def correct_value(selection=None):
# do something
if selection is None:
#do something else
To handle a default parameter even if it might be None
def correct_value(*args):
if not args:
objs = cmds.ls ('*_control')
return objs
elif len(args) == 1:
selection = args
objs = selection
return objs
else:
raise TypeError # ...
Here's a really useful pair of patterns for this kind of stuff:
# always return a list from scene queries (maya will often return 'none'
def get_items_named_foo():
return cmds.ls("foo") or [] # this makes sure that you've always got a list, even if it's empty
# always use the variable *args method to pass in lists
def do_something(*args):
for item in args:
do_something(item) # args will always be a tuple, so you can iterate over it
# this lets you do stuff like this without lots of boring argument checks:
do_something (*get_items_named_foo())
If you use both of these tricks consistently, you can transparently handle cases where your maya queries have returned None instead of a list
As an aside, you can mimic the default maya behaviour (where passing no arguments uses the current selection) like this:
def work_on_list_or_selected(*args):
args = args or cmds.ls(sl=True) or []
for item in args:
do_something (item)

Categories