how to access an element from a list of dictionaries - python

I'm trying to build a cart view following a tutorial and I need to print out the quantity of an item. I have two functions in utils.py from where I wanna access the quantity element and print it out in a view, currently getting an error 'dict' object has no attribute 'quantity'
def cookieCart(request):
try:
cart = json.loads(request.COOKIES['cart'])
except:
cart = {}
print('Cart:', cart)
items = []
order = {'get_cart_total': 0, 'get_cart_items': 0, 'shipping': False}
cartItems = order['get_cart_items']
for i in cart:
try:
cartItems += cart[i]["quantity"]
product = Product.objects.get(id=i)
total = (product.final_price * cart[i]["quantity"])
order['get_cart_total'] += total
order['get_cart_items'] += cart[i]["quantity"]
item = {
'product':{
'id':product.id,
'name':product.name,
'final_price':product.final_price,
'image_URL':product.image_URL,
},
**#print the quantity on view**
'quantity':cart[i]["quantity"],
'get_total':total,
}
items.append(item)
except:
pass
return {"items": items, "order": order, "cartItems": cartItems}
def cartData(request):
if request.user.is_authenticated:
customer = request.user.customer
order, created = Order.objects.get_or_create(customer=customer, complete=False)
items = order.orderitem_set.all()
cartItems = order.get_cart_items
else:
cookieData = cookieCart(request)
cartItems = cookieData['cartItems']
order = cookieData['order']
items = cookieData['items']
return {'cartItems':cartItems ,'order':order, 'items':items}
Views
from .utils import cookieCart, cartData
def my_view(request):
data = cartData(request)
items = data['items']
qty = items[0].quantity
print(qty)
Data Structure:
if I print out 'items' instead of 'qty' the data looks like this
[{"product": {"id": 9, "name": "p_one", "final_price": 59, "image_URL": "/images/p_one.jpg"}, "quantity": 2, "get_total": 118}, {"product": {"id": 10, "name": "p_two", "final_price": 32, "image_URL": "/images/p_two.jpg"}, "quantity": 3, "get_total": 96}]

Let's decompose your view :
data = cartData(request)
cartData returns a dict where {"items": list of dicts, ... }
items = data['items']
At this point, items's value is a list of dicts.
To access the first item of a list you use integers indices like
myList[0] # first item in "myList"
myList[1] # second item in "myList"
etc
So here, remember this is a list that contains dicts.
So Items[0] = a dict that is defined in your cookieCart function
item = {
'product':{
'id':product.id,
'name':product.name,
'final_price':product.final_price,
'image_URL':product.image_URL,
},
**#print the quantity on view**
'quantity':cart[i]["quantity"],
'get_total':total,
}
this dict has 3 keys : "product", "quantity" and "get_total".
To access a dict key's value, you use this syntax :
myDict["the key"] # As opposed to lists, you use string keys to match values
So to get the quantity in your case, we could decompose like this :
order = cartData(request) # dict
all_items_in_cart_list = order["items"] # list
first_item_in_cart = all_items_in_cart_list[0] # dict again
quantity_of_first_item = first_item_in_cart["quantity"] # your value !

Not sure how does your data look like, but this is where the problem might be:
def my_view(request):
data = cartData(request)
qty = data['items']['quantity']
# qty = data['items',['quantity']]
print(qty)

Related

Graphene GraphQL on return return records where calculated field is greater than zero

I have a GrpahQL endpoint that returns all the Magic the Gathering cards for a particular MtG set. I have created a field that returns a list of calculated fields which is the delta is card price between two days. I would like to filter all the cards where the delta percentage is greater than 0.
so something along the lines of return magic_sets_cards_pricing.objects.filter(MagicSetsGainsPricing.delta_percent>0)
Code:
class MagicSetsGainsPricing(graphene.ObjectType):
value = graphene.String()
delta_percent = graphene.String()
delta_value = graphene.String()
class Meta:
model = magic_sets_cards_pricing
def resolve_gains(self, info, days=None, **kwargs):
us = auth_user_settings.objects.values('region', 'currency').get()
if us['region'] == 'Europe':
region_nf = 'eur'
match us['currency']:
case 'Pound':
symbol = '£'
rate = currency_exchanges.objects.filter(c_from='EUR').filter(c_to='GBP').values('rate').get()['rate']
case 'Dollar':
symbol = '$'
rate = currency_exchanges.objects.filter(c_from='EUR').filter(c_to='USD').values('rate').get()['rate']
case _:
symbol = '€'
rate = 1
else:
region_nf = 'usd'
match us['currency']:
case 'Pound':
symbol = '£'
rate = currency_exchanges.objects.filter(c_from='USD').filter(c_to='GBP').values('rate').get()['rate']
case 'Euro':
symbol = '€'
rate = currency_exchanges.objects.filter(c_from='USD').filter(c_to='EUR').values('rate').get()['rate']
case _:
symbol = '$'
rate = 1
price_today = magic_sets_cards_pricing.objects.filter(card_id=self).values('date', region_nf).order_by('-date')[0:1][0][region_nf] or 0
calc_price_today = round((price_today * rate), 2)
price_previous = magic_sets_cards_pricing.objects.filter(card_id=self).values('date', region_nf).order_by('-date')[days:days+1][0][region_nf] or 0
calc_price_previous = round((price_previous * rate), 2)
calc_delta_value = str(calc_price_today - calc_price_previous)
calc_delta_percent = str(round(((calc_price_today - calc_price_previous) / (calc_price_previous or 1)) * 100, 2))
return MagicSetsGainsPricing(
value=symbol + str(calc_price_today),
delta_value=symbol + calc_delta_value,
delta_percent=calc_delta_percent + '%',
)
class MagicSetsGains(DjangoObjectType):
pricing = graphene.Field(MagicSetsGainsPricing, days=graphene.Int(), resolver=MagicSetsGainsPricing.resolve_gains)
class Meta:
model = magic_sets_cards
def resolve_pricing(self, info, **kwargs):
return magic_sets_cards_pricing.objects.filter(card_id=self)
class MagicSetsGainsQuery(ObjectType):
magic_sets_gains = graphene.List(MagicSetsGains, code=graphene.String())
def resolve_magic_sets_gains(self, info, code=None, **kwargs):
sql_number_to_int = "CAST((REGEXP_MATCH(number, '\d+'))[1] as INTEGER)"
excluded_sides = ['b', 'c', 'd', 'e']
return magic_sets_cards.objects.filter(set_id__code=code).exclude(side__in=excluded_sides).extra(select={'int': sql_number_to_int}).order_by('int', 'number').all()
Response:
{
"data": {
"magicSetsGains": [
{
"number": "1",
"name": "Adeline, Resplendent Cathar",
"pricing": {
"value": "£2.23",
"deltaValue": "£0.52",
"deltaPercent": "30.41%"
}
},
{
"number": "2",
"name": "Ambitious Farmhand // Seasoned Cathar",
"pricing": {
"value": "£0.07",
"deltaValue": "£-0.04",
"deltaPercent": "-36.36%"
}
},
...
]
}
}
So you cant use the model.objects.filter() because that filters the objects on the database level, and the database doesn't know about the "delta_percent" field that you've calculated at the schema level. However you can filter it in python using the filter function.
What I would suggest you do is this. Firstly, the code inside resolve_gains needs to move outside of the schema into a utility file and you can name it something like get_pricing_for_card(card, days=None) that returns a dict with {"value": blah, "delta_percent": blah, "delta_price": blah}.
Then your resolve_gains becomes:
def resolve_gains(self, info, days=None, **kwargs):
pricing = get_pricing_for_card(self, days)
return MagicSetsGainsPricing(**pricing)
Then MagicSetsGainsQuery becomes this:
class MagicSetsGainsQuery(ObjectType):
magic_sets_gains = graphene.List(MagicSetsGains, code=graphene.String())
def resolve_magic_sets_gains(self, info, code=None, **kwargs):
sql_number_to_int = "CAST((REGEXP_MATCH(number, '\d+'))[1] as INTEGER)"
excluded_sides = ['b', 'c', 'd', 'e']
cards_to_return = magic_sets_cards.objects.filter(set_id__code=code).exclude(side__in=excluded_sides).extra(select={'int': sql_number_to_int}).order_by('int', 'number').all()
return filter(lambda card: get_pricing_for_card(card)["delta_percent"] > 0, cards_to_return)
Alternatively you can replace the return line with:
return [card for card in cards_to_return if get_pricing_for_card(card)["delta_percent"] > 0]
I know this isn't great because essentially you end up running get_pricing_for_card twice. This is a little annoying thing about the graphene schema where you can't really pass around values calculated in the resolve function. The larger refactor to this would be that you save the pricing in the pricing table with the delta percent field and so forth, when you create a card. then you can do pricing.objects.filter(delta_percent__gte=0)

Create a sum of specific values in a dict in Python

I have this dict with my values for some products which can be added to the cart.
I want to sum the prices of all products in the cart and print them. How Do I select
the specific value (price) from the dict using a function. In this instance ShoeA should be two times in the cart which should result in 144.98 + 144.98 = 289.96
Here is my complete code:
shoeA = {"id": 342453, "name": "Shoe A", "price": 144.98 }
shoeB = {"id": 9800989, "name": "Shoe B", "price": 300}
# A cart of a shop has the following functions. Each functionality should be implemented as a separate function.
# 1.1 add_product(cart, product, times): adds a product "times" times to the cart. The cart is a list and the product a
# dictionary. A product consists of a ID, name and price
cart = []
def add_product(product, times):
for i in range(0, times):
cart.append(product)
print(cart)
add_product(342453, 4)
# 1.2 remove_product(cart, productID): Removes all products with ID = productID from the given cart
# 1.3 print_cart(cart): Prints the whole cart
def remove_product(productID, times):
for i in range(0, times):
cart.remove(productID)
print(cart)
remove_product(342453, 2) #only two reductions.
# 1.4 find_product(cart, productID): Returns a list of integers, which are indexes of the searched product in the cart.
# E.g. find_product(cart, 12) may return [2,5,10] if the cart contains a the product with productID at index 2,5 and 10.
def find_product(productID):
for i, j in enumerate(cart):
if j == productID:
print(i)
find_product(342453)
# 1.5 get_total(cart): Function that sums up the prices of all products in the cart and returns this value
def get_total():
#Please help me here. :)
get_total()
Firstly, you have to store all products in a list or dict.
all_products = {342453: shoeA, 9800989: shoeB}
Then simply iterate over it and add up all the prices.
def get_total():
total = 0
for productId in cart:
shoe = all_products[productId]
total += shoe["price"]
print("Total: ", total)
Here's the Complete code:
shoeA = {"id": 342453, "name": "Shoe A", "price": 144.98 }
shoeB = {"id": 9800989, "name": "Shoe B", "price": 300}
# A cart of a shop has the following functions. Each functionality should be implemented as a separate function.
# 1.1 add_product(cart, product, times): adds a product "times" times to the cart. The cart is a list and the product a
# dictionary. A product consists of a ID, name and price
all_products = {342453: shoeA, 9800989: shoeB}
cart = []
def add_product(product, times):
for i in range(0, times):
cart.append(product)
print(cart)
add_product(342453, 4)
# 1.2 remove_product(cart, productID): Removes all products with ID = productID from the given cart
# 1.3 print_cart(cart): Prints the whole cart
def remove_product(productID, times):
for i in range(0, times):
cart.remove(productID)
print(cart)
remove_product(342453, 2) #only two reductions.
# 1.4 find_product(cart, productID): Returns a list of integers, which are indexes of the searched product in the cart.
# E.g. find_product(cart, 12) may return [2,5,10] if the cart contains a the product with productID at index 2,5 and 10.
def find_product(productID):
for i, j in enumerate(cart):
if j == productID:
print(i)
# find_product(342453)
# 1.5 get_total(cart): Function that sums up the prices of all products in the cart and returns this value
def get_total():
total = 0
for productId in cart:
shoe = all_products[productId]
total += shoe["price"]
print("Total: ", total)
get_total()
First, you'll need somewhere to keep a catalog of the products, like so:
catalog = [shoeA, shoeB]
Then, you can use a simple for-loop to find the sum of the products:
def total_price():
sum = 0
for product in catalog:
sum += product['price'] * cart.count(product['id'])
return sum
Change the declaration of the shoe products into a dictionary of products. I updated the other functions to reflect that change.
products_dict = {342453 : {"name": "Shoe A", "price": 144.98 }, 9800989: {"name": "Shoe B", "price": 300}}
# A cart of a shop has the following functions. Each functionality should be implemented as a separate function.
# 1.1 add_product(cart, product, times): adds a product "times" times to the cart. The cart is a list and the product a
# dictionary. A product consists of a ID, name and price
cart = []
def add_product(product_id, times):
for i in range(0, times):
cart.append(products_dict[product_id])
print(cart)
add_product(342453, 4)
# 1.2 remove_product(cart, productID): Removes all products with ID = productID from the given cart
# 1.3 print_cart(cart): Prints the whole cart
def remove_product(product_id, times):
for i in range(0, times):
cart.remove(products_dict[product_id])
print(cart)
remove_product(342453, 2) #only two reductions.
# 1.4 find_product(cart, productID): Returns a list of integers, which are indexes of the searched product in the cart.
# E.g. find_product(cart, 12) may return [2,5,10] if the cart contains a the product with productID at index 2,5 and 10.
def find_product(product_id):
for index, product in enumerate(cart):
if product == products_dict[product_id]:
print(index)
find_product(342453)
# 1.5 get_total(cart): Function that sums up the prices of all products in the cart and returns this value
def get_total():
#Please help me here. :)
sum = 0
for product in cart:
sum += product["price"]
print(sum)
get_total()
I would suggest to first try a easier-to-manage structure of your products. Like a nested dictionary
catalog = dict(
shoeA={"id": 342453, "name": "Shoe A", "price": 144.98 },
shoeB={"id": 9800989, "name": "Shoe B", "price": 300}
)
This means that the whole catalog will be callable. The problem with your code is that each product is separated thus not easily callable. In the nested dictionary we can easily call the product variable since it is just considered a string.
Then you edit the add function to accommodate not the internal details of your product (which is the case in your code) but the 'catalog' key(product) instead. Future functions should call this key (e.g. 'shoeA', or 'shoeB')
cart = []
cart_id = []
def add_product(product, times):
for i in range(0, times):
cart.append(product)
cart_id.append(catalog[product]['id'])
print(cart)
print(cart_id)
add_product('shoeA', 4)
['shoeA', 'shoeA', 'shoeA', 'shoeA']
[342453, 342453, 342453, 342453]
In this part we used the callable nested dictionary. Now you have free control of all attributes of the product you want easily. For example, if you want to retain the list of ids you can use the cart_id instead of the cart.
Now this format makes it easy to do a sum.
def get_total():
cart_prices = [catalog[product]['price'] for product in cart ]
total = sum(cart_prices)
print(cart_prices)
print(total)
get_total()
This should work. Feel free to adjust the other functions to accept the new structure.
  To process the extracted products data (documents, api or database) you could store them in this way if they have a fixed and available size:
product_dict = {product["id"]:product for product in products}
Then iterate over cart list like this to return a total sum:
def get_total():
amount = 0
for product_id in set(cart):
amount += product_dict[product_id]["price"] * cart.count(product_id)
return amount
print(get_total())

Python adding list as a key in dictionary

I'm trying to create a dictionary where the key is date ,
In my case each date is a list like this : [2012, 5, 25].
This is the code that I have which reads from a csv file into dictionary.
This code is given I can't change it :
def read_profiles(r_file_name):
"""Reads the file profiles-full.csv into a dictionary where the key is the users id
and the value is a dictionary with users fields "public", "compl_percent", "gender",
"region", "last_login", "age", "body". In addition to this dictionary, the function
returns the list of all users that have 0 completion percentage."""
users = {}
noinfo_users = []
with open(r_file_name, 'rb') as csvfile:
linereader = csv.reader(csvfile)
for line in linereader:
profile = {}
data_fields = ("public", "compl_percent", "gender", "region",
"last_login", "age", "body")
for field, raw_data in zip(data_fields, line[1:]):
if field == "compl_percent" and int(raw_data) == 0:
noinfo_users.append(int(line[0]))
if raw_data == "null":
profile[field] = None
elif field == "body":
profile[field] = raw_data
elif field == "last_login": #<=== reading last_login
profile[field] = map(int, raw_data.split("-"))
elif field == "region":
profile[field] = map(int, raw_data.split(";"))
else:
profile[field] = int(raw_data)
users[int(line[0])] = profile
return users, noinfo_users
And this is the content of the csv file, corresponding to this pattern:
"public", "compl_percent", "gender", "region", "last_login", "age", "body"**
231702, 1, 60, 0, 1;1;21, 2012-05-15, 0, 171 cm;58 kg
This is how an element in profiles dictionary looks like:
1492433: {'body': '160 cm;54 kg', 'compl_percent': 78, 'gender': 0, 'region': [1, 10, 6], 'age': 36, 'last_login': [2012, 5, 25], 'public': 1}
This is my function:
def getStrongConnectedForAttr(user,edges,profiles,attr):
result = dict()
if user in edges:
userFriends = edges.get(user)
for friend in userFriends:
if isBidirectional(edges,user,friend) == 2:
if friend in profiles:
friendAttr = (profiles.get(friend))[str(attr)]
if attr == "last_login":
#friendAttr = '-'.join(map(str, friendAttr))
friendAttr = tuple(friendAttr)
if friendAttr in result: #<===== error
result[friendAttr] = result.get(friendAttr) + 1
else:
result[friendAttr] = 1
return sorted(result.items(), key=operator.itemgetter(1), reverse=True)
else:
return result
It takes profiles as one of the parameters, and builds an empty dictionary.
In the line if friendAttr in result: I get the error:
TypeError: unhashable type: 'list'
I tried searching the web for solution and I found many, especially here on Stack Overflow, and as you can see I tried many solutions, one to convert the list to tuple, or join the list to string.
But none of them worked.
You are only turning the login_attr values to a tuple, but forgetting about the region attribute. It is those values that still throw the exception.
You are only testing for login_attr here:
if attr == "last_login":
friendAttr = tuple(friendAttr)
Rather than turn your values to tuples there, just store tuples when reading the file.
Replace
elif field == "last_login": #<=== reading last_login
profile[field] = map(int, raw_data.split("-"))
elif field == "region":
profile[field] = map(int, raw_data.split(";"))
with
elif field == "last_login":
profile[field] = tuple(map(int, raw_data.split("-")))
elif field == "region":
profile[field] = tuple(map(int, raw_data.split(";")))

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

python generating nested dictionary key error

I am trying to create a nested dictionary from a mysql query but I am getting a key error
result = {}
for i, q in enumerate(query):
result['data'][i]['firstName'] = q.first_name
result['data'][i]['lastName'] = q.last_name
result['data'][i]['email'] = q.email
error
KeyError: 'data'
desired result
result = {
'data': {
0: {'firstName': ''...}
1: {'firstName': ''...}
2: {'firstName': ''...}
}
}
You wanted to create a nested dictionary
result = {} will create an assignment for a flat dictionary, whose items can have any values like "string", "int", "list" or "dict"
For this flat assignment
python knows what to do for result["first"]
If you want "first" also to be another dictionary you need to tell Python by an assingment
result['first'] = {}.
otherwise, Python raises "KeyError"
I think you are looking for this :)
>>> from collections import defaultdict
>>> mydict = lambda: defaultdict(mydict)
>>> result = mydict()
>>> result['Python']['rules']['the world'] = "Yes I Agree"
>>> result['Python']['rules']['the world']
'Yes I Agree'
result = {}
result['data'] = {}
for i, q in enumerate(query):
result['data']['i'] = {}
result['data'][i]['firstName'] = q.first_name
result['data'][i]['lastName'] = q.last_name
result['data'][i]['email'] = q.email
Alternatively, you can use you own class which adds the extra dicts automatically
class AutoDict(dict):
def __missing__(self, k):
self[k] = AutoDict()
return self[k]
result = AutoDict()
for i, q in enumerate(query):
result['data'][i]['firstName'] = q.first_name
result['data'][i]['lastName'] = q.last_name
result['data'][i]['email'] = q.email
result['data'] does exist. So you cannot add data to it.
Try this out at the start:
result = {'data': []};
You have to create the key data first:
result = {}
result['data'] = {}
for i, q in enumerate(query):
result['data'][i] = {}
result['data'][i]['firstName'] = q.first_name
result['data'][i]['lastName'] = q.last_name
result['data'][i]['email'] = q.email

Categories