Reputation: 115
I am currently working on an Inventory System, and have just developed code that allows me to add items to my .csv
My current csv file looks like this:
Here is my relevant code:
class CsvReader:
def __init__(self):
self.result = []
def make_dict_items(self):
#fn = os.path.join('subdir', "Items2.csv")
with open("Items2.csv") as fp:
reader = csv.reader(fp)
labels = next(reader, None)
result = []
for row in reader:
row[0] = int(row[0])
row[1] = float(row[1])
row[2] = int(row[2])
pairs = zip(labels, row)
self.result.append(dict(pairs))
def show_available(self):
for item in self.result:
print(item)
def add_item(self):
item_num = int(input("What is the items #?\n"))
price = float(input("What is the items price?\n"))
quant = int(input("What is the items quantity?\n"))
name = input("What is the items name?\n")
new_row = [item_num, price, quant, name]
with open("Items2.csv", "a+") as fp:
reader = csv.reader(fp)
fp.seek(0)
labels = next(reader, None)
writer = csv.writer(fp)
new_record = dict(zip(labels, new_row))
self.result.append(new_record)
writer.writerow(new_record.values())
From what I understand, my add item is able to successfully input into my csv. For example, if I input 1, 2.99, 3, and Pumpkin into the inputs, and print(system.result), I can see the new dictionary in the list, and visually see it if I were to open the csv document.
What I dont understand is my current error I am getting. When I try to view my new item with my existing method that shows all items, I get an error:
row[0] = int(row[0])
IndexError: list index out of range
I understand this error has to do with trying to call elements in a list that don't exist, but what seems to be the issue here. Why when I have the four items in my csv, the code works fine, but when I add a new row into the csv file, the code fails? Why does that specific line in make_dict_items not work when a 5th item is added, but works with the beginning four?
Upvotes: 0
Views: 947
Reputation: 11612
I would also suggest looking into something like dataclasses and dataclass-csv for this, just to align with best practices in Python and to also to streamline the task, and make it easier to work with the data.
Here are contents of Book.csv
that I was testing with:
Item #,Price,Quantity,Name
1,5.99,1,Blueberry Muffin
2,6.99,2,Bannana-nut Muffin
3,7.99,3,Chocolate Chip Cookie
4,8.99,4,Oatmeal Cookie
For example, here's a working sample I was able to test with. This works with pip install dataclass-csv
to install the library beforehand.
from dataclasses import dataclass
import dataclass_csv
@dataclass
class MyClass:
item_no: int
price: float
quantity: int
name: str
with open('Book.csv') as f:
reader = dataclass_csv.DataclassReader(f, MyClass)
reader.map('Item #').to('item_no')
reader.map('Price').to('price')
reader.map('Name').to('name')
reader.map('Quantity').to('quantity')
for line in reader:
print(line)
Notice here how the type annotations such as price: float
indicate that the type of the field should be float
for example.
The output of the above code is as follows:
MyClass(item_no=1, price=5.99, quantity=1, name='Blueberry Muffin')
MyClass(item_no=2, price=6.99, quantity=2, name='Bannana-nut Muffin')
MyClass(item_no=3, price=7.99, quantity=3, name='Chocolate Chip Cookie')
MyClass(item_no=4, price=8.99, quantity=4, name='Oatmeal Cookie')
I'm planning to update my flagship library that I've been developing for a while now, dataclass-wizard, so that it adds proper CSV support -- currently it does not handle CSV data at all. I've checked in a WIP branch to track my efforts so far, and hope to create a PR and release soon.
Usage should be mostly similar to above, but hopefully more streamlined in some aspects:
from dataclasses import dataclass
from typing import Annotated # Python < 3.9: import from `typing_extensions` instead
from dataclass_wizard import Alias, CSVWizard
@dataclass
class Inventory(CSVWizard):
item_no: Annotated[int, Alias('Item #', all=True)]
price: float
quantity: int
name: str
# items is type `Container[Inventory]`
items = Inventory.from_csv_file('Book.csv')
print(items)
# add a new row to existing CSV file
new_item = Inventory(7, 12.99, quantity=10, name='Apple Strudel')
new_item.append_to_csv_file('Book.csv')
# copy data to a new file
items.to_csv_file('Book.New.csv')
Output is similar to above:
[Inventory(item_no=1, price=5.99, quantity=1, name='Blueberry Muffin'),
Inventory(item_no=2, price=6.99, quantity=2, name='Bannana-nut Muffin'),
Inventory(item_no=3, price=7.99, quantity=3, name='Chocolate Chip Cookie'),
Inventory(item_no=4, price=8.99, quantity=4, name='Oatmeal Cookie')]
Upvotes: 0
Reputation: 781058
Your CSV has blank lines, which create empty row
. Check for this and skip it.
for row in reader:
if row:
row[0] = int(row[0])
row[1] = float(row[1])
row[2] = int(row[2])
pairs = zip(labels, row)
self.result.append(dict(pairs))
Upvotes: 2