A contact book application is a practical tool to store, manage, and organize contact information. In this tutorial, you'll learn how to build a simple yet powerful Contact Management System in Python that uses JSON for persistent storage.
Understanding the Contact Book
Think of a contact book as a digital version of the address books we used to carry around. It's structured to store names, phone numbers, email addresses, and other details. This tutorial will teach you concepts like file handling, data management, and Python programming.
Key Features of the Contact Book Application
- Class-Based Organization: The application is structured using a class, making it modular and reusable.
- Persistent Storage: Contacts are saved to a JSON file, allowing data to persist between application runs.
- Contact Management: Add, view, search, update, and delete contacts.
- Flexible Search and Delete: Search contacts with partial names and delete specific matches.
- Menu-Driven Interaction: Provides a simple and intuitive user interface.
- Structured Data Storage: Stores contact details in a list of dictionaries.
Implementing the Contact Book Application
This section guides you through building the Contact Book in Python. The app's core features—adding, viewing, searching, updating, and deleting contacts—are all accessible via a straightforward menu-driven interface.
Project Setup and Required Libraries
To get started, you'll use Python's json
and os
modules for managing data and handling files. These modules are built into Python, so no additional installation is needed.
import json
import os
Defining the ContactBook
Class
To keep your code organized, you'll structure it around a ContactBook
class. This class will handle all the operations like adding, updating, and deleting contacts, as well as saving and loading data from a file.
class ContactBook:
def __init__(self, filename='contacts.json'):
"""
Initialize the contact book with a filename for storage.
Load existing contacts from the file if available.
"""
self.filename = filename
self.contacts = self.load_contacts()
def load_contacts(self):
"""
Load contacts from the JSON file.
If the file does not exist, return an empty list.
"""
if os.path.exists(self.filename):
try:
with open(self.filename, 'r') as file:
return json.load(file)
except json.JSONDecodeError:
print("Error reading contacts file. Starting with an empty contact book.")
return []
return []
def save_contacts(self):
"""
Save the current contact list to the JSON file.
"""
with open(self.filename, 'w') as file:
json.dump(self.contacts, file, indent=4)
Adding New Contacts
The add_contact
method lets users add new contacts. It checks for duplicate phone numbers and email addresses before adding to ensure data consistency.
def add_contact(self):
"""
Add a new contact to the contact book.
Validates phone and email for duplicates before adding.
"""
name = input("Enter contact name: ")
phone = input("Enter phone number: ")
email = input("Enter email address: ")
# Check for duplicate phone/email
duplicate_error = self.find_duplicate(phone, email)
if duplicate_error:
print(duplicate_error)
return
# Add the new contact
self.contacts.append({'name': name, 'phone': phone, 'email': email})
self.save_contacts()
print(f"Contact '{name}' added successfully!")
Output:
Contact Book Menu:
1. View Contacts
2. Search Contact
3. Add Contact
4. Update Contact
5. Delete Contact
6. Exit
Enter your choice (1-6): 3
Enter contact name: Alex Johnson
Enter phone number: 7778889990
Enter email address: [email protected]
Contact 'Alex Johnson' added successfully!
Viewing Contacts
The view_contacts
method displays all saved contacts in an easy-to-read format. If no contacts are found, let the user know.
def view_contacts(self):
"""
Display all contacts in the contact book.
"""
if not self.contacts:
print("No contacts found.")
else:
for index, contact in enumerate(self.contacts, 1):
print(f"{index}. Name: {contact['name']}, Phone: {contact['phone']}, Email: {contact['email']}")
Output:
Contact Book Menu:
1. View Contacts
2. Search Contact
3. Add Contact
4. Update Contact
5. Delete Contact
6. Exit
Enter your choice (1-6): 1
1. Name: John Doe, Phone: 1234567890, Email: [email protected]
2. Name: Priya Sharma, Phone: 9876543210, Email: [email protected]
3. Name: Alice Johnson, Phone: 5556667777, Email: [email protected]
4. Name: Alex Carter, Phone: 6665554443, Email: [email protected]
5. Name: Bob Williams, Phone: 4443332221, Email: [email protected]
6. Name: Emma Brown, Phone: 8889990001, Email: [email protected]
7. Name: Alex Johnson, Phone: 7778889990, Email: [email protected]
Searching for Contacts
The search_contact
method allows users to search for contacts by name—either full or partial matches.
def search_contact(self, query):
"""
Search for contacts by name.
Displays all matching results.
"""
results = [contact for contact in self.contacts if query.lower() in contact['name'].lower()]
if results:
for contact in results:
print(f"Name: {contact['name']}, Phone: {contact['phone']}, Email: {contact['email']}")
else:
print("No matching contacts found.")
Output:
Contact Book Menu:
1. View Contacts
2. Search Contact
3. Add Contact
4. Update Contact
5. Delete Contact
6. Exit
Enter your choice (1-6): 2
Enter name to search: ale
Name: Alex Carter, Phone: 6665554443, Email: [email protected]
Name: Alex Johnson, Phone: 7778889990, Email: [email protected]
Updating Contact
The update_contact
method lets users search for a contact by name, view matching results, and then select one to update. The method validates the updated phone number and email for duplicates before saving.
def update_contact(self, name):
"""
Update an existing contact by name.
Allows updating phone and email while checking for duplicates.
"""
matches = [contact for contact in self.contacts if contact['name'].lower() == name.lower()]
if not matches:
print(f"No contacts found with the name '{name}'.")
return
# Show options to select contact
print("\nMatching Contacts:")
for index, contact in enumerate(matches, 1):
print(f"{index}. Name: {contact['name']}, Phone: {contact['phone']}, Email: {contact['email']}")
try:
choice = int(input("Select the contact number to update (e.g., 1): ")) - 1
if choice < 0 or choice >= len(matches):
print("Invalid selection.")
return
selected_contact = matches[choice]
except ValueError:
print("Please enter a valid number.")
return
# Collect updated details
new_phone = input(f"Enter new phone (or press Enter to keep '{selected_contact['phone']}'): ") or selected_contact['phone']
new_email = input(f"Enter new email (or press Enter to keep '{selected_contact['email']}'): ") or selected_contact['email']
# Check for duplicates
duplicate_error = self.find_duplicate(new_phone, new_email, exclude_contact=selected_contact)
if duplicate_error:
print(duplicate_error)
return
# Apply updates
selected_contact['phone'] = new_phone
selected_contact['email'] = new_email
self.save_contacts()
print(f"Contact '{selected_contact['name']}' updated successfully!")
Output:
Contact Book Menu:
1. View Contacts
2. Search Contact
3. Add Contact
4. Update Contact
5. Delete Contact
6. Exit
Enter your choice (1-6): 4
Enter name to update: Alex Johnson
Matching Contacts:
1. Name: Alex Johnson, Phone: 7778889990, Email: [email protected]
Select the contact number to update (e.g., 1): 1
Enter new phone (or press Enter to keep '7778889990'): 0123456789
Enter new email (or press Enter to keep '[email protected]'):
Contact 'Alex Johnson' updated successfully!
Deleting Contacts
The delete_contact
method lets users search for a contact by name, display matching results, and then select the specific contact to delete. This is particularly useful when multiple contacts have similar names, as the method first lists all matching entries, allowing the user to choose the exact contact to remove.
def delete_contact(self, query):
"""
Delete a contact by name.
Displays matching contacts and allows selection for deletion.
"""
results = [contact for contact in self.contacts if query.lower() in contact['name'].lower()]
if not results:
print("No matching contacts found.")
return
print("\nMatching Contacts:")
for index, contact in enumerate(results, 1):
print(f"{index}. {contact['name']} - {contact['phone']}")
try:
choice = int(input("Enter the number of the contact to delete: ")) - 1
if 0 <= choice < len(results):
contact_to_delete = results[choice]
self.contacts.remove(contact_to_delete)
self.save_contacts()
print(f"Contact {contact_to_delete['name']} deleted successfully!")
else:
print("Invalid selection.")
except ValueError:
print("Please enter a valid number.")
Output:
Contact Book Menu:
1. View Contacts
2. Search Contact
3. Add Contact
4. Update Contact
5. Delete Contact
6. Exit
Enter your choice (1-6): 5
Enter name to delete: bob
Matching Contacts:
1. Bob Williams - 4443332221
Enter the number of the contact to delete: 1
Contact Bob Williams deleted successfully!
Main Menu for User Interaction
The main
method serves as the user-friendly interface for the contact book application. It provides a menu-driven system for viewing, searching, adding, updating, and deleting contacts. Users can select operations by entering the corresponding menu option.
def main():
"""
Main menu loop for the contact book.
Provides options for viewing, searching, adding, updating, and deleting contacts.
"""
contact_book = ContactBook()
while True:
print("\nContact Book Menu:")
print("1. View Contacts")
print("2. Search Contact")
print("3. Add Contact")
print("4. Update Contact")
print("5. Delete Contact")
print("6. Exit")
choice = input("Enter your choice (1-6): ")
if choice == '1':
contact_book.view_contacts()
elif choice == '2':
query = input("Enter name to search: ")
contact_book.search_contact(query)
elif choice == '3':
contact_book.add_contact()
elif choice == '4':
name = input("Enter name to update: ")
contact_book.update_contact(name)
elif choice == '5':
query = input("Enter name to delete: ")
contact_book.delete_contact(query)
elif choice == '6':
print("Exiting Contact Book. Goodbye!")
break
else:
print("Invalid choice. Try again.")
if __name__ == "__main__":
main()
Output:
Contact Book Menu:
1. View Contacts
2. Search Contact
3. Add Contact
4. Update Contact
5. Delete Contact
6. Exit
Enter your choice (1-6):
Full Featured Application Code
Below is the complete implementation of the contact book application:
import json
import os
class ContactBook:
def __init__(self, filename='contacts.json'):
"""
Initialize the contact book with a filename for storage.
Load existing contacts from the file if available.
"""
self.filename = filename
self.contacts = self.load_contacts()
def load_contacts(self):
"""
Load contacts from the JSON file.
If the file does not exist, return an empty list.
"""
if os.path.exists(self.filename):
try:
with open(self.filename, 'r') as file:
return json.load(file)
except json.JSONDecodeError:
print("Error reading contacts file. Starting with an empty contact book.")
return []
return []
def save_contacts(self):
"""
Save the current contact list to the JSON file.
"""
with open(self.filename, 'w') as file:
json.dump(self.contacts, file, indent=4)
def find_duplicate(self, phone, email, exclude_contact=None):
"""
Check for duplicate phone numbers and email addresses in the contact list.
Exclude a specific contact when checking (used during updates).
"""
duplicate_messages = []
for contact in self.contacts:
if contact == exclude_contact:
continue # Skip the contact being updated
if contact['phone'] == phone:
duplicate_messages.append(f"Phone number '{phone}' already exists.")
if contact['email'] == email:
duplicate_messages.append(f"Email '{email}' already exists.")
# Return combined message if there are duplicates
return "\n".join(duplicate_messages) if duplicate_messages else None
def view_contacts(self):
"""
Display all contacts in the contact book.
"""
if not self.contacts:
print("No contacts found.")
else:
for index, contact in enumerate(self.contacts, 1):
print(f"{index}. Name: {contact['name']}, Phone: {contact['phone']}, Email: {contact['email']}")
def search_contact(self, query):
"""
Search for contacts by name.
Displays all matching results.
"""
results = [contact for contact in self.contacts if query.lower() in contact['name'].lower()]
if results:
for contact in results:
print(f"Name: {contact['name']}, Phone: {contact['phone']}, Email: {contact['email']}")
else:
print("No matching contacts found.")
def add_contact(self):
"""
Add a new contact to the contact book.
Validates phone and email for duplicates before adding.
"""
name = input("Enter contact name: ")
phone = input("Enter phone number: ")
email = input("Enter email address: ")
# Check for duplicate phone/email
duplicate_error = self.find_duplicate(phone, email)
if duplicate_error:
print(duplicate_error)
return
# Add the new contact
self.contacts.append({'name': name, 'phone': phone, 'email': email})
self.save_contacts()
print(f"Contact '{name}' added successfully!")
def update_contact(self, name):
"""
Update an existing contact by name.
Allows updating phone and email while checking for duplicates.
"""
matches = [contact for contact in self.contacts if contact['name'].lower() == name.lower()]
if not matches:
print(f"No contacts found with the name '{name}'.")
return
# Show options to select contact
print("\nMatching Contacts:")
for index, contact in enumerate(matches, 1):
print(f"{index}. Name: {contact['name']}, Phone: {contact['phone']}, Email: {contact['email']}")
try:
choice = int(input("Select the contact number to update (e.g., 1): ")) - 1
if choice < 0 or choice >= len(matches):
print("Invalid selection.")
return
selected_contact = matches[choice]
except ValueError:
print("Please enter a valid number.")
return
# Collect updated details
new_phone = input(f"Enter new phone (or press Enter to keep '{selected_contact['phone']}'): ") or selected_contact['phone']
new_email = input(f"Enter new email (or press Enter to keep '{selected_contact['email']}'): ") or selected_contact['email']
# Check for duplicates
duplicate_error = self.find_duplicate(new_phone, new_email, exclude_contact=selected_contact)
if duplicate_error:
print(duplicate_error)
return
# Apply updates
selected_contact['phone'] = new_phone
selected_contact['email'] = new_email
self.save_contacts()
print(f"Contact '{selected_contact['name']}' updated successfully!")
def delete_contact(self, query):
"""
Delete a contact by name.
Displays matching contacts and allows selection for deletion.
"""
results = [contact for contact in self.contacts if query.lower() in contact['name'].lower()]
if not results:
print("No matching contacts found.")
return
print("\nMatching Contacts:")
for index, contact in enumerate(results, 1):
print(f"{index}. {contact['name']} - {contact['phone']}")
try:
choice = int(input("Enter the number of the contact to delete: ")) - 1
if 0 <= choice < len(results):
contact_to_delete = results[choice]
self.contacts.remove(contact_to_delete)
self.save_contacts()
print(f"Contact {contact_to_delete['name']} deleted successfully!")
else:
print("Invalid selection.")
except ValueError:
print("Please enter a valid number.")
def main():
"""
Main menu loop for the contact book.
Provides options for viewing, searching, adding, updating, and deleting contacts.
"""
contact_book = ContactBook()
while True:
print("\nContact Book Menu:")
print("1. View Contacts")
print("2. Search Contact")
print("3. Add Contact")
print("4. Update Contact")
print("5. Delete Contact")
print("6. Exit")
choice = input("Enter your choice (1-6): ")
if choice == '1':
contact_book.view_contacts()
elif choice == '2':
query = input("Enter name to search: ")
contact_book.search_contact(query)
elif choice == '3':
contact_book.add_contact()
elif choice == '4':
name = input("Enter name to update: ")
contact_book.update_contact(name)
elif choice == '5':
query = input("Enter name to delete: ")
contact_book.delete_contact(query)
elif choice == '6':
print("Exiting Contact Book. Goodbye!")
break
else:
print("Invalid choice. Try again.")
if __name__ == "__main__":
main()
Conclusion
This tutorial demonstrated how to build a contact management system in Python using JSON for persistent storage. By completing this project, you learned about Python classes, file handling, and CRUD operations. Customize this application to explore additional features like advanced search filters or a graphical user interface.