Python for Profit

Building a Complete Inventory System

08

Full Control: Updating and Deleting Inventory

The Big Idea

This chapter completes our core application logic by adding the final two essential operations of data management: updating an existing product's details and deleting a product from the inventory.

Roadmap

  • Introducing CRUD: We'll learn about CRUD (Create, Read, Update, Delete), the four fundamental operations of any data-driven application.

  • The Helper Function Pattern: A professional technique for avoiding code repetition. We'll build a find_product helper function that both our update and delete functions will use.

  • Searching with enumerate: Learn how to use the enumerate function to get both the index and the item from a list while looping.

  • Building the update_product Function: Create the logic to find an item, ask for new values, and modify the dictionary in place.

  • Building the delete_product Function: Create the logic to find an item and remove it from the inventory list using the .pop() method.

  • Expanding the Main Menu: Integrate the new Update and Delete options into our application's main loop.

Full Chapter Content

CRUD: The Four Pillars of Data

So far, our application can Create new products (with add_product) and Read them (with display_inventory). These are two of the four fundamental operations of data management, collectively known as CRUD:

  • Create: Add new data.

  • Read: Retrieve data.

  • Update: Modify existing data.

  • Delete: Remove existing data.

To make our application complete, we need to implement Update and Delete.

A Professional Technique: The Helper Function

Both updating and deleting a product start with the exact same first step: finding the product in our inventory list. Instead of writing the finding logic twice (violating the DRY principle), we'll create a single, reusable helper function called find_product.

This function will ask the user for a product name, loop through the inventory to find it, and then return the product's dictionary and its index (position) in the list.

To do this efficiently, we'll use a new tool: enumerate(). When you use enumerate in a for loop, it gives you back two values on each iteration: the index of the item, and the item itself.

for index, item in enumerate(inventory_list):

Now, let's build our new features step-by-step.

Step 1: Create the find_product Helper Function

This function's only job is to search. It is a tool for our other functions to use.

Add this new function to the "Function Definitions" section of your main.py file. It's good practice to place helper functions before the main functions that use them.

def find_product(inventory_list):
    """
    Helper function to find a product by name and return its index and data.
    """
    search_name = input("Enter the name of the product to find: ")
    for index, product in enumerate(inventory_list):
        if product['name'].lower() == search_name.lower():
            return index, product # Return both the index and the product dict
    return None, None # Return None if not found

What's New Here?

  • enumerate(inventory_list): Provides both the index and the product dictionary for each item in the list.

  • .lower(): We compare both the stored name and the search term in lowercase to make the search case-insensitive ('laptop' will find 'Laptop').

  • return index, product: If we find a match, we return two values at once.

  • return None, None: If the loop finishes without finding a match, we return None (a special Python value representing "nothing") for both values.

Step 2: Create the update_product Function

This function will orchestrate the update process. It will call our new helper function first.

Add this function to main.py:

def update_product(inventory_list, currency_symbol):
    """Finds a product and allows the user to update its details."""
    index, product = find_product(inventory_list)

    if product is None:
        print("Product not found.")
        return

    print(f"Found: {product['name']}. Current quantity: {product['quantity']}, Current price: {currency_symbol}{product['price']}")

    # Get new values, allowing user to press Enter to skip a change
    new_quantity_str = input(f"Enter new quantity (or press Enter to keep {product['quantity']}): ")
    new_price_str = input(f"Enter new price (or press Enter to keep {product['price']}): ")

    if new_quantity_str:
        product['quantity'] = int(new_quantity_str)

    if new_price_str:
        product['price'] = float(new_price_str)

    print(f"SUCCESS: '{product['name']}' has been updated.")

What's New Here?

  • It first calls find_product() and checks if the result was None.

  • It cleverly allows the user to update only what they need. If the user just presses Enter, the input string is empty, the if condition is false, and the old value is kept.

Step 3: Create the delete_product Function

This function will also use our helper, but its action is simpler: remove the item from the list. We do this with the .pop() list method, which removes an item at a specific index.

Add this final function to main.py:

def delete_product(inventory_list):
    """Finds and deletes a product from the inventory."""
    index, product = find_product(inventory_list)

    if product is None:
        print("Product not found.")
        return

    product_name = product['name'] # Store name for the message
    inventory_list.pop(index)
    print(f"SUCCESS: '{product_name}' has been deleted from the inventory.")

Step 4: Update the Main Menu

The last step is to add the new options to our while loop so the user can actually use our new functions.

Modify the main loop at the end of main.py:

# --- Main Application Logic ---
inventory = load_inventory_from_file(DATA_FILE)

while True:
    print("\n--- PyInventory Main Menu ---")
    print("1: Display Current Inventory")
    print("2: Add a New Product")
    print("3: Update a Product") # New option
    print("4: Delete a Product") # New option
    print("q: Quit")

    choice = input("Please enter your choice (1, 2, 3, 4, or q): ")

    if choice == '1':
        display_inventory(inventory, CURRENCY)
    elif choice == '2':
        add_product(inventory, CURRENCY)
    elif choice == '3': # New block
        update_product(inventory, CURRENCY)
    elif choice == '4': # New block
        delete_product(inventory)
    elif choice.lower() == 'q':
        save_inventory_to_file(inventory, DATA_FILE)
        print("Inventory saved. Exiting PyInventory. Goodbye!")
        break
    else:
        print("Invalid choice. Please try again.")

The full, final code for this chapter is available below. You can run it and test your new powers to update quantities and prices, and to remove products entirely.

The Complete main.py for Chapter 8

# --- PyInventory: A Step-by-Step Journey to Profit ---
# Chapter 8: Full Control: Updating and Deleting Inventory

import json

# --- Configuration ---
CURRENCY = "MAD"
DATA_FILE = "inventory.json"

# --- Function Definitions ---

def save_inventory_to_file(inventory_list, filename):
    with open(filename, 'w') as file:
        json.dump(inventory_list, file, indent=4)

def load_inventory_from_file(filename):
    try:
        with open(filename, 'r') as file:
            return json.load(file)
    except FileNotFoundError:
        return []

def find_product(inventory_list):
    """Helper function to find a product by name and return its index and data."""
    search_name = input("Enter the name of the product to find: ")
    for index, product in enumerate(inventory_list):
        if product['name'].lower() == search_name.lower():
            return index, product
    return None, None

def display_inventory(inventory_list, currency_symbol):
    print("\n--- Current Inventory Report ---")
    print("-" * 60)
    total_inventory_value = 0.0
    if not inventory_list:
        print("Inventory is currently empty.")
    else:
        print(f"{'Product':<30} | {'Quantity':<10} | {'Value':>15}")
        print("-" * 60)
        for item in inventory_list:
            item_value = item['quantity'] * item['price']
            total_inventory_value += item_value
            print(f"{item['name']:<30} | {item['quantity']:<10} | {currency_symbol}{item_value:>14.2f}")
    print("-" * 60)
    print(f"GRAND TOTAL INVENTORY VALUE: {currency_symbol}{total_inventory_value:>40.2f}")
    print("-" * 60)

def add_product(inventory_list, currency_symbol):
    print("\n--- Add a New Product ---")
    product_name = input("Enter the new product name: ")
    quantity_str = input(f"Enter the quantity for {product_name}: ")
    price_str = input(f"Enter the price per item (in {currency_symbol}): ")
    new_product = {"name": product_name, "quantity": int(quantity_str), "price": float(price_str)}
    inventory_list.append(new_product)
    print(f"\nSUCCESS: '{product_name}' has been added to the inventory.")

def update_product(inventory_list, currency_symbol):
    """Finds a product and allows the user to update its details."""
    index, product = find_product(inventory_list)
    if product is None:
        print("Product not found.")
        return
    print(f"Found: {product['name']}. Current quantity: {product['quantity']}, Current price: {currency_symbol}{product['price']}")
    new_quantity_str = input(f"Enter new quantity (or press Enter to keep {product['quantity']}): ")
    new_price_str = input(f"Enter new price (or press Enter to keep {product['price']}): ")
    if new_quantity_str:
        product['quantity'] = int(new_quantity_str)
    if new_price_str:
        product['price'] = float(new_price_str)
    print(f"SUCCESS: '{product['name']}' has been updated.")

def delete_product(inventory_list):
    """Finds and deletes a product from the inventory."""
    index, product = find_product(inventory_list)
    if product is None:
        print("Product not found.")
        return
    product_name = product['name']
    inventory_list.pop(index)
    print(f"SUCCESS: '{product_name}' has been deleted from the inventory.")

# --- Main Application Logic ---
inventory = load_inventory_from_file(DATA_FILE)
while True:
    print("\n--- PyInventory Main Menu ---")
    print("1: Display Current Inventory")
    print("2: Add a New Product")
    print("3: Update a Product")
    print("4: Delete a Product")
    print("q: Quit")
    choice = input("Please enter your choice (1, 2, 3, 4, or q): ")
    if choice == '1':
        display_inventory(inventory, CURRENCY)
    elif choice == '2':
        add_product(inventory, CURRENCY)
    elif choice == '3':
        update_product(inventory, CURRENCY)
    elif choice == '4':
        delete_product(inventory)
    elif choice.lower() == 'q':
        save_inventory_to_file(inventory, DATA_FILE)
        print("Inventory saved. Exiting PyInventory. Goodbye!")
        break
    else:
        print("Invalid choice. Please try again.")


Chapter 8: Summary & What's Next

You now have a fully functional CRUD application!

  • You learned about the four crucial CRUD operations.

  • You wrote a helper function to avoid repeating code.

  • You used enumerate to search a list efficiently.

  • You implemented logic to update and delete inventory items.

Our command-line tool is now feature-complete. However, as applications grow, errors become more likely. What if the user enters "abc" for a product's price? Our program will crash. In the next chapter, we will make our program robust and professional by adding comprehensive error handling.