Automated Database Populator
Python utility for generating coherent fake books, users, profiles, and cover images for Django development.
Python · Django · Faker · Pillow
README.md
# Automated Database Populator
**Video Demo:** https://youtu.be/enrY6JeYuJE
## Description
Automated Database Populator Python-based tool designed to automatically generate and populate data for a Django Book Management System. This project was developed as part of the CS50 Python course. My goal was to demonstrate the ability to automate tasks in a web-based application, also created by myself.
## Project Files
- `db_populator.py`: It contains functions to connect to the Django database, create genres, books, users, and their profiles. Each function is responsible for generating random but coherent data to populate the database.
- `connect_to_db()`: Sets up the connection to the Django database.
- `create_genres()`: Populates the database with a predefined list of book genres.
- `create_books()`: Generates and adds books with randomly generated titles, authors, and other relevant details.
- `create_users()`: Creates user accounts along with their profiles, including a default profile picture and a personalized bio.
-`README.md`file: This readme file
- requirements.txt
## Design Choices
One of the critical design decisions in this project was choosing to generate random data for books and users instead of scraping content from external sources. This approach was taken to avoid any issues related to the collection or generation of sensitive data, ensuring the project remains ethical and privacy-focused.
The project uses the Faker library to create realistic names, titles, and other text-based data. For image generation, the Pillow library is used to create unique book covers with random shapes and colors. This approach provides a diverse range of data, making the book management system feel more authentic for demonstration purposes.
## User and Profile Generation
The `create_users` function also associates each account with a profile. The profile includes a preset bio template that incorporates the user's username to personalize the experience. Additionally, a default profile picture is assigned to each profile, ensuring that every account has a complete set of data.
The Automated Database Populator uses scripting to automate the process of data generation in web applications. It's a practical tool for quickly setting up a rich dataset for a book management system.
db_populator.py
import sys
sys.path.append('/code')
import os
import django
from faker import Faker
from PIL import Image, ImageDraw, ImageFont
import random
import secrets
def connect_to_db():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'library_demo_project.settings')
django.setup()
def create_genres():
from library_demo_app.models import Genre # Import after Django setup
genres = ["Fantasy", "Science Fiction", "Mystery", "Thriller", "Romance", "Non-Fiction", "Test Genre"]
for genre_name in genres:
Genre.objects.get_or_create(name=genre_name)
def generate_linear_gradient(start_color, end_color, width, height):
"""Create a linear gradient from start_color to end_color."""
base = Image.new('RGB', (width, height), start_color)
top = Image.new('RGB', (width, height), end_color)
mask = Image.new('L', (width, height))
mask_data = []
for y in range(height):
mask_data.extend([int(255 * (y / height))] * width)
mask.putdata(mask_data)
base.paste(top, (0, 0), mask)
return base
def generate_cover_image(title, media_directory):
width, height = 320, 500
start_color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
end_color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
img = generate_linear_gradient(start_color, end_color, width, height)
safe_title = title.replace('/', '_')
d = ImageDraw.Draw(img)
font = ImageFont.load_default()
# Add random shapes
for _ in range(random.randint(1, 5)): # Random number of shapes
shape_type = random.choice(['rectangle', 'ellipse', 'triangle', 'square'])
x0 = random.randint(0, width)
y0 = random.randint(0, height)
x1 = random.randint(x0, width)
y1 = random.randint(y0, height)
shape_color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
if shape_type == 'rectangle':
d.rectangle([x0, y0, x1, y1], outline=shape_color, fill=None)
elif shape_type == 'ellipse':
d.ellipse([x0, y0, x1, y1], outline=shape_color, fill=None)
elif shape_type == 'triangle':
d.polygon([x0, y1, (x0 + x1) // 2, y0, x1, y1], outline=shape_color, fill=None)
elif shape_type == 'square':
size = min(x1 - x0, y1 - y0)
d.rectangle([x0, y0, x0 + size, y0 + size], outline=shape_color, fill=None)
max_chars = 25 # Adjust as needed
displayed_title = title if len(title) <= max_chars else title[:max_chars-3] + '...'
d.text((10, 50), displayed_title, fill=(255, 255, 255), font=font)
# Custom message
d.text((10, 150), "Generated by BookTracker Populator", fill=(255, 255, 255), font=font)
if not media_directory.endswith('/'):
media_directory += '/'
filename = f"{title.replace(' ', '_').replace('/', '_')}.png"
img_path = os.path.join(media_directory, 'covers', filename)
img.save(img_path)
return 'covers/' + filename
def create_books():
from library_demo_app.models import Book, Genre
faker = Faker()
number_of_books = 30 # Any number of books to be generated
media_directory = ('/code/media')
all_genres = list(Genre.objects.all())
random.shuffle(all_genres)
for _ in range(number_of_books):
title = faker.catch_phrase().title()
author = faker.name()
isbn = faker.isbn13().replace('-', '') # Remove dashes from the ISBN
pages = random.randint(50, 1000)
publication_date = faker.date_between(start_date='-10y', end_date='today')
summary = faker.text(max_nb_chars=400)
cover_image_filename = generate_cover_image(title, media_directory)
book, created = Book.objects.get_or_create(
title=title,
author=author,
isbn=isbn,
defaults={
'pages': pages,
'publication_date': publication_date,
'summary': summary,
'cover_image': cover_image_filename
}
)
# Randomly assign a few genres
num_genres_to_assign = random.randint(1, 3)
selected_genres = all_genres[:num_genres_to_assign]
for genre in selected_genres:
book.genres.add(genre)
random.shuffle(all_genres)
def create_users(num_users=30):
from django.contrib.auth.models import User
from library_demo_app.models import Profile
faker = Faker()
created_users = []
for _ in range(num_users):
username = faker.unique.user_name()
email = faker.email()
password = secrets.token_urlsafe() # random password
user, created = User.objects.get_or_create(username=username, email=email)
if created:
user.set_password(password)
user.save()
# Create and associate a profile with the user
profile, profile_created = Profile.objects.get_or_create(user=user)
if profile_created:
bio_template = (
f"Hi, I'm {user.username}! 📚 I'm a passionate bookworm with a never-ending love for literature. "
"When I'm not nose-deep in a good book, you can find me sipping on a cup of tea, enjoying the great outdoors, "
"or curled up with my furry reading companion, Whiskers. I'm a firm believer that books have the power to transport "
"us to different worlds and broaden our horizons. My reading journey is a mix of genres, from classic literature to "
"contemporary fiction, and I'm always on the lookout for hidden gems."
)
profile.bio = bio_template
profile_photo_path = 'profile_photos/demo.png'
profile.profile_photo = profile_photo_path
profile.location = "Storyville"
birth_date = faker.date_of_birth(minimum_age=22, maximum_age=52)
profile.birth_date = birth_date
profile.save()
created_users.append(user)
return created_users
def main():
connect_to_db()
create_genres()
create_books()
create_users()
if __name__ == "__main__":
main()
requirements.txt
# Last update pending
imgkit==1.2.3
Pillow==10.2.0 # if using higher version from script
sqlparse==0.5.1 # Higher version from script requirements
urllib3==2.2.1
Faker==22.0.0