Master Python CRUD operations step-by-step — and discover how this simple project is the launchpad to full-stack web development with Django.
Python CMD → Python + File Storage → Django Web App → Django + MySQL → REST API → Full-Stack Developer 🎯 Mastering CRUD in the terminal is the foundation that every full-stack developer must understand first.
Welcome to one of the most practical beginner Python tutorials you will find! 🎉 In this complete guide, you will build a command-line Car Rental System using pure Python — no external libraries, no complicated setup. Just Python and your terminal.
But this tutorial is about more than just a Car Rental app. Every line of code you write here directly teaches you a skill that applies to real-world full-stack development. When you later build Django web apps, REST APIs, or database-powered systems, you will look back at this project and realise it taught you everything that matters:
Lists and dictionaries — the same concept as database tables and rows.
Loops and conditionals that power every application ever built.
Reusable code blocks — identical in concept to Django views.
Create, Read, Update, Delete — the backbone of every app and API.
Processing input — mirrors handling web forms and API requests.
Splitting code into small functions — the foundation of clean architecture.
CRUD is an acronym that describes the four essential operations any data management system must support. It is not a Python-specific concept — it applies equally to MySQL databases, REST APIs, Django ORM, Firebase, Excel spreadsheets, and even paper filing systems! 😄
| Letter | Operation | In Our App | In Django/Web Apps | In SQL |
|---|---|---|---|---|
| C | Create | Add a new car to the list | POST request / form submit | INSERT INTO |
| R | Read | Display all cars | GET request / list view | SELECT * FROM |
| U | Update | Edit car details | PUT / PATCH request | UPDATE SET |
| D | Delete | Remove a car | DELETE request | DELETE FROM |
By completing this tutorial, you will have personally implemented all four CRUD operations in Python. That understanding transfers directly to any framework or language you use in the future. 🔥
Before you start coding, make sure you have the following ready. Don't worry — the setup takes less than 5 minutes!
Open your terminal and type this to confirm Python is installed:
python --version
# or on some systems:
python3 --version
# You should see something like:
# Python 3.11.4
Create a new folder and file to keep your work organised:
# Create a folder
mkdir car-rental
cd car-rental
# Create the Python file
# On Windows:
type nul > car_rental.py
# On Mac/Linux:
touch car_rental.py
code . and you can write code and run it in the integrated terminal side by side. Highly recommended for beginners!
Here is what our completed Car Rental System will be able to do. When a user runs the program, they will see a text-based menu with the following options:
==== Car Rental System ====
1. Add Car
2. View Cars
3. Update Car
4. Delete Car
5. Rent a Car
6. Return a Car
7. Exit
Enter choice:
Each option maps to a specific function in our code. Here is a quick summary of what each feature does:
| Menu Option | Function | CRUD Type | What It Does |
|---|---|---|---|
| 1. Add Car | add_car() | CREATE | Takes input and adds a car to the list |
| 2. View Cars | view_cars() | READ | Displays all cars with status |
| 3. Update Car | update_car() | UPDATE | Edits an existing car's details |
| 4. Delete Car | delete_car() | DELETE | Removes a car from the list |
| 5. Rent Car | rent_car() | UPDATE | Marks a car as rented |
| 6. Return Car | return_car() | UPDATE | Marks a car as available again |
In Python, we store our car data in a list of dictionaries. Before we write a single function, we need to understand this data structure clearly — because everything else depends on it.
Think of a list like a table in a spreadsheet. Each dictionary inside the list is one row of data. Each key in the dictionary is a column header.
# Our car "database" — a Python list
cars = []
# What one car record looks like (a dictionary):
# {
# "brand": "Toyota",
# "model": "Corolla",
# "price": 50.0, # price per day
# "rented": False # is it currently rented?
# }
Car.objects.create(brand="Toyota") in Django, it's doing the exact same concept as cars.append({...}) here — just saving it to a real database instead. The logic is identical!
"brand" — The manufacturer name (e.g., Toyota, Honda, BMW)"model" — The specific model name (e.g., Corolla, Civic, X5)"price" — Rental price per day as a float (e.g., 50.0)"rented" — A boolean (True or False) indicating availability
The CREATE operation is all about taking new data from the user and storing it.
Our add_car() function uses Python's built-in input() function to collect
the car details, builds a dictionary, and appends it to the cars list.
def add_car():
print("\n-- Add New Car --")
# Collect user input
brand = input("Car Brand: ")
model = input("Car Model: ")
year = input("Year of Manufacture: ")
# Keep asking until valid price is entered
while True:
try:
price = float(input("Rental Price Per Day ($): "))
if price <= 0:
print("⚠️ Price must be greater than zero. Try again.")
continue
break
except ValueError:
print("⚠️ Please enter a valid number.")
# Build the car dictionary
car = {
"brand": brand,
"model": model,
"year": year,
"price": price,
"rented": False
}
# Add to our list
cars.append(car)
print(f"\n✅ {brand} {model} added successfully!")
try/except block. This prevents your program from crashing if someone types letters instead of a number for the price. This is a real-world coding habit every developer must have!
In a Django web app, the equivalent of add_car() would be:
# Django equivalent — views.py
def add_car_view(request):
if request.method == "POST":
form = CarForm(request.POST)
if form.is_valid():
form.save() # Same as cars.append(car) but saves to MySQL!
return redirect("car_list")
See the parallels? The logic is the same — you are just collecting data and storing it. 🚀
The READ operation retrieves stored data and displays it to the user.
Our view_cars() function loops through every dictionary in the cars list
and prints a formatted line for each one.
def view_cars():
print("\n-- Available Cars --")
if not cars:
print("No cars available. Add some cars first! 🚗")
return
print(f"\n{'ID':<5} {'Brand':<12} {'Model':<15} {'Year':<6} {'Price/Day':<12} {'Status'}")
print("-" * 60)
for index, car in enumerate(cars):
status = "🔴 Rented" if car["rented"] else "🟢 Available"
print(
f"{index:<5} {car['brand']:<12} {car['model']:<15} "
f"{car.get('year','N/A'):<6} ${car['price']:<11.2f} {status}"
)
print("-" * 60)
print(f" Total cars: {len(cars)}")
We use Python's f-string formatting with alignment characters (< for left-align)
to create a clean, table-like output in the terminal. The enumerate() function gives us
both the index (ID) and the car dictionary in each loop iteration.
for index, car in enumerate(cars) is the same as writing for i in range(len(cars)):
but much cleaner. index gives you the position (0, 1, 2...) and car gives you
the dictionary at that position.
The UPDATE operation lets the user modify existing records. First we show the list so the user can pick the correct ID, then we update only the fields the user provides input for. If they leave a field blank, the old value is kept.
def update_car():
view_cars()
if not cars:
return
try:
car_id = int(input("\nEnter Car ID to update: "))
if car_id < 0 or car_id >= len(cars):
print("❌ Invalid ID. Please enter a number from the list above.")
return
except ValueError:
print("❌ Please enter a valid number.")
return
car = cars[car_id]
print(f"\nUpdating: {car['brand']} {car['model']} (leave blank to keep current value)\n")
new_brand = input(f" Brand [{car['brand']}]: ").strip()
new_model = input(f" Model [{car['model']}]: ").strip()
new_price = input(f" Price/Day [${car['price']}]: ").strip()
new_year = input(f" Year [{car.get('year','N/A')}]: ").strip()
# Only update fields where user entered something
if new_brand: car["brand"] = new_brand
if new_model: car["model"] = new_model
if new_year: car["year"] = new_year
if new_price:
try:
car["price"] = float(new_price)
except ValueError:
print("⚠️ Invalid price entered — keeping original value.")
print(f"\n✅ Car updated successfully!")
Brand [Toyota]:. This is a UX (User Experience) pattern used in professional CLI tools.
It saves users from having to remember the old value before editing.
The DELETE operation permanently removes a record. We always add a confirmation prompt before deleting — a critical safety measure in any real-world application!
def delete_car():
view_cars()
if not cars:
return
try:
car_id = int(input("\nEnter Car ID to delete: "))
if car_id < 0 or car_id >= len(cars):
print("❌ Invalid ID.")
return
except ValueError:
print("❌ Please enter a valid number.")
return
car = cars[car_id]
# ⚠️ Always confirm before deleting!
confirm = input(f"\n⚠️ Are you sure you want to delete {car['brand']} {car['model']}? (yes/no): ")
if confirm.lower() == "yes":
cars.pop(car_id)
print("✅ Car deleted successfully!")
else:
print("❌ Deletion cancelled.")
yes/no prompt. Skipping this is how developers accidentally delete important data!
Real applications have business logic — actions that go beyond basic CRUD and implement
actual business rules. In our Car Rental System, the ability to rent and return cars is the core feature.
These are essentially special UPDATE operations that change the rented status field.
def rent_car():
"""Mark a car as rented."""
view_cars()
if not cars:
return
try:
car_id = int(input("\nEnter Car ID to rent: "))
if car_id < 0 or car_id >= len(cars):
print("❌ Invalid ID.")
return
except ValueError:
print("❌ Please enter a valid number.")
return
car = cars[car_id]
if car["rented"]:
print(f"❌ Sorry! {car['brand']} {car['model']} is already rented.")
else:
days = int(input("How many days do you want to rent? "))
total_cost = car["price"] * days
car["rented"] = True
print(f"\n✅ {car['brand']} {car['model']} rented for {days} days.")
print(f" 💰 Total Cost: ${total_cost:.2f}")
def return_car():
"""Mark a car as returned (available)."""
view_cars()
if not cars:
return
try:
car_id = int(input("\nEnter Car ID to return: "))
if car_id < 0 or car_id >= len(cars):
print("❌ Invalid ID.")
return
except ValueError:
print("❌ Please enter a valid number.")
return
car = cars[car_id]
if not car["rented"]:
print(f"❌ {car['brand']} {car['model']} is not currently rented.")
else:
car["rented"] = False
print(f"✅ {car['brand']} {car['model']} has been returned. Now available!")
rent_car()
become service layer methods or Django model methods in a real web app.
The logic is the same — you are validating state (is it already rented?) and performing
a conditional update. Every web app you ever build will have this kind of business logic.
The menu function is the controller of our application. It runs in an infinite while True
loop, continuously presenting options to the user and calling the appropriate function based on their input.
The loop only exits when the user chooses option 7.
def menu():
"""Main application loop."""
print("\n" + "="*40)
print(" 🚗 Welcome to Car Rental System 🚗")
print("="*40)
while True:
print("\n──── Main Menu ────")
print(" 1️⃣ Add Car")
print(" 2️⃣ View Cars")
print(" 3️⃣ Update Car")
print(" 4️⃣ Delete Car")
print(" 5️⃣ Rent a Car")
print(" 6️⃣ Return a Car")
print(" 7️⃣ Exit")
print("───────────────────")
choice = input("Enter your choice (1-7): ").strip()
if choice == "1": add_car()
elif choice == "2": view_cars()
elif choice == "3": update_car()
elif choice == "4": delete_car()
elif choice == "5": rent_car()
elif choice == "6": return_car()
elif choice == "7":
print("\n👋 Thank you for using Car Rental System. Goodbye!")
break
else:
print("❌ Invalid option. Please enter a number between 1 and 7.")
# ── Entry Point ──
if __name__ == "__main__":
menu()
if __name__ == "__main__": Explained:
This line is a Python best practice. It ensures the menu() function only runs when you
directly execute the file. If another Python file imports this file as a module,
menu() won't automatically run. This is important for building larger, modular projects.
Here is the entire Car Rental System in one file. You can copy this, save it as car_rental.py,
and run it with python car_rental.py in your terminal!
# ============================================================
# 🚗 Python Car Rental System — EgoTECH World
# Complete CRUD Command-Line Application
# Perfect starter project for full-stack beginners
# egotechworld.com
# ============================================================
cars = [] # Our in-memory "database"
# ── CREATE ──────────────────────────────────────────────────
def add_car():
print("\n-- Add New Car --")
brand = input("Car Brand: ").strip()
model = input("Car Model: ").strip()
year = input("Year of Manufacture: ").strip()
while True:
try:
price = float(input("Rental Price Per Day ($): "))
if price <= 0:
print("⚠️ Price must be positive.")
continue
break
except ValueError:
print("⚠️ Invalid price. Enter a number.")
cars.append({
"brand": brand,
"model": model,
"year": year,
"price": price,
"rented": False
})
print(f"\n✅ {brand} {model} added successfully!")
# ── READ ─────────────────────────────────────────────────────
def view_cars():
print("\n-- Car List --")
if not cars:
print("No cars found. Add some cars first! 🚗")
return
print(f"\n{'ID':<5} {'Brand':<12} {'Model':<15} {'Year':<6} {'Price/Day':<12} {'Status'}")
print("-" * 62)
for i, car in enumerate(cars):
status = "🔴 Rented" if car["rented"] else "🟢 Available"
print(f"{i:<5} {car['brand']:<12} {car['model']:<15} {car.get('year','N/A'):<6} ${car['price']:<11.2f} {status}")
print("-" * 62)
print(f" Total: {len(cars)} car(s)")
# ── UPDATE ───────────────────────────────────────────────────
def update_car():
view_cars()
if not cars:
return
try:
cid = int(input("\nCar ID to update: "))
assert 0 <= cid < len(cars)
except (ValueError, AssertionError):
print("❌ Invalid ID.")
return
car = cars[cid]
print(f"\nUpdating {car['brand']} {car['model']} (blank = keep current)\n")
b = input(f" Brand [{car['brand']}]: ").strip()
m = input(f" Model [{car['model']}]: ").strip()
y = input(f" Year [{car.get('year','N/A')}]: ").strip()
p = input(f" Price [${car['price']}]: ").strip()
if b: car["brand"] = b
if m: car["model"] = m
if y: car["year"] = y
if p:
try:
car["price"] = float(p)
except ValueError:
print("⚠️ Invalid price — keeping original.")
print("✅ Car updated!")
# ── DELETE ───────────────────────────────────────────────────
def delete_car():
view_cars()
if not cars:
return
try:
cid = int(input("\nCar ID to delete: "))
assert 0 <= cid < len(cars)
except (ValueError, AssertionError):
print("❌ Invalid ID.")
return
car = cars[cid]
if input(f"⚠️ Delete {car['brand']} {car['model']}? (yes/no): ").lower() == "yes":
cars.pop(cid)
print("✅ Car deleted!")
else:
print("❌ Cancelled.")
# ── RENT ─────────────────────────────────────────────────────
def rent_car():
view_cars()
if not cars:
return
try:
cid = int(input("\nCar ID to rent: "))
assert 0 <= cid < len(cars)
except (ValueError, AssertionError):
print("❌ Invalid ID.")
return
car = cars[cid]
if car["rented"]:
print(f"❌ {car['brand']} {car['model']} is already rented!")
else:
try:
days = int(input("Number of days: "))
cost = car["price"] * days
car["rented"] = True
print(f"✅ Rented for {days} day(s). 💰 Total: ${cost:.2f}")
except ValueError:
print("❌ Invalid input.")
# ── RETURN ───────────────────────────────────────────────────
def return_car():
view_cars()
if not cars:
return
try:
cid = int(input("\nCar ID to return: "))
assert 0 <= cid < len(cars)
except (ValueError, AssertionError):
print("❌ Invalid ID.")
return
car = cars[cid]
if not car["rented"]:
print(f"❌ {car['brand']} {car['model']} is not rented.")
else:
car["rented"] = False
print(f"✅ {car['brand']} {car['model']} returned. Now available!")
# ── MENU ─────────────────────────────────────────────────────
def menu():
print("\n" + "="*42)
print(" 🚗 Car Rental System — EgoTECH World")
print("="*42)
while True:
print("\n──── Main Menu ────")
print(" 1 Add Car (CREATE)")
print(" 2 View Cars (READ)")
print(" 3 Update Car (UPDATE)")
print(" 4 Delete Car (DELETE)")
print(" 5 Rent a Car")
print(" 6 Return a Car")
print(" 7 Exit")
print("───────────────────")
choice = input("Choice (1-7): ").strip()
if choice == "1": add_car()
elif choice == "2": view_cars()
elif choice == "3": update_car()
elif choice == "4": delete_car()
elif choice == "5": rent_car()
elif choice == "6": return_car()
elif choice == "7":
print("\n👋 Goodbye! Keep coding! 🚀")
break
else:
print("❌ Enter a number from 1 to 7.")
if __name__ == "__main__":
menu()
Prefer learning by watching? This video covers Python fundamentals including functions, lists, dictionaries, loops, and user input — everything you need to fully understand this project. Watch it alongside this tutorial!
🎓 Recommended: Python for Beginners — covers all the concepts used in this project. Watch at 1.25x speed once you understand the basics!
Once you have the code saved as car_rental.py, here's a step-by-step test workflow:
python car_rental.py
Once you are comfortable with the basic version, here are challenges you can try yourself. Each one teaches you a new skill:
Add a Search Feature
Write a search_cars(brand) function that loops through the list and prints only matching cars. Practice: loops + string comparison.
Count Rented vs Available
Add a summary line to view_cars() showing how many cars are rented and how many are available. Practice: conditional counting.
Save Data to JSON File
Use Python's json module to save and load the cars list from a file. Your data will now persist between runs! Practice: file I/O.
Add a Customer System
Create a second list: customers = []. Track which customer rented which car. Practice: relationships between data — exactly like foreign keys in SQL!
Migrate to SQLite
Replace the list with an SQLite database using Python's built-in sqlite3 module. Now your CRUD operations use real SQL queries. Practice: databases!
Build the Django Version
Rebuild this entire system as a Django web application with HTML pages, forms, and MySQL. This is the full-stack milestone! Follow our Django courses on EgoTECH. 🚀
This project is Step 1 of a carefully designed learning journey. Here is the complete path from absolute beginner to full-stack Python developer — and you are already on it! 🎉
Functions, loops, dictionaries, user input, CRUD operations, input validation. This project.
Persist your data using JSON files. Learn file reading/writing, data serialisation. Same car rental system — now it remembers data!
Replace JSON with a real relational database. Write actual SQL INSERT, SELECT, UPDATE, DELETE queries from Python. Huge milestone!
Learn Django's MVT architecture. Create models, views, templates, URLs. Rebuild the car rental as a real webpage. HTML forms instead of input()!
Connect your Django app to MySQL, add user login/registration, role-based permissions. This is production-level back-end development!
Build JSON APIs that mobile apps and frontend frameworks can consume. The standard for modern full-stack Python development.
Deploy on VPS/cloud, add HTTPS, CI/CD. You have built, tested, and shipped a complete web application. You are a full-stack developer! 🎓
input(), and print().
Just install Python and you are ready to go.
cars = []) which lives in your computer's
RAM (temporary memory). When the program closes, RAM is cleared.
To persist data, you need either a JSON file (beginner approach) or a database like SQLite or MySQL
(the professional approach). This is covered in our next course modules.
POST, Read = GET, Update = PUT/PATCH,
Delete = DELETE. Our command-line app delivers the same CRUD operations
via keyboard input instead of HTTP. Same logic, different delivery method!