dynamic query getting data post python - python

I'm doing the following , when I get to post data to the queries :
fields = [
'name',
'surname',
'weight',
'height',
'position',
'agent',
'nationality',
'shirt_name',
'shirt_number',
'preferred_foot',
]
if request.method == 'POST':
filter_spec = None
for fld_name in fields:
value = request.POST.get(fld_name, None)
if value is not None:
if fld_name == 'position':
fld_name = 'position__in'
value = Position.objects.filter(name=value)
if fld_name == 'agent':
fld_name = 'entrepreneur__in'
value = Entrepreneur.objects.filter(name=value)
if filter_spec is None:
filter_spec = Q(**{fld_name: value})
else:
filter_spec &= Q(**{fld_name: value})
players = Player.objects.filter(filter_spec) if filter_spec else Player.objects.none()
result = [p.get_json() for p in players]
py_resp = {
'meta': {
'total_count': len(result),
},
'objects': result,
}
json_resp = json.dumps(py_resp)
return HttpResponse(json_resp, content_type='application/json')
To position the query is as follows:
Player.objects.filter(position__in=Position.objects.filter(name=positionpost))
So I did as it is on top , which I am not able to implement it in the code for the nationality, since the query is thus:
Player.objects.filter(country__in=Country.objects.filter(nationality__in=Nationality.objects.filter(name='Espanhola')))
And I'm not sure how to put this in the code above
Someone can help me ?

Both your queries can be significantly simplified. The first should be:
Player.objects.filter(position__name=positionpost)
and the second:
Player.objects.filter(country__nationality__name='Espanhola')
These will usually also be more efficient, as they are doing JOINs rather than sub-queries.

Related

Dynamic data filter using sqlalchemy, it works for a given value but doesn't work for NULL(None) values

I am trying to create dynamic filters on a table, user could provide column name and its value to get filtered data
Connect to DB to get table meta data
db = create_engine(engineconn)
Session = sessionmaker(bind=db)
sess = Session()
metadata = MetaData()
queryobj = Table(tablename, metadata, schema=schemaname, autoload=True, autoload_with=db)
#Gets table object
"filtercols" - This is the list of columns that a filter can be applied
"filterparams" - formatted parameters value (from the user via console)
filtercols = ['Description','Type','Created'] #Fields in the table
filterparams = [{'description': {'op': 'eq', 'val': 'Test Data'}}, {'type': {'op': 'eq', 'val': 'none'}}]
#get filter list
filterlist = self.getfilter(queryobj, filtercols, filterparams)
Applying filterlist on to the query, it works fine if there is only one filter i.e.
filterparams = [{'description': {'op': 'eq', 'val': 'Test Data'}}] --> it works fine.
Also it works fine for
filterparams = [{'description': {'op': 'eq', 'val': 'Test Data'}}, {'type': {'op': 'eq', 'val': 'Name'}}] i.e. non null value,
But it won't work for 2 filter, one with NULL value
filterparams = [{'description': {'op': 'eq', 'val': 'Test Data'}}, {'type': {'op': 'eq', 'val': 'none'}}].
At the same time if there is only one filter with NULL value then it works i.e.
filterparams = [{'type': {'op': 'eq', 'val': 'none'}}]
Call the methods
result = sess.query(queryobj).filter(*filterlist).all()
print(result)
def getfilter(self, tbl, cfgfiltercolumns, filterparams):
filters = []
'''print(1)
print(cfgfiltercolumns)
print(2)
print(filterparams)'''
iskeyfound = False
try:
#iterate through list of parameters given by the user
for item in filterparams:
# get the key and value which is formatted by 'getargs' method e.g. {'colname':{'op':'gr', val:'2020-06-01'}}
for key,value in item.items():
iskeyfound = False
#parameter given by the user should exists in 'FilterFields' for the given 'dataconfig' key
for i,cdfcol in enumerate(cfgfiltercolumns):
#colmatcheddict = {}
if(key.upper() == cdfcol.upper()):#
iskeyfound = True
#colmatcheddict[cdfcol] = item[key]
if tbl.columns.has_key(cdfcol):
#print('found')
opereratortype = item[key]['op']
paramval = item[key]['val']
if opereratortype == 'lt':
filters.append(tbl.columns[cdfcol] < paramval)
elif opereratortype == 'gt':
filters.append(tbl.columns[cdfcol] > paramval)
elif opereratortype == 'eq':
if(paramval.upper() == "NONE"):
filters.append(tbl.columns[cdfcol].is_(None))
#filters.append(tbl.columns[cdfcol] == None)
else:
filters.append(tbl.columns[cdfcol] == paramval)
elif opereratortype == 'noteq':
if(paramval.upper() == "NONE"):
filters.append(tbl.columns[cdfcol].isnot(None))
#filters.append(tbl.columns[cdfcol] != None)
else:
filters.append(tbl.columns[cdfcol] != paramval)
elif opereratortype == 'in':
formattedval = (paramval[1:len(paramval)-1])
inlist=[]
splitval = formattedval.split(',')
for item in splitval:
inlist.append(item)
#print(inlist)
filters.append(tbl.columns[cdfcol].in_(inlist))
elif opereratortype == 'notin':
formattedval = (paramval[1:len(paramval)-1])
inlist=[]
splitval = formattedval.split(',')
for item in splitval:
inlist.append(item)
#print(inlist)
filters.append(tbl.columns[cdfcol].notin_(inlist))
#filters.append(tbl.columns[cdfcol] in paramval)
else:
pass
#Logging
if (iskeyfound == False):
pass
#logging
except:
pass
#logging
print(filters)
return filters

Expected singleton error occurs when return more than one record in odoo?

This method to get the product price from the PO, and it works well if the PO have only one record otherwise I am getting this error.
raise ValueError("Expected singleton: %s" % self)
This is the method
#api.multi
def create_refund_invoice(self):
inv_obj = self.env['account.invoice']
for pick in self.filtered(lambda x:x.return_type):
type = 'in_refund' if pick.return_type == 'purchase' else 'out_refund'
inv_lines = {'type':type, 'partner_id':pick.partner_id.id, 'invoice_line_ids':[]}
account = pick.return_type == 'sale' and pick.partner_id.property_account_receivable_id.id or pick.partner_id.property_account_payable_id.id
inv_lines['account_id'] = account
inv_lines['origin'] = pick.name
inv_lines['name'] = pick.origin
for line in pick.move_lines:
name = line.product_id.partner_ref
for rec in self:
rec.order_id = line.env['purchase.order'].search([('name', '=', line.origin)]).order_line
rec.price = rec.order_id.price_unit
inv_lines['invoice_line_ids'] += [(0, None, {
'product_id':line.product_id.id,
'name':name,
'quantity':line.quantity_done,
'price_unit': rec.price,
'account_id':line.product_id.product_tmpl_id.get_product_accounts()['income'].id})]
if inv_lines['invoice_line_ids']:
inv_id = inv_obj.create(inv_lines)
pick.invoice_id = inv_id.id
It is necessary for odoo that when you are getting more than one record then you can not access it's field values directly.
In your code you are trying to get purchase_order_line of purchase_order It may possible that many lines are available in a single order.
def create_refund_invoice(self):
purchase_order_obj = self.env['purchase.order']
inv_obj = self.env['account.invoice']
for pick in self.filtered(lambda x:x.return_type):
type = 'in_refund' if pick.return_type == 'purchase' else 'out_refund'
inv_lines = {'type':type, 'partner_id':pick.partner_id.id, 'invoice_line_ids':[]}
account = pick.return_type == 'sale' and pick.partner_id.property_account_receivable_id.id or pick.partner_id.property_account_payable_id.id
inv_lines['account_id'] = account
inv_lines['origin'] = pick.name
inv_lines['name'] = pick.origin
for line in pick.move_lines:
name = line.product_id.partner_ref
for rec in self:
order_lines = purchase_order_obj.search([('name', '=', line.origin)]).order_line
for pol in order_lines:
price = pol.order_id.price_unit
inv_lines['invoice_line_ids'] += [(0, None, {
'product_id':line.product_id.id,
'name':name,
'quantity':line.quantity_done,
'price_unit': price,
'account_id':line.product_id.product_tmpl_id.get_product_accounts()['income'].id})
]
if inv_lines['invoice_line_ids']:
inv_id = inv_obj.create(inv_lines)
pick.invoice_id = inv_id.id
I have updated code test above code and update it as per your requirement.

M2m relation breaks when passing filter parameters

I have a m2m relation between properties and images in my model like imageproperty = models.ManyToManyField(Property, blank = True). Im having an issue trying to filter properties with their associated images as whenever i pass a parameter in my query i get something like this and the images are not showing quiet good
. This is my code so far
def filter_properties(request, prop, p):
order = "creation_date"
if p["sort"]: order = p["sort"]
if p["asc_desc"] == "desc": order = '-' + order
results = Property.objects.filter(status = True)
for prop in results:
prop.images = prop.image_set.all()[:1] #Should i need to return in results so it brings values when filtering?
if p["name"] : results = results.filter(name__icontains=p["name"])
if p["price_from"] : results = results.filter(price__gte=int(p["price_from"]))
if p["price_to"] : results = results.filter(price__lte=int(p["price_to"]))
if p["category"]:
lst = p["category"]
or_query = Q(categories = lst[0])
for c in lst[1:]:
or_query = or_query | Q(categories = c)
results = results.filter(or_query).distinct()
return results
def search_properties_view(request):
try:
page = int(request.GET.get("page", '1'))
except ValueError:
page = 1
p = request.POST
prop = defaultdict(dict)
parameters = dict.fromkeys(
('name', 'price_from', 'price_to', 'currency_type', 'activity_type', 'sort', 'asc_desc'),
'',
)
parameters["category"] = []
for k, v in p.items():
if k == "category":
parameters[k] = [int(x) for x in p.getlist(k)]
elif k in parameters:
parameters[k] = v
elif k.startswith("name") or k.startswith("curency_type") or k.startswith("activity_type"):
k, pk = k.split('-')
prop[pk][k] = v
elif k.startswith("category"):
pk = k.split('-')[1]
prop[pk]["category"] = p.getlist(k)
if page != 1 and "parameters" in request.session:
parameters = request.session["parameters"]
else:
request.session["parameters"] = parameters
results = filter_properties(request, prop, parameters)
paginator = Paginator(results, 20)
try:
results = paginator.page(page)
except (InvalidPage, EmptyPage):
request = paginator.page(paginator.num_pages)
return render(request, 'propiedades/propiedades.html', {
'propiedades': request.POST,
'media_url': settings.MEDIA_URL,
'results': results,
'params': parameters,
'categories': PropertyCategory.objects.all()
})

Generate hierarchical JSON tree structure from Django model

I have a Django model as
class Classification(models.Model):
kingdom = models.CharField(db_column='Kingdom', max_length=50)
phylum = models.CharField(db_column='Phylum', max_length=50)
class_field = models.CharField(db_column='Class', max_length=50)
order = models.CharField(db_column='Order', max_length=50)
family = models.CharField(db_column='Family', max_length=50)
genus = models.CharField(db_column='Genus', max_length=50)
species = models.CharField(db_column='Species', max_length=50)
to represent biological taxonomy classification as shown here:
I have classification records of over 5,000 species. I need to generate JSON hierarchical structure as shown below.
{
'name': "root",
'children': [
{
'name': "Animalia",
'children': [
{
{
'name':"Chordata"
'children': [ ... ]
}
},
...
...
]
},
...
...
]
}
Can you suggest me any method(s) to do so?
You can do the following:
Transform a list of Classifications to a nested dict.
Transform nested dict to the required format
Samples here will operate on slightly reduced Classification class to improve readability:
class Classification:
def __init__(self, kingdom, phylum, klass, species):
self.kingdom = kingdom
self.phylum = phylum
self.klass = klass
self.species = species
First part:
from collections import defaultdict
# in order to work with your actual implementation add more levels of nesting
# as lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
nested_dict = defaultdict(
lambda: defaultdict(
lambda: defaultdict(list)
)
)
for c in all_classifications:
nested_dict[c.kingdom][c.phylum][c.klass].append(c.species)
defaultdict is just a nice tool to guarantee existence of the key in a dictionary, it receives any callable and use it to create a value for missing key.
Now we have nice nested dictionary in the form of
{
'Kingdom1': {
'Phylum1': {
'Class1': ["Species1", "Species2"],
'Class2': ["Species3", "Species4"],
},
'Phylum2': { ... }
},
'Kingdom2': { 'Phylum3': { ... }, 'Phylum4': {... } }
}
Part two: converting to desired output
def nested_to_tree(key, source):
result = {'name': key, 'children':[]}
for key, value in source.items():
if isinstance(value, list):
result['children'] = value
else:
child = nested_to_tree(key, value)
result['children'].append(child)
return result
tree = nested_to_tree('root', nested_dict')
I believe it's self-explanatory - we just convert passed dictionary to desired format and recurse to it's content to form children.
Complete example is here.
Two notes:
Written in python 3. Replacing source.items() with source.iteritems() should suffice to run in python 2.
You haven't specify what leafs should looks like, so I just assumed that leaf nodes should be genus with all species attached as children. If you want species to be leaf nodes - it's pretty straightforward to modify the code to do so. If you have any trouble doing so - let me know in comments.
Finally got what I wanted. Code is not beautiful, near ugly, yet somehow I got what I wanted.
def classification_flare_json(request):
#Extracting from database and sorting the taxonomy from left to right
clazz = Classification.objects.all().order_by('kingdom','phylum','class_field','genus','species')
tree = {'name': "root", 'children': []}
#To receive previous value of given taxa type
def get_previous(type):
types = ['kingdom', 'phylum', 'class_field', 'family', 'genus', 'species']
n = types.index(type)
sub_tree = tree['children']
if not sub_tree: return None
for i in range(n):
if not sub_tree: return None
sub_tree = sub_tree[len(sub_tree)-1]['children']
if not sub_tree: return None
last_item = sub_tree[len(sub_tree)-1]
return last_item['name']
#To add new nodes in the tree
def append(type, item):
types = ['kingdom', 'phylum', 'class_field', 'family', 'genus', 'species_id']
n = types.index(type)
sub_tree = tree['children']
for i in range(n+1):
if not sub_tree: return None
sub_tree = sub_tree[len(sub_tree)-1]['children']
sub_tree.append(item)
for item in clazz:
while True:
if item.kingdom == get_previous('kingdom'):
if item.phylum == get_previous('phylum'):
if item.class_field == get_previous('class_field'):
if item.family == get_previous('family'):
if item.genus == get_previous('genus'):
append('genus', {'name':item.species, 'size': 1})
break;
else:
append('family', {'name':item.genus, 'children': []})
else:
append('class_field', {'name':item.family, 'children':[]})
else:
append('phylum', {'name': item.class_field, 'children':[]})
else:
append('kingdom', {'name': item.phylum, 'children':[]})
else:
tree['children'].append({'name': item.kingdom, 'children':[]})
return HttpResponse(json.dumps(tree), content_type="application/json")

Store path to dictionary value for setting value

Consider a dict that holds a person:
person = {}
person['name'] = 'Jeff Atwood'
person['address'] = {}
person['address']['street'] = 'Main Street'
person['address']['zip'] = '12345'
person['address']['city'] = 'Miami'
How might the path to a location in the dictionary be stored for writing to the value?
# Set city (Existing field)
city_field = ['address', 'city']
person[city_field] = 'London' // Obviously won't work!
# Set country (New field)
country_field = ['address', 'country']
person[city_country] = 'UK' // Obviously won't work!
Note that I had previously asked how to store the path to dictionary value for reading.
Use tuples as index.
city_field = ('address', 'city')
country_field = ('address', 'country')
Usage:
>>> person = {}
>>> city_field = ('address', 'city')
>>> country_field = ('address', 'country')
>>> person[city_field] = 'Miami'
>>> person[country_field] = 'UK'
>>> person
{('address', 'country'): 'UK', ('address', 'city'): 'Miami'}
Got it! Actually my co-worker Moshe is the brains behind this one:
def set_path(someDict, path, value):
for x in path[::-1]:
value = {x: value}
return deepupdate(someDict, value)
def deepupdate(original, update):
for key, value in original.items():
if not key in update:
update[key] = value
elif isinstance(value, dict):
deepupdate(value, update[key])
return update
person = {}
person = set_path(person, ['name'], 'Shalom')
person = set_path(person, ['address', 'city'], 'Toronto')
person = set_path(person, ['address', 'street'], 'Baddessa')
pprint(person)
Returns:
{
'address': {
'city': 'Toronto',
'street': 'Baddessa'
},
'name': 'Shalom'
}
This depends on user Stanislav's excellent recursive dictionary deepmerge() function.

Categories