Model methods stop working correctly after first call - python

I have defined two models for retailers and items in their stock. Product is defined in another app. I defined two model methods to get and add stock items. Here is the relevant part of the code:
class Retailer(models.Model):
name = models.CharField(max_length=100)
#property
def stock_items(self):
return StockItem.objects.filter(retailer__id=F('id'))
def add_stock_item(self, product_id):
try:
print "Checking if it's already in stock"
self.stock_items.get(product__id=product_id)
print "It's already in stock"
except ObjectDoesNotExist:
try:
print "Adding item to stock"
product = Product.objects.get(pk=product_id)
StockItem.objects.create(retailer=self, product=product)
print "Added to stock"
except Product.DoesNotExist:
print "Such product doesn't exist"
def __unicode__(self):
return self.name
StockItem(models.Model):
retailer = models.ForeignKey(Retailer)
product = models.ForeignKey(Product)
def __unicode__(self):
return "%s - %s %s" % (self.retailer, self.product.brand, self.product)
When I want to use these model methods, something weird happens. They stop working correctly after adding the first item (In these examples, product 1 is 16 GB iPhone and Product 2 is 32 GB iPhone).
First let's add some products to our stock:
>>> r = Retailer.objects.get(pk=1)
>>> r.stock_items
[]
>>> r.add_stock_item(1)
Checking if it's already in stock
Adding item to stock
Added to stock
>>> r.add_stock_item(2)
Checking if it's already in stock
Adding item to stock
Added to stock
So far so good. Now let's try adding products again to see how it handles errors:
>>> r.add_stock_item(1)
Checking if it's already in stock
It's already in stock
>>> r.add_stock_item(2)
Checking if it's already in stock
Adding item to stock
Added to stock
What? Why did it add product 2 again. It was supposed to show a message similar to product 1. Let's see our stock:
>>> r.stock_items
[<StockItem: hh - Apple iPhone 4S 16GB>]
What happened to product 2? Did it fail to add it to database?
[<StockItem: hh - Apple iPhone 4S 16GB>, <StockItem: hh - Apple iPhone 4S 32GB>, <StockItem: hh - Apple iPhone 4S 32GB>]
Apparently not. It was added to database, but somehow our program fails to check it correctly. Only the first product added to stock is shown by calling r.stock_items. Restarting shell doesn't change the situation either, so I guess the reason couldn't be because of when the function is evaluated.
Why does this happen and how can I fix it?

Try to remove #property and change the method to this:
def stock_items(self):
return self.stockitem_set.all()

The reason why your method was failing is because that expression doesn't do what you think it does. The F() syntax is for comparing values within the same row: so that expression was checking that the related retailer ID was the same as the current row's ID. It so happened that this was true for the first item, but it would never be true of any subsequent items, because the row ID has incremented with the new items but the retailer ID remains the same.

Related

How do I find each instance of a model in Django

I am trying to iterate through each instance of a model I have defined.
Say I have the following in models.py, under the people django app:
class foo(models.Model):
name = models.CharField(max_length=20, default="No Name")
age = models.PositiveSmallIntegerField(default=0)
And I have populated the database to have the following data in foo:
name="Charley", age=17
name="Matthew", age=63
name="John", age=34
Now I want to work out the average age of my dataset. In another part of the program (outside the people app, inside the project folder), in a file called bar.py that will be set up to run automatically every day at a specific time, I calculate this average.
from people.models import foo
def findAverageAge():
total = 0
for instance in //What goes here?// :
total += instance.age
length = //What goes here, to find the number of instances?//
average = total / length
return average
print(findAverageAge)
In the code above, the //What goes here?// signifies I don't know what goes there and need help.
You can retrieve all elements with .all() [Django-doc]:
from people.models import foo
def findAverageAge():
total = 0
qs = foo.objects.all()
for instance in :
total += instance.age
length = len(qs)
average = total / length
return average
print(findAverageAge())
But you should not calculate the average at the Django/Python level. This requires retrieving all records, and this can be quite large. A database itself can calculate the average, and this is (normally) done in a more efficient way. You can .aggregate(…) [jango-doc] on the queryset with:
from people.models import foo
def findAverageAge():
return foo.objects.aggregate(
avg_age=Avg('age')
)['avg_age'] or 0
print(findAverageAge())
You should make a management command however: this will load the Django apps, and also makes it more convenient to run command with parameters, etc.

How do I call a function into another code file without getting error messages; and how to compare dictionaries to lists?

I am making an online grocery store as a homework assignment. I have successfully created the dictionaries of items including their stock and price from a text file. We have to create input statements where the users tell us what they want and how many. Then we have to create two functions in a separate python script.
I am concerned about Function 1 The purpose of this function is to:
Loop through the items in the groc_list (which I have created)
Pull the unit price of each item from the prices dictionary
Check the stock of the items in the stock dictionary versus the number of items selected in the unit_list.
If there are not enough items in stock, we have to respond to the user accordingly.
The function should update the stock dictionary to show a reduction in the total stock. (e.g. if the shopper selects 2 gallons of milk, the stock dictionary key – “milk” should have its value decreased by 2).
I continue to get an error code about my arguments in the main code and one about a "list" item not being callable in my function code. I cannot figure it out.
I am omitting the first part of the code where I create the dictionaries and what not. In this code, I get an error when I try to call the function with its arguments. I simply gives a regular 'error'
#code for main grocery store
#empty user shopping list
groc_list=[]
unit_list=[]
while 'done' not in groc_list:
groc_item=input('\nType the grocery item you would like to purchase.\If you are done shopping, type done: ')
groc_list.append(groc_item)
if groc_item == 'done':
print('Thanks for shopping')
#asks user quantity
elif groc_item in stock.keys():
unit_quan=input('How many would you like? ')
unit_list.append(unit_quan)
else:
print('We are sorry. We do not recognize that item.\Please check your spelling.')
# removes 'done'
groc_list.pop()
# function 1
from grocery import grocery_cost
grocery_cost(groc_list, prices, stock, unit_list)
In the function, I run into trouble when it goes to the 'elif' part of the statement. It says 'list' item is not callable. I think all my numbers are set as integers.
#The code for function 1
def grocery_cost(groc_list, prices, stock, unit_list):
"""Checks stock of items vs items customer selects. Prints if item is unavailable"""
count=0
for item in groc_list:
if stock[item] == 0:
print('We are sorry. We are out of stock of that item, \
and it will not be added to your cart.')
elif stock[item] >= unit_list(count):
y=unit_list(count)*prices[item]
print('%s %15.2f \n' %(item, y))
stock[item]=stock[item]-unit_list(count)
else:
print('Only', stock[item], ' of that item is available. \
Those have been added to your cart.')
y=stock[item]*prices[item]
unit_list.pop(count)
unit_list.insert(count, stock[item])
print('%s %15.2f \n' %(item, y))
stock[item]=stock[item]-unit_list(count)
count+=1
return y
I expected the program to calculate my totals for each item and allow me to take the returned value from the function and use it in my code, but instead, it quits.
unit_list(count) should be unit_list[count]
Try it, please!

Get Sales Rank of Product From Python Amazon Product Advertising API - ItemSearch

I have obtained a list of products based on Search Index, Browse Node and Keyword from the Amazon Product Advertising API using Python. Now I have to get the Sales Rank of the list of 20 products that I have obtained, but I am not able to print the value. I can sort the list by the products' Sales Rank but this does not give me the value of the Sales Rank corresponding to each product.
Here is my code so far:
from amazonproduct import api
import lxml
api = API(locale='us')
def search(index, keyword):
items = api.item_search(index, BrowseNode = '', Keywords = keyword)
return items
result = search("Books", "Harry Potter")
count = 0
for item in result:
print "%s" % (item.ItemAttributes.Title)
count += 1
if count == 20:
break
I have just recently looked into the Amazon API and I'm fairly unfamiliar with it, so I've just been reading the documentation and relying on it to figure stuff out.
Any help in printing the Sales_Rank would be greatly appreciated. Thanks a lot!
Try this:
print "%s" % (item.SalesRank)
You can view the contents of the item array using the pprint class. See this related post on how to display the contents of an array in Python: How to print a list in Python "nicely"

Confused about AttributeError in Python

I am trying to do the following project:
Imagine you have started up a small restaurant and are trying to make it easier to take and calculate orders. Since your restaurant only sells 9 different items, you assign each one to a number, as shown below.
Chicken Strips - $3.50
French Fries - $2.50
Hamburger - $4.00
Hotdog - $3.50
Large Drink - $1.75
Medium Drink - $1.50
Milk Shake - $2.25
Salad - $3.75
Small Drink - $1.25
To quickly take orders, your program should allow the user to type in a string of numbers and then it should calculate the cost of the order. For example, if one large drink, two small drinks, two hamburgers, one hotdog, and a salad are ordered, the user should type in 5993348, and the program should say that it costs $19.50. Also, make sure that the program loops so the user can take multiple orders without having to restart the program each time.
My code looks like this so far:
print "------------------Menu-------------------"
class Menu_Item():
def __init__(self, num,item,price):
self.num = num
self.item = item
self.price = price
def __repr__(self):
return "\n" + str(self.num) + ". " + self.item + " - $" + str(self.price) + ' dollars'
Strips = Menu_Item(1,'Chicken Strips', 3.50)
Fries = Menu_Item(2,"Fries",2.50)
Burger = Menu_Item(3,"Burger",4.00)
Hotdog = Menu_Item(4,"Hotdog",3.50)
Large_Drink = Menu_Item(5,"Large Drink",1.75)
Medium_Drink = Menu_Item(6,"Medium Drink", 1.50)
Milkshake = Menu_Item(7,"Milkshake", 2.25)
Salad = Menu_Item(8,"Salad", 3.75)
Small_Drink = Menu_Item(9,"Small Drink", 1.25)
Class_Items = [Strips,Fries,Burger,Hotdog,Large_Drink,Medium_Drink,Milkshake,Salad,Small_Drink]
print Class_Items
def take_order():
count = 0
string = raw_input("Enter your order")
order = []
for a in string:
order.append(a)
for food in Class_Items:
for b in order:
if b == Menu_Item.num:
count = count + Menu_Item.price
return count
else:
print "-"
take_order()
I am getting the following error. I have tried a ton of ways to make this work and I can't figure out why the compiler isn't recognizing the num attribute. Any suggestions as to why this is happening? Thanks!
Traceback (most recent call last):
File "python", line 43, in <module>
File "python", line 37, in take_order
AttributeError: class Menu_Item has no attribute 'num'
Why are you accessing Menu_Item here? That's the class itself; the item that you've got from the list is called food.
Menu_Item is the class. You attempted to access an attribute of the class. Think of a class like a blueprint for a house. You asked for the street address for the blueprint.
In Python, you're supposed to name classes with CamelCase by convention and PEP 8. So, MenuItem instead of Menu_Item. You are also supposed to name variables using separated_by_underscores. This way, you can easily distinguish between classes and normal objects.
I think what you want is:
for food in Class_Items:
for b in order:
if b == food.num:
...
This way, instead of comparing the blueprint's street address, you compare the actual house's street address. Python ≠ English; the blueprint is always Menu_Item and food will refer to a house. (assuming you don't explicitly say Menu_Item = ... or likewise food = ...)
Also, python doesn't have a compiler. It's not a compiled language.
You should inherit off of object by convention; class MenuItem(object):
The foods would be better implemented as classes with class attributes that inherit off of MenuItem.
Class attributes are attributes defined in the body of the class. For example:
class HotDog(MenuItem):
price = 1.5
would let you write HotDog.price and receive 1.5 as the answer.
You've got several problems here. Most notably the one pointed out by Daniel Roseman and uoɥʇʎPʎzɐɹC. However, after fixing that issue you will still have problems with your code.
In take_order() you need to move return count outside of your for loop otherwise you will return count after the first item is added. You will also need to change the input to an integer or b == food.num will never resolve to True.
In addition to the useful answers you received, I just wanted to point out that you can solve this problem with a simple function, without having to define a new class (the project didn't require that, according to what you wrote).
To begin with, you can create a dictionary of the items on the menu and their price:
menu_items={'1':['Chicken Strips',3.50],'2':['French Fries',2.50],'3':['Hamburger',4.00],'4':['Hotdog',3.50],'5':['Large Drink',1.75],'6':['Medium Drink',1.50],'7':['Milk Shake',2.25],'8':['Salad',3.75],'9':['Small Drink',1.25]}
After that, you can create a function that addresses you needs:
def total_order():
total=0
while True:
order=str(input('Please enter your order, or type "done" to terminate the program: '))
sum_order=0
if order=='done':
print('\nProgram terminated. The overall total order is: '+str(total)+'$')
return total
for number in order:
if number in menu_items:
sum_order+=menu_items[number][1]
total+=sum_order
print('The total for this order is: '+str(sum_order)+'$\n')
total_order()
The function keeps executing until you type the word 'done', and it prints the amount of money spent for each order, as well as the total amount of money spent (for all the orders together).

MLM downline distribution count

I make my first MLM software and I think I managed to code how to get the points from the downline even though it is a recursive problem I didn't use recursion and I might refactor to a recursive version if that seems better. With our system, the level of a distributor is measured i number of silvers and for each product that gets sold the promotion/bonus/score/points works upline so if Bob is the sponsor of Alice and Alice makes a purchase then Bob will get points measured in number of silvers for that purchase. I added a business function to my user class:
def this_month_non_manager_silver(self):
silver = 0
today = date.today()
timeline = date(today.year, today.month, 1)
downline = User.query(User.sponsor
== self._key).fetch()
distributor = self
while distributor.has_downline():
downline = User.query(User.sponsor == distributor.key).fetch()
for person in downline:
orders = model.Order.all().filter('buyer_id =' , person.key.id()).filter('created >' , timeline).filter('status =', 'PAID').fetch(999999)
for order in orders:
for idx,item in enumerate(order.items):
purchase = model.Item.get_by_id(long(item.id()))
amount = int(order.amounts[idx])
silver = silver + amount*purchase.silver/1000.000
distributor = person
return silver
What might be to do is now just a % on the silver according to the depth of the order.
The code actually output the correct result for an order downline but I didn't yet test it extensively and I wonder if you think the code looks strange and if I have thought of everything since the models are somewhat complicated / advanced. The user class is from webapp2 and I could use a subclass but I didn't have time to do that so I just put in the method to the user class that's there and now I can call it from Jinja2 like {{user.this_month_non_manager_silver}}
Recursion might to be right way to do this but isn't my solution still OK and I can move on and keep this code for now or do you think it is not acceptable?
Thanks for any constructive criticism.
The main problem I see here is that you're essentially trying to do a breadth-first search (you look at all the users who are below the distributor, then look at all of the users below those distributors, etc etc), but each time the while loop loops you're only looking at the users below the last distributor.
If we break down the important parts into something python-ish, you get this:
distributor=self
while distributor.has_downline():
for person in distributor.downline:
distributor = person
As you can see, the value of distributor after the first set of downlines are accessed is the last distributor in the user's downline. Then the next time the for loop is run, you're only looking at the last distributor's downline.
Traditionally a tree-walking algorithm is either recursive or loop-based with a stack. Generally you will choose one or the other based on memory constraints. To keep the solution iterative, you'd need to rewrite the above python-ish code like this:
downlinestack = []
distributor=self
downlinestack += distributor.downline
while downlinestack:
downline = downlinestack.pop()
for person in downline:
downlinestack.append(person.downline)

Categories

Resources