add postgres example instructions

This commit is contained in:
Timur Gordon 2025-08-02 11:19:57 +02:00
parent 0f3ea0fe2f
commit 0cf66642a2
3 changed files with 659 additions and 0 deletions

View File

@ -0,0 +1,73 @@
# PostgreSQL Model Example
This example demonstrates the Hero Models framework's PostgreSQL integration capabilities, showcasing how to create, store, retrieve, and manage models in a PostgreSQL database.
## Quick Setup
**Automated Setup (Recommended):**
```bash
./setup.sh
```
The setup script will automatically:
- Detect your operating system (macOS, Ubuntu/Debian, CentOS/RHEL)
- Install PostgreSQL if not already installed
- Start the PostgreSQL service
- Create the required database user with password
- Test the database connection
- Configure PATH (macOS only)
**Manual Setup:**
If you prefer to set up PostgreSQL manually, the example expects:
- PostgreSQL server running on `localhost:5432`
- Username: `postgres`
- Password: `test123`
## What This Example Demonstrates
### Core Features
1. **Model Creation** - Creating User and Comment models with the Hero Models framework
2. **Database Operations** - Storing, retrieving, updating, and deleting records
3. **Indexing** - Using username and active status indexes for efficient queries
4. **Relationships** - Associating comments with users
5. **Connection Management** - PostgreSQL connection pooling and configuration
### Specific Operations
- **User Management**: Create users with different attributes (username, email, active status)
- **Index Queries**: Retrieve users by username and filter by active status
- **Data Deletion**: Remove users and see the impact on queries
- **Comment System**: Create comments and associate them with users
- **Model Introspection**: Display model information and database prefixes
## Running the Example
From the heromodels root directory:
```bash
cargo run --example postgres_model_example
```
## Expected Output
The example will:
1. Create 4 sample users with different attributes
2. Display all users retrieved from the database
3. Demonstrate index-based queries (by username and active status)
4. Delete a user and show the updated results
5. Create and associate comments with users
6. Display model metadata information
## Code Structure
- **Database Configuration**: Sets up PostgreSQL connection with credentials
- **Model Creation**: Uses fluent builder pattern for creating User and Comment instances
- **Database Operations**: Demonstrates CRUD operations using the Hero Models API
- **Index Usage**: Shows how to query using predefined indexes
- **Error Handling**: Proper error handling for database operations
## Key Technologies
- **Hero Models Framework**: Core ORM-like functionality
- **PostgreSQL**: Database backend with connection pooling
- **Rust**: Type-safe model definitions and operations
- **Serde**: JSON serialization/deserialization for database storage

View File

@ -0,0 +1,249 @@
use heromodels::db::postgres::Config;
use heromodels::db::{Collection, Db};
use heromodels::models::userexample::user::user_index::{is_active, username};
use heromodels::models::{Comment, User};
use heromodels_core::Model;
// Helper function to print user details
fn print_user_details(user: &User) {
println!("\n--- User Details ---");
println!("ID: {}", user.get_id());
println!("Username: {}", user.username);
println!("Email: {}", user.email);
println!("Full Name: {}", user.full_name);
println!("Active: {}", user.is_active);
println!("Created At: {}", user.base_data.created_at);
println!("Modified At: {}", user.base_data.modified_at);
println!("Comments: {:?}", user.base_data.comments);
}
// Helper function to print comment details
fn print_comment_details(comment: &Comment) {
println!("\n--- Comment Details ---");
println!("ID: {}", comment.get_id());
println!("User ID: {}", comment.user_id);
println!("Content: {}", comment.content);
println!("Created At: {}", comment.base_data.created_at);
println!("Modified At: {}", comment.base_data.modified_at);
}
fn main() {
let db = heromodels::db::postgres::Postgres::new(
Config::new()
.user(Some("postgres".into()))
.password(Some("test123".into()))
.host(Some("localhost".into()))
.port(Some(5432)),
)
.expect("Can connect to postgress");
println!("Hero Models - Basic Usage Example");
println!("================================");
// Clean up any existing data to ensure consistent results
println!("Cleaning up existing data...");
let user_collection = db.collection::<User>().expect("can open user collection");
let comment_collection = db.collection::<Comment>().expect("can open comment collection");
// Clear all existing users and comments
if let Ok(existing_users) = user_collection.get_all() {
for user in existing_users {
let _ = user_collection.delete_by_id(user.get_id());
}
}
if let Ok(existing_comments) = comment_collection.get_all() {
for comment in existing_comments {
let _ = comment_collection.delete_by_id(comment.get_id());
}
}
println!("Database cleaned.\n");
// Create users with auto-generated IDs
// User 1
let user1 = User::new()
.username("johndoe")
.email("john.doe@example.com")
.full_name("John Doe")
.is_active(false)
.build();
// User 2
let user2 = User::new()
.username("janesmith")
.email("jane.smith@example.com")
.full_name("Jane Smith")
.is_active(true)
.build();
// User 3
let user3 = User::new()
.username("willism")
.email("willis.masters@example.com")
.full_name("Willis Masters")
.is_active(true)
.build();
// User 4
let user4 = User::new()
.username("carrols")
.email("carrol.smith@example.com")
.full_name("Carrol Smith")
.is_active(false)
.build();
// Save all users to database and get their assigned IDs and updated models
let (user1_id, db_user1) = db
.collection()
.expect("can open user collection")
.set(&user1)
.expect("can set user");
let (user2_id, db_user2) = db
.collection()
.expect("can open user collection")
.set(&user2)
.expect("can set user");
let (user3_id, db_user3) = db
.collection()
.expect("can open user collection")
.set(&user3)
.expect("can set user");
let (user4_id, db_user4) = db
.collection()
.expect("can open user collection")
.set(&user4)
.expect("can set user");
println!("User 1 assigned ID: {user1_id}");
println!("User 2 assigned ID: {user2_id}");
println!("User 3 assigned ID: {user3_id}");
println!("User 4 assigned ID: {user4_id}");
// We already have the updated models from the set method, so we don't need to retrieve them again
// Print all users retrieved from database
println!("\n--- Users Retrieved from Database ---");
println!("\n1. First user:");
print_user_details(&db_user1);
println!("\n2. Second user:");
print_user_details(&db_user2);
println!("\n3. Third user:");
print_user_details(&db_user3);
println!("\n4. Fourth user:");
print_user_details(&db_user4);
// Demonstrate different ways to retrieve users from the database
// 1. Retrieve by username index
println!("\n--- Retrieving Users by Different Methods ---");
println!("\n1. By Username Index:");
let stored_users = db
.collection::<User>()
.expect("can open user collection")
.get::<username, _>("johndoe")
.expect("can load stored user");
assert_eq!(stored_users.len(), 1);
print_user_details(&stored_users[0]);
// 2. Retrieve by active status
println!("\n2. By Active Status (Active = true):");
let active_users = db
.collection::<User>()
.expect("can open user collection")
.get::<is_active, _>(&true)
.expect("can load stored users");
assert_eq!(active_users.len(), 2);
for active_user in active_users.iter() {
print_user_details(active_user);
}
// 3. Delete a user and show the updated results
println!("\n3. After Deleting a User:");
let user_to_delete_id = active_users[0].get_id();
println!("Deleting user with ID: {user_to_delete_id}");
db.collection::<User>()
.expect("can open user collection")
.delete_by_id(user_to_delete_id)
.expect("can delete existing user");
// Show remaining active users
let active_users = db
.collection::<User>()
.expect("can open user collection")
.get::<is_active, _>(&true)
.expect("can load stored users");
println!(" a. Remaining Active Users:");
assert_eq!(active_users.len(), 1);
for active_user in active_users.iter() {
print_user_details(active_user);
}
// Show inactive users
let inactive_users = db
.collection::<User>()
.expect("can open user collection")
.get::<is_active, _>(&false)
.expect("can load stored users");
println!(" b. Inactive Users:");
assert_eq!(inactive_users.len(), 2);
for inactive_user in inactive_users.iter() {
print_user_details(inactive_user);
}
// Delete a user based on an index for good measure
db.collection::<User>()
.expect("can open user collection")
.delete::<username, _>("janesmith")
.expect("can delete existing user");
println!("\n--- User Model Information ---");
println!("User DB Prefix: {}", User::db_prefix());
// Demonstrate comment creation and association with a user
println!("\n--- Working with Comments ---");
// 1. Create and save a comment
println!("\n1. Creating a Comment:");
let comment = Comment::new()
.user_id(db_user1.get_id()) // commenter's user ID
.content("This is a comment on the user")
.build();
// Save the comment and get its assigned ID and updated model
let (comment_id, db_comment) = db
.collection()
.expect("can open comment collection")
.set(&comment)
.expect("can set comment");
println!("Comment assigned ID: {comment_id}");
println!(" a. Comment Retrieved from Database:");
print_comment_details(&db_comment);
// 3. Associate the comment with a user
println!("\n2. Associating Comment with User:");
let mut updated_user = db_user1.clone();
updated_user.base_data.add_comment(db_comment.get_id());
// Save the updated user and get the new version
let (_, user_with_comment) = db
.collection::<User>()
.expect("can open user collection")
.set(&updated_user)
.expect("can set updated user");
println!(" a. User with Associated Comment:");
print_user_details(&user_with_comment);
println!("\n--- Model Information ---");
println!("User DB Prefix: {}", User::db_prefix());
println!("Comment DB Prefix: {}", Comment::db_prefix());
}

View File

@ -0,0 +1,337 @@
#!/bin/bash
# PostgreSQL Setup Script for Hero Models Example
# This script checks for PostgreSQL installation and sets up the required database configuration
set -e # Exit on any error
echo "🚀 Hero Models PostgreSQL Example Setup"
echo "========================================"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to print colored output
print_status() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Detect operating system
detect_os() {
if [[ "$OSTYPE" == "darwin"* ]]; then
echo "macos"
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
if command -v apt-get &> /dev/null; then
echo "ubuntu"
elif command -v yum &> /dev/null; then
echo "centos"
else
echo "linux"
fi
else
echo "unknown"
fi
}
# Check if PostgreSQL is installed
check_postgres_installed() {
# Check if PostgreSQL is in current PATH
if command -v postgres &> /dev/null || command -v psql &> /dev/null; then
return 0
fi
# Check macOS Homebrew installation location
if [[ $(detect_os) == "macos" ]]; then
if [[ -f "/opt/homebrew/opt/postgresql@15/bin/postgres" ]] || [[ -f "/opt/homebrew/opt/postgresql@15/bin/psql" ]]; then
return 0
fi
# Also check Intel Mac location
if [[ -f "/usr/local/opt/postgresql@15/bin/postgres" ]] || [[ -f "/usr/local/opt/postgresql@15/bin/psql" ]]; then
return 0
fi
fi
return 1
}
# Install PostgreSQL based on OS
install_postgres() {
local os=$(detect_os)
case $os in
"macos")
print_status "Installing PostgreSQL on macOS using Homebrew..."
if ! command -v brew &> /dev/null; then
print_error "Homebrew is not installed. Please install Homebrew first:"
echo " /bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""
exit 1
fi
brew install postgresql@15
print_success "PostgreSQL installed successfully"
;;
"ubuntu")
print_status "Installing PostgreSQL on Ubuntu/Debian..."
sudo apt-get update
sudo apt-get install -y postgresql postgresql-contrib
print_success "PostgreSQL installed successfully"
;;
"centos")
print_status "Installing PostgreSQL on CentOS/RHEL..."
sudo yum install -y postgresql-server postgresql-contrib
sudo postgresql-setup initdb
print_success "PostgreSQL installed successfully"
;;
*)
print_error "Unsupported operating system: $os"
print_error "Please install PostgreSQL manually and run this script again"
exit 1
;;
esac
}
# Start PostgreSQL service
start_postgres() {
local os=$(detect_os)
case $os in
"macos")
print_status "Starting PostgreSQL service on macOS..."
brew services start postgresql@15
# Add PostgreSQL to PATH for this session
export PATH="/opt/homebrew/opt/postgresql@15/bin:$PATH"
print_success "PostgreSQL service started"
;;
"ubuntu")
print_status "Starting PostgreSQL service on Ubuntu/Debian..."
sudo systemctl start postgresql
sudo systemctl enable postgresql
print_success "PostgreSQL service started and enabled"
;;
"centos")
print_status "Starting PostgreSQL service on CentOS/RHEL..."
sudo systemctl start postgresql
sudo systemctl enable postgresql
print_success "PostgreSQL service started and enabled"
;;
esac
}
# Check if PostgreSQL is running
check_postgres_running() {
local os=$(detect_os)
# Ensure PostgreSQL binaries are in PATH for macOS
if [[ $os == "macos" ]]; then
export PATH="/opt/homebrew/opt/postgresql@15/bin:$PATH"
fi
if pg_isready -h localhost -p 5432 &> /dev/null; then
return 0
else
return 1
fi
}
# Setup database user and password
setup_database() {
print_status "Setting up database user and password..."
local os=$(detect_os)
# Ensure PostgreSQL binaries are in PATH for macOS
if [[ $os == "macos" ]]; then
export PATH="/opt/homebrew/opt/postgresql@15/bin:$PATH"
fi
# Create postgres user if it doesn't exist (mainly for macOS)
if ! psql -U postgres -c '\q' &> /dev/null; then
print_status "Creating postgres user..."
if [[ $os == "macos" ]]; then
createuser -s postgres 2>/dev/null || true
else
sudo -u postgres createuser -s postgres 2>/dev/null || true
fi
fi
# Set password for postgres user
print_status "Setting password for postgres user..."
if [[ $os == "macos" ]]; then
psql -U postgres -c "ALTER USER postgres PASSWORD 'test123';" 2>/dev/null || {
print_warning "Could not set password directly. Trying alternative method..."
createdb -U postgres postgres 2>/dev/null || true
psql -U postgres -d postgres -c "ALTER USER postgres PASSWORD 'test123';" || {
print_error "Failed to set password. You may need to set it manually:"
echo " psql -U postgres -c \"ALTER USER postgres PASSWORD 'test123';\""
return 1
}
}
else
sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'test123';" || {
print_error "Failed to set password. You may need to set it manually:"
echo " sudo -u postgres psql -c \"ALTER USER postgres PASSWORD 'test123';\""
return 1
}
fi
print_success "Database user configured successfully"
}
# Test database connection
test_connection() {
print_status "Testing database connection..."
local os=$(detect_os)
if [[ $os == "macos" ]]; then
export PATH="/opt/homebrew/opt/postgresql@15/bin:$PATH"
fi
if PGPASSWORD=test123 psql -h localhost -p 5432 -U postgres -d postgres -c "SELECT version();" &> /dev/null; then
print_success "Database connection test successful!"
return 0
else
print_error "Database connection test failed"
return 1
fi
}
# Add PATH export to shell profile (macOS only)
setup_path_macos() {
if [[ $(detect_os) == "macos" ]]; then
local shell_profile=""
if [[ $SHELL == *"zsh"* ]]; then
shell_profile="$HOME/.zshrc"
elif [[ $SHELL == *"bash"* ]]; then
shell_profile="$HOME/.bash_profile"
fi
if [[ -n $shell_profile ]]; then
local path_export='export PATH="/opt/homebrew/opt/postgresql@15/bin:$PATH"'
if ! grep -q "$path_export" "$shell_profile" 2>/dev/null; then
print_status "Adding PostgreSQL to PATH in $shell_profile..."
echo "" >> "$shell_profile"
echo "# PostgreSQL" >> "$shell_profile"
echo "$path_export" >> "$shell_profile"
print_success "PostgreSQL added to PATH. Restart your terminal or run: source $shell_profile"
fi
fi
fi
}
# Prompt user for installation
prompt_install() {
echo ""
print_warning "PostgreSQL is not installed on your system."
echo ""
print_status "The Hero Models example requires PostgreSQL to be installed and configured."
echo ""
echo "Options:"
echo " 1. Let this script install PostgreSQL automatically"
echo " 2. Install PostgreSQL manually and run this script again"
echo ""
read -p "Would you like this script to install PostgreSQL for you? (y/n): " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
return 0 # User wants automatic installation
else
return 1 # User wants manual installation
fi
}
# Main setup process
main() {
echo ""
# Check if PostgreSQL is already installed
if check_postgres_installed; then
print_success "PostgreSQL is already installed"
else
if prompt_install; then
print_status "Installing PostgreSQL..."
install_postgres
else
print_status "Please install PostgreSQL manually and run this script again."
echo ""
print_status "Manual installation instructions:"
local os=$(detect_os)
case $os in
"macos")
echo " brew install postgresql@15"
echo " brew services start postgresql@15"
;;
"ubuntu")
echo " sudo apt-get update"
echo " sudo apt-get install postgresql postgresql-contrib"
echo " sudo systemctl start postgresql"
;;
"centos")
echo " sudo yum install postgresql-server postgresql-contrib"
echo " sudo postgresql-setup initdb"
echo " sudo systemctl start postgresql"
;;
*)
echo " Please install PostgreSQL using your system's package manager"
;;
esac
echo ""
exit 0
fi
fi
# Start PostgreSQL service
print_status "Checking PostgreSQL service status..."
if ! check_postgres_running; then
print_status "PostgreSQL is not running. Starting service..."
start_postgres
sleep 2 # Give service time to start
else
print_success "PostgreSQL service is already running"
fi
# Setup database user and password
setup_database
# Test the connection
if test_connection; then
print_success "Setup completed successfully!"
else
print_error "Setup completed but connection test failed"
print_error "You may need to manually configure the database"
exit 1
fi
# Setup PATH for macOS
setup_path_macos
echo ""
print_success "🎉 PostgreSQL setup complete!"
echo ""
print_status "You can now run the example with:"
echo " cargo run --example postgres_model_example"
echo ""
print_status "Database configuration:"
echo " Host: localhost"
echo " Port: 5432"
echo " Username: postgres"
echo " Password: test123"
echo ""
}
# Run main function
main "$@"