Comandos y Deployment
Management Commands
Sync Stripe Products
# managementApp/management/commands/sync_stripe_products.py
from django.core.management.base import BaseCommand
import stripe
from django.conf import settings
class Command(BaseCommand):
help = 'Synchronize Stripe products with local database'
def handle(self, *args, **options):
stripe.api_key = settings.STRIPE_SECRET_KEY
try:
# Get products from Stripe
products = stripe.Product.list(active=True)
self.stdout.write(f"Found {len(products.data)} active products in Stripe")
for product in products.data:
# Sync logic here
self.stdout.write(f"Syncing product: {product.name}")
# Get prices for this product
prices = stripe.Price.list(product=product.id, active=True)
for price in prices.data:
self.stdout.write(f" - Price: {price.unit_amount} {price.currency}")
self.stdout.write(
self.style.SUCCESS('Successfully synchronized Stripe products')
)
except Exception as e:
self.stdout.write(
self.style.ERROR(f'Error synchronizing products: {e}')
)
Create Admin User
# managementApp/management/commands/create_admin.py
from django.core.management.base import BaseCommand
from managementApp.modelsfolder.UserModel import CustomUser, Profile, UserProfile
class Command(BaseCommand):
help = 'Create admin user and profile'
def add_arguments(self, parser):
parser.add_argument('--email', type=str, help='Admin email')
parser.add_argument('--password', type=str, help='Admin password')
parser.add_argument('--name', type=str, help='Admin name')
def handle(self, *args, **options):
email = options['email']
password = options['password']
name = options['name']
if not all([email, password, name]):
self.stdout.write(
self.style.ERROR('Email, password, and name are required')
)
return
try:
# Create admin user
user = CustomUser.objects.create_user(
username=email,
email=email,
password=password,
first_name=name.split()[0],
last_name=' '.join(name.split()[1:]) if len(name.split()) > 1 else '',
is_staff=True,
is_superuser=True
)
# Create or get admin profile
admin_profile, created = Profile.objects.get_or_create(
profile_name='Admin',
defaults={
'description': 'System Administrator',
'permissions': {
'all': True,
'users': ['create', 'read', 'update', 'delete'],
'properties': ['create', 'read', 'update', 'delete'],
'contracts': ['create', 'read', 'update', 'delete']
}
}
)
# Assign profile to user
UserProfile.objects.create(
user=user,
profile=admin_profile
)
self.stdout.write(
self.style.SUCCESS(f'Admin user {email} created successfully')
)
except Exception as e:
self.stdout.write(
self.style.ERROR(f'Error creating admin user: {e}')
)
Setup Default Data
# managementApp/management/commands/setup_default_data.py
from django.core.management.base import BaseCommand
from managementApp.modelsfolder.UserModel import Profile
from managementApp.modelsfolder.MenuModel import MenuModel
class Command(BaseCommand):
help = 'Setup default profiles and menu items'
def handle(self, *args, **options):
try:
# Create default profiles
profiles_data = [
{
'profile_name': 'Owner',
'description': 'Property Owner',
'permissions': {
'properties': ['create', 'read', 'update', 'delete'],
'contracts': ['create', 'read'],
'payments': ['read']
}
},
{
'profile_name': 'Tenant',
'description': 'Property Tenant',
'permissions': {
'properties': ['read'],
'contracts': ['read'],
'payments': ['create', 'read'],
'requests': ['create', 'read']
}
},
{
'profile_name': 'Agent',
'description': 'Real Estate Agent',
'permissions': {
'properties': ['create', 'read', 'update'],
'contracts': ['create', 'read'],
'users': ['read']
}
}
]
for profile_data in profiles_data:
profile, created = Profile.objects.get_or_create(
profile_name=profile_data['profile_name'],
defaults=profile_data
)
if created:
self.stdout.write(f"Created profile: {profile.profile_name}")
# Create default menu items
menu_items = [
{
'menu_title': 'Dashboard',
'icon': 'home',
'path': '/admin',
'permissions_required': 'Owner,Tenant,Agent,Admin',
'order_index': 1
},
{
'menu_title': 'Properties',
'icon': 'building',
'path': '/admin/properties',
'permissions_required': 'Owner,Agent,Admin',
'order_index': 2
},
{
'menu_title': 'Contracts',
'icon': 'document',
'path': '/admin/contracts',
'permissions_required': 'Owner,Tenant,Agent,Admin',
'order_index': 3
},
{
'menu_title': 'Users',
'icon': 'users',
'path': '/admin/users',
'permissions_required': 'Admin',
'order_index': 4
},
{
'menu_title': 'Payments',
'icon': 'credit-card',
'path': '/admin/payments',
'permissions_required': 'Owner,Tenant,Admin',
'order_index': 5
}
]
for menu_data in menu_items:
menu, created = MenuModel.objects.get_or_create(
menu_title=menu_data['menu_title'],
defaults=menu_data
)
if created:
self.stdout.write(f"Created menu: {menu.menu_title}")
self.stdout.write(
self.style.SUCCESS('Default data setup completed successfully')
)
except Exception as e:
self.stdout.write(
self.style.ERROR(f'Error setting up default data: {e}')
)
Database Management
Migration Commands
# Generate migrations
python manage.py makemigrations
# Apply migrations
python manage.py migrate
# Check migration status
python manage.py showmigrations
# Reset migrations (development only)
python manage.py migrate --fake managementApp zero
rm managementApp/migrations/0*.py
python manage.py makemigrations managementApp
python manage.py migrate
Database Backup
# managementApp/management/commands/backup_db.py
import os
import subprocess
from datetime import datetime
from django.core.management.base import BaseCommand
from django.conf import settings
class Command(BaseCommand):
help = 'Create database backup'
def add_arguments(self, parser):
parser.add_argument('--output-dir', type=str, default='/backups')
def handle(self, *args, **options):
output_dir = options['output_dir']
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
backup_filename = f"alojaplus_backup_{timestamp}.sql"
backup_path = os.path.join(output_dir, backup_filename)
try:
# Ensure output directory exists
os.makedirs(output_dir, exist_ok=True)
# MySQL dump command
db_config = settings.DATABASES['default']
dump_cmd = [
'mysqldump',
'-h', db_config['HOST'],
'-P', db_config['PORT'],
'-u', db_config['USER'],
f"-p{db_config['PASSWORD']}",
'--single-transaction',
'--routines',
'--triggers',
db_config['NAME']
]
with open(backup_path, 'w') as backup_file:
subprocess.run(dump_cmd, stdout=backup_file, check=True)
self.stdout.write(
self.style.SUCCESS(f'Database backup created: {backup_path}')
)
except subprocess.CalledProcessError as e:
self.stdout.write(
self.style.ERROR(f'Error creating backup: {e}')
)
except Exception as e:
self.stdout.write(
self.style.ERROR(f'Unexpected error: {e}')
)
Docker Configuration
Dockerfile
# Dockerfile
FROM python:3.11-slim
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# Set work directory
WORKDIR /app
# Install system dependencies
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
gcc \
default-libmysqlclient-dev \
pkg-config \
&& rm -rf /var/lib/apt/lists/*
# Install Python dependencies
COPY requirements.txt /app/
RUN pip install --no-cache-dir -r requirements.txt
# Copy project
COPY . /app/
# Collect static files
RUN python manage.py collectstatic --noinput
# Create non-root user
RUN adduser --disabled-password --gecos '' appuser
RUN chown -R appuser:appuser /app
USER appuser
# Expose port
EXPOSE 8000
# Run gunicorn
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "managementProject.wsgi:application"]
Docker Compose
# docker-compose.yml
version: '3.8'
services:
db:
image: mysql:8.0
environment:
MYSQL_DATABASE: alojaplus
MYSQL_USER: alojaplus
MYSQL_PASSWORD: password
MYSQL_ROOT_PASSWORD: rootpassword
volumes:
- mysql_data:/var/lib/mysql
ports:
- "3306:3306"
web:
build: .
ports:
- "8000:8000"
environment:
- DB_HOST=db
- DB_NAME=alojaplus
- DB_USER=alojaplus
- DB_PASSWORD=password
- DEBUG_STATUS=False
depends_on:
- db
volumes:
- ./staticfiles:/app/staticfiles
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./staticfiles:/app/staticfiles
depends_on:
- web
volumes:
mysql_data:
Nginx Configuration
# nginx.conf
events {
worker_connections 1024;
}
http {
upstream django {
server web:8000;
}
server {
listen 80;
server_name localhost;
location /static/ {
alias /app/staticfiles/;
}
location / {
proxy_pass http://django;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
Production Deployment
Environment Variables
# .env.production
DEBUG_STATUS=False
SECRET_KEY=your-secret-key-here
# Database
DB_HOST=your-db-host
DB_NAME=alojaplus
DB_USER=alojaplus
DB_PASSWORD=your-secure-password
DB_PORT=3306
# AWS
AWS_ACCESS_KEY=your-aws-access-key
AWS_SECRET_ACCESS_KEY=your-aws-secret-key
AWS_S3_BUCKET_NAME=alojaplus-storage
AWS_REGION_NAME=us-east-1
# Stripe
STRIPE_SECRET_KEY=sk_live_...
STRIPE_PUBLISHABLE_KEY=pk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
# JWT
JWT_AUTH_SECRET_KEY=your-jwt-secret
JWT_AUTH_VALID_FOR=36000
# Email
DEFAULT_FROM_EMAIL=noreply@alojaplus.com
Gunicorn Configuration
# gunicorn.conf.py
bind = "0.0.0.0:8000"
workers = 4
worker_class = "sync"
worker_connections = 1000
max_requests = 1000
max_requests_jitter = 50
preload_app = True
timeout = 30
keepalive = 2
# Logging
accesslog = "/var/log/gunicorn/access.log"
errorlog = "/var/log/gunicorn/error.log"
loglevel = "info"
Deployment Script
#!/bin/bash
# deploy.sh
set -e
echo "Starting deployment..."
# Pull latest code
git pull origin main
# Build Docker images
docker-compose build
# Run migrations
docker-compose run --rm web python manage.py migrate
# Collect static files
docker-compose run --rm web python manage.py collectstatic --noinput
# Restart services
docker-compose down
docker-compose up -d
# Health check
sleep 10
curl -f http://localhost/health || exit 1
echo "Deployment completed successfully!"
Monitoring and Logging
Health Check Endpoint
# managementApp/viewsfolder/HealthViews.py
from django.http import JsonResponse
from django.db import connection
def health_check(request):
try:
# Check database connection
cursor = connection.cursor()
cursor.execute("SELECT 1")
return JsonResponse({
'status': 'healthy',
'database': 'connected',
'timestamp': timezone.now().isoformat()
})
except Exception as e:
return JsonResponse({
'status': 'unhealthy',
'error': str(e),
'timestamp': timezone.now().isoformat()
}, status=503)
Logging Configuration
# managementProject/settings.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
'style': '{',
},
},
'handlers': {
'file': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'filename': '/var/log/django/django.log',
'maxBytes': 15728640, # 15MB
'backupCount': 10,
'formatter': 'verbose',
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'verbose',
},
},
'loggers': {
'django': {
'handlers': ['file', 'console'],
'level': 'INFO',
'propagate': True,
},
'managementApp': {
'handlers': ['file', 'console'],
'level': 'DEBUG',
'propagate': True,
},
},
}
Scheduled Tasks
# For periodic tasks, consider using Django-Cron or Celery
# Example with django-cron
from django_cron import CronJobBase, Schedule
class SendPaymentReminders(CronJobBase):
RUN_AT_TIMES = ['09:00']
schedule = Schedule(run_at_times=RUN_AT_TIMES)
code = 'managementApp.send_payment_reminders'
def do(self):
# Send payment reminders logic
pass