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