mirror of
https://github.com/thecyberlearn/hostinger-django-demo.git
synced 2026-08-18 07:52:56 +00:00
Initial commit: Django demo project for VPS deployment
🎉 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
commit
2f3f1ea867
20
.env.example
Normal file
20
.env.example
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Django settings
|
||||||
|
SECRET_KEY=your-super-secret-key-here
|
||||||
|
DEBUG=True
|
||||||
|
ALLOWED_HOSTS=localhost,127.0.0.1,yourdomain.com
|
||||||
|
|
||||||
|
# Database settings (choose one approach)
|
||||||
|
|
||||||
|
# Option 1: Use DATABASE_URL (recommended for production)
|
||||||
|
# DATABASE_URL=postgresql://username:password@hostname:port/database_name
|
||||||
|
|
||||||
|
# Option 2: Use individual PostgreSQL settings
|
||||||
|
# USE_POSTGRES=True
|
||||||
|
# DB_NAME=demo_db
|
||||||
|
# DB_USER=demo_user
|
||||||
|
# DB_PASSWORD=your_password
|
||||||
|
# DB_HOST=localhost
|
||||||
|
# DB_PORT=5432
|
||||||
|
|
||||||
|
# Production security settings
|
||||||
|
SECURE_SSL_REDIRECT=False
|
||||||
42
.gitignore
vendored
Normal file
42
.gitignore
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Django
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
media/
|
||||||
|
staticfiles/
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
.env
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
ehthumbs.db
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Deployment
|
||||||
|
*.pid
|
||||||
|
*.sock
|
||||||
146
DEPLOYMENT_GUIDE.md
Normal file
146
DEPLOYMENT_GUIDE.md
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
# VPS Deployment Guide
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before starting, make sure you have:
|
||||||
|
- [ ] Ubuntu 20.04+ VPS with root/sudo access
|
||||||
|
- [ ] Your VPS IP address
|
||||||
|
- [ ] SSH key or password for VPS access
|
||||||
|
- [ ] Domain name (optional, can use IP address)
|
||||||
|
|
||||||
|
## Step 1: Prepare Local Files
|
||||||
|
|
||||||
|
Clean up your local project:
|
||||||
|
```bash
|
||||||
|
rm -rf venv/
|
||||||
|
rm -f db.sqlite3
|
||||||
|
rm -rf staticfiles/
|
||||||
|
rm -f requirements_full.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 2: Upload to VPS
|
||||||
|
|
||||||
|
### Option A: Using SCP (if you have the files locally)
|
||||||
|
```bash
|
||||||
|
# Replace YOUR_VPS_IP with your actual IP
|
||||||
|
scp -r . root@YOUR_VPS_IP:/home/ubuntu/django-demo
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option B: Using Git (recommended)
|
||||||
|
```bash
|
||||||
|
# On your VPS, clone the repository
|
||||||
|
ssh root@YOUR_VPS_IP
|
||||||
|
cd /home/ubuntu
|
||||||
|
git clone YOUR_REPO_URL django-demo
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 3: Run Deployment Script
|
||||||
|
|
||||||
|
SSH into your VPS and run:
|
||||||
|
```bash
|
||||||
|
ssh root@YOUR_VPS_IP
|
||||||
|
cd /home/ubuntu/django-demo
|
||||||
|
sudo bash deploy/deploy.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 4: Configure Environment
|
||||||
|
|
||||||
|
Edit the `.env` file:
|
||||||
|
```bash
|
||||||
|
sudo nano /home/ubuntu/django-demo/.env
|
||||||
|
```
|
||||||
|
|
||||||
|
Set these values:
|
||||||
|
```env
|
||||||
|
SECRET_KEY=your-super-secret-key-here-make-it-long-and-random
|
||||||
|
DEBUG=False
|
||||||
|
ALLOWED_HOSTS=your-domain.com,www.your-domain.com,YOUR_VPS_IP
|
||||||
|
DATABASE_URL=postgresql://demo_user:your_password@localhost:5432/demo_db
|
||||||
|
SECURE_SSL_REDIRECT=False # Set to True after SSL setup
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 5: Update Domain in Nginx
|
||||||
|
|
||||||
|
Edit nginx configuration:
|
||||||
|
```bash
|
||||||
|
sudo nano /etc/nginx/sites-available/django-demo
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace `yourdomain.com` with your actual domain or IP.
|
||||||
|
|
||||||
|
## Step 6: Restart Services
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl restart django-demo
|
||||||
|
sudo systemctl restart nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 7: Test Deployment
|
||||||
|
|
||||||
|
Check if everything is working:
|
||||||
|
```bash
|
||||||
|
# Check service status
|
||||||
|
sudo systemctl status django-demo
|
||||||
|
sudo systemctl status nginx
|
||||||
|
|
||||||
|
# View logs if there are issues
|
||||||
|
sudo journalctl -u django-demo -f
|
||||||
|
```
|
||||||
|
|
||||||
|
Visit your website: `http://YOUR_VPS_IP` or `http://your-domain.com`
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues:
|
||||||
|
|
||||||
|
1. **Service won't start**:
|
||||||
|
```bash
|
||||||
|
sudo journalctl -u django-demo -f
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Static files not loading**:
|
||||||
|
```bash
|
||||||
|
cd /home/ubuntu/django-demo
|
||||||
|
source venv/bin/activate
|
||||||
|
python manage.py collectstatic --noinput
|
||||||
|
sudo systemctl restart django-demo
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Database errors**:
|
||||||
|
- Check PostgreSQL is running: `sudo systemctl status postgresql`
|
||||||
|
- Verify database settings in `.env`
|
||||||
|
|
||||||
|
4. **Permission errors**:
|
||||||
|
```bash
|
||||||
|
sudo chown -R www-data:www-data /home/ubuntu/django-demo
|
||||||
|
```
|
||||||
|
|
||||||
|
## Optional: SSL Certificate (Let's Encrypt)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install certbot python3-certbot-nginx
|
||||||
|
sudo certbot --nginx -d your-domain.com
|
||||||
|
```
|
||||||
|
|
||||||
|
After SSL setup, update `.env`:
|
||||||
|
```env
|
||||||
|
SECURE_SSL_REDIRECT=True
|
||||||
|
```
|
||||||
|
|
||||||
|
## Management Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Restart Django
|
||||||
|
sudo systemctl restart django-demo
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
sudo journalctl -u django-demo -f
|
||||||
|
|
||||||
|
# Update application
|
||||||
|
sudo bash /home/ubuntu/django-demo/deploy/update.sh
|
||||||
|
|
||||||
|
# Access Django shell
|
||||||
|
cd /home/ubuntu/django-demo
|
||||||
|
source venv/bin/activate
|
||||||
|
python manage.py shell
|
||||||
|
```
|
||||||
262
README.md
Normal file
262
README.md
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
# Django Demo Project
|
||||||
|
|
||||||
|
A production-ready Django application template optimized for VPS hosting deployment. This project serves as a foundation for testing VPS deployment workflows and can be used as a template for future Django projects.
|
||||||
|
|
||||||
|
## ✨ Features
|
||||||
|
|
||||||
|
- **Minimal Dependencies**: Clean, lightweight setup with only essential packages
|
||||||
|
- **Environment Configuration**: Production settings managed via environment variables
|
||||||
|
- **Database Flexible**: Works with SQLite for development and PostgreSQL for production
|
||||||
|
- **Static Files Handling**: Configured with WhiteNoise for efficient static file serving
|
||||||
|
- **Contact Form**: Functional contact form with email validation
|
||||||
|
- **Simple Blog System**: Basic blog functionality with admin interface
|
||||||
|
- **Bootstrap UI**: Responsive design using Bootstrap 5
|
||||||
|
- **Production Security**: Security headers, HTTPS support, and production optimizations
|
||||||
|
- **VPS Deployment**: Complete deployment scripts and configuration files
|
||||||
|
|
||||||
|
## 🛠 Tech Stack
|
||||||
|
|
||||||
|
- **Backend**: Django 4.2.7
|
||||||
|
- **Database**: SQLite (development) / PostgreSQL (production)
|
||||||
|
- **Web Server**: Gunicorn + Nginx
|
||||||
|
- **Frontend**: Bootstrap 5, Vanilla JavaScript
|
||||||
|
- **Static Files**: WhiteNoise
|
||||||
|
- **Configuration**: python-decouple
|
||||||
|
|
||||||
|
## 🚀 Quick Start (Development)
|
||||||
|
|
||||||
|
1. **Clone and setup**:
|
||||||
|
```bash
|
||||||
|
git clone <repository-url>
|
||||||
|
cd hostinger-django-demo
|
||||||
|
python3 -m venv venv
|
||||||
|
source venv/bin/activate # On Windows: venv\Scripts\activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Configure environment**:
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
# Edit .env with your settings
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Initialize database**:
|
||||||
|
```bash
|
||||||
|
python manage.py migrate
|
||||||
|
python manage.py createsuperuser
|
||||||
|
python manage.py collectstatic
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Run development server**:
|
||||||
|
```bash
|
||||||
|
python manage.py runserver
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Access the application**:
|
||||||
|
- Homepage: http://127.0.0.1:8000
|
||||||
|
- Admin: http://127.0.0.1:8000/admin
|
||||||
|
|
||||||
|
## 🌐 VPS Deployment
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- Ubuntu 20.04+ VPS
|
||||||
|
- Root or sudo access
|
||||||
|
- Domain name (optional, can use IP address)
|
||||||
|
|
||||||
|
### Automated Deployment
|
||||||
|
|
||||||
|
1. **Upload project files** to your VPS:
|
||||||
|
```bash
|
||||||
|
scp -r . user@your-vps-ip:/home/ubuntu/django-demo
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Run deployment script**:
|
||||||
|
```bash
|
||||||
|
cd /home/ubuntu/django-demo
|
||||||
|
sudo bash deploy/deploy.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Configure your settings**:
|
||||||
|
- Edit `.env` file with production settings
|
||||||
|
- Update domain in `deploy/nginx.conf`
|
||||||
|
- Restart services: `sudo systemctl restart django-demo nginx`
|
||||||
|
|
||||||
|
### Manual Deployment Steps
|
||||||
|
|
||||||
|
If you prefer manual setup, follow these steps:
|
||||||
|
|
||||||
|
1. **Install system packages**:
|
||||||
|
```bash
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install python3 python3-venv python3-pip nginx postgresql postgresql-contrib
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Setup project**:
|
||||||
|
```bash
|
||||||
|
cd /home/ubuntu/django-demo
|
||||||
|
python3 -m venv venv
|
||||||
|
source venv/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Configure environment**:
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
# Edit .env with production settings
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Setup database and static files**:
|
||||||
|
```bash
|
||||||
|
python manage.py migrate
|
||||||
|
python manage.py collectstatic --noinput
|
||||||
|
python manage.py createsuperuser
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Configure services**:
|
||||||
|
```bash
|
||||||
|
sudo cp deploy/systemd.service /etc/systemd/system/django-demo.service
|
||||||
|
sudo cp deploy/nginx.conf /etc/nginx/sites-available/django-demo
|
||||||
|
sudo ln -s /etc/nginx/sites-available/django-demo /etc/nginx/sites-enabled/
|
||||||
|
sudo systemctl enable django-demo
|
||||||
|
sudo systemctl start django-demo
|
||||||
|
sudo systemctl restart nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚙️ Configuration
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
Create a `.env` file based on `.env.example`:
|
||||||
|
|
||||||
|
```env
|
||||||
|
# Required
|
||||||
|
SECRET_KEY=your-super-secret-key-here
|
||||||
|
DEBUG=False
|
||||||
|
ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com
|
||||||
|
|
||||||
|
# Database (choose one method)
|
||||||
|
# Method 1: DATABASE_URL
|
||||||
|
DATABASE_URL=postgresql://username:password@hostname:port/database_name
|
||||||
|
|
||||||
|
# Method 2: Individual settings
|
||||||
|
USE_POSTGRES=True
|
||||||
|
DB_NAME=demo_db
|
||||||
|
DB_USER=demo_user
|
||||||
|
DB_PASSWORD=your_password
|
||||||
|
DB_HOST=localhost
|
||||||
|
DB_PORT=5432
|
||||||
|
|
||||||
|
# Security
|
||||||
|
SECURE_SSL_REDIRECT=True # Set to True if using HTTPS
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Options
|
||||||
|
|
||||||
|
1. **SQLite (Development)**: Default, no additional setup required
|
||||||
|
2. **PostgreSQL (Production)**: Set `USE_POSTGRES=True` or provide `DATABASE_URL`
|
||||||
|
|
||||||
|
## 🔧 Management Commands
|
||||||
|
|
||||||
|
### Development
|
||||||
|
```bash
|
||||||
|
python manage.py runserver # Start development server
|
||||||
|
python manage.py migrate # Apply database migrations
|
||||||
|
python manage.py createsuperuser # Create admin user
|
||||||
|
python manage.py collectstatic # Collect static files
|
||||||
|
```
|
||||||
|
|
||||||
|
### Production
|
||||||
|
```bash
|
||||||
|
sudo systemctl restart django-demo # Restart Django service
|
||||||
|
sudo systemctl status django-demo # Check service status
|
||||||
|
sudo journalctl -u django-demo -f # View live logs
|
||||||
|
sudo bash deploy/update.sh # Deploy updates
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📁 Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
django-demo/
|
||||||
|
├── core/ # Main Django app
|
||||||
|
│ ├── templates/ # HTML templates
|
||||||
|
│ ├── static/ # CSS, JS files
|
||||||
|
│ ├── models.py # Database models
|
||||||
|
│ ├── views.py # View functions
|
||||||
|
│ ├── forms.py # Django forms
|
||||||
|
│ └── admin.py # Admin configuration
|
||||||
|
├── demo_project/ # Django project settings
|
||||||
|
│ ├── settings.py # Main settings
|
||||||
|
│ ├── urls.py # URL configuration
|
||||||
|
│ └── wsgi.py # WSGI application
|
||||||
|
├── deploy/ # Deployment files
|
||||||
|
│ ├── deploy.sh # Deployment script
|
||||||
|
│ ├── update.sh # Update script
|
||||||
|
│ ├── gunicorn.conf.py # Gunicorn configuration
|
||||||
|
│ ├── nginx.conf # Nginx configuration
|
||||||
|
│ └── systemd.service # Systemd service file
|
||||||
|
├── requirements.txt # Python dependencies
|
||||||
|
├── .env.example # Environment variables template
|
||||||
|
└── README.md # This file
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔍 Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
1. **Static files not loading**:
|
||||||
|
```bash
|
||||||
|
python manage.py collectstatic --noinput
|
||||||
|
sudo systemctl restart django-demo
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Database connection errors**:
|
||||||
|
- Check `.env` file database settings
|
||||||
|
- Ensure PostgreSQL is running: `sudo systemctl status postgresql`
|
||||||
|
|
||||||
|
3. **Permission errors**:
|
||||||
|
```bash
|
||||||
|
sudo chown -R www-data:www-data /home/ubuntu/django-demo
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Service not starting**:
|
||||||
|
```bash
|
||||||
|
sudo journalctl -u django-demo -f # Check logs
|
||||||
|
sudo systemctl status django-demo # Check status
|
||||||
|
```
|
||||||
|
|
||||||
|
### Log Files
|
||||||
|
|
||||||
|
- Django logs: `sudo journalctl -u django-demo -f`
|
||||||
|
- Nginx access: `/var/log/nginx/access.log`
|
||||||
|
- Nginx errors: `/var/log/nginx/error.log`
|
||||||
|
- Custom Django logs: `/var/log/django/`
|
||||||
|
|
||||||
|
## 🔒 Security Considerations
|
||||||
|
|
||||||
|
- Change `SECRET_KEY` in production
|
||||||
|
- Set `DEBUG=False` in production
|
||||||
|
- Use HTTPS in production (`SECURE_SSL_REDIRECT=True`)
|
||||||
|
- Regularly update dependencies
|
||||||
|
- Configure firewall (UFW) properly
|
||||||
|
- Use strong passwords for database users
|
||||||
|
|
||||||
|
## 🚀 Deployment Checklist
|
||||||
|
|
||||||
|
- [ ] Update `.env` with production settings
|
||||||
|
- [ ] Set `DEBUG=False`
|
||||||
|
- [ ] Configure proper `ALLOWED_HOSTS`
|
||||||
|
- [ ] Set strong `SECRET_KEY`
|
||||||
|
- [ ] Configure database settings
|
||||||
|
- [ ] Update domain in nginx configuration
|
||||||
|
- [ ] Set up SSL certificate (recommended)
|
||||||
|
- [ ] Configure firewall rules
|
||||||
|
- [ ] Test all functionality
|
||||||
|
|
||||||
|
## 📝 License
|
||||||
|
|
||||||
|
This project is open source and available under the MIT License.
|
||||||
|
|
||||||
|
## 🤝 Contributing
|
||||||
|
|
||||||
|
This is a demo project, but improvements are welcome! Please feel free to submit issues and pull requests.
|
||||||
0
core/__init__.py
Normal file
0
core/__init__.py
Normal file
21
core/admin.py
Normal file
21
core/admin.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
from .models import Contact, BlogPost
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Contact)
|
||||||
|
class ContactAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ['name', 'email', 'subject', 'created_at']
|
||||||
|
list_filter = ['created_at']
|
||||||
|
search_fields = ['name', 'email', 'subject']
|
||||||
|
readonly_fields = ['created_at']
|
||||||
|
ordering = ['-created_at']
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(BlogPost)
|
||||||
|
class BlogPostAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ['title', 'is_published', 'created_at', 'updated_at']
|
||||||
|
list_filter = ['is_published', 'created_at']
|
||||||
|
search_fields = ['title', 'content']
|
||||||
|
prepopulated_fields = {'slug': ('title',)}
|
||||||
|
readonly_fields = ['created_at', 'updated_at']
|
||||||
|
ordering = ['-created_at']
|
||||||
6
core/apps.py
Normal file
6
core/apps.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class CoreConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'core'
|
||||||
27
core/forms.py
Normal file
27
core/forms.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
from django import forms
|
||||||
|
from .models import Contact
|
||||||
|
|
||||||
|
|
||||||
|
class ContactForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Contact
|
||||||
|
fields = ['name', 'email', 'subject', 'message']
|
||||||
|
widgets = {
|
||||||
|
'name': forms.TextInput(attrs={
|
||||||
|
'class': 'form-control',
|
||||||
|
'placeholder': 'Your Name'
|
||||||
|
}),
|
||||||
|
'email': forms.EmailInput(attrs={
|
||||||
|
'class': 'form-control',
|
||||||
|
'placeholder': 'your.email@example.com'
|
||||||
|
}),
|
||||||
|
'subject': forms.TextInput(attrs={
|
||||||
|
'class': 'form-control',
|
||||||
|
'placeholder': 'Subject'
|
||||||
|
}),
|
||||||
|
'message': forms.Textarea(attrs={
|
||||||
|
'class': 'form-control',
|
||||||
|
'rows': 5,
|
||||||
|
'placeholder': 'Your message...'
|
||||||
|
}),
|
||||||
|
}
|
||||||
44
core/migrations/0001_initial.py
Normal file
44
core/migrations/0001_initial.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# Generated by Django 4.2.7 on 2025-08-29 16:12
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.utils.timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='BlogPost',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('title', models.CharField(max_length=200)),
|
||||||
|
('slug', models.SlugField(unique=True)),
|
||||||
|
('content', models.TextField()),
|
||||||
|
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True)),
|
||||||
|
('is_published', models.BooleanField(default=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['-created_at'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Contact',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=100)),
|
||||||
|
('email', models.EmailField(max_length=254)),
|
||||||
|
('subject', models.CharField(max_length=200)),
|
||||||
|
('message', models.TextField()),
|
||||||
|
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['-created_at'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
0
core/migrations/__init__.py
Normal file
0
core/migrations/__init__.py
Normal file
31
core/models.py
Normal file
31
core/models.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
from django.db import models
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Contact(models.Model):
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
email = models.EmailField()
|
||||||
|
subject = models.CharField(max_length=200)
|
||||||
|
message = models.TextField()
|
||||||
|
created_at = models.DateTimeField(default=timezone.now)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.name} - {self.subject}"
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['-created_at']
|
||||||
|
|
||||||
|
|
||||||
|
class BlogPost(models.Model):
|
||||||
|
title = models.CharField(max_length=200)
|
||||||
|
slug = models.SlugField(unique=True)
|
||||||
|
content = models.TextField()
|
||||||
|
created_at = models.DateTimeField(default=timezone.now)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
is_published = models.BooleanField(default=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['-created_at']
|
||||||
30
core/static/core/css/style.css
Normal file
30
core/static/core/css/style.css
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
.hero-section {
|
||||||
|
background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-brand {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
23
core/static/core/js/main.js
Normal file
23
core/static/core/js/main.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Auto-hide alerts after 5 seconds
|
||||||
|
const alerts = document.querySelectorAll('.alert');
|
||||||
|
alerts.forEach(function(alert) {
|
||||||
|
setTimeout(function() {
|
||||||
|
const bsAlert = new bootstrap.Alert(alert);
|
||||||
|
bsAlert.close();
|
||||||
|
}, 5000);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Smooth scrolling for internal links
|
||||||
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||||
|
anchor.addEventListener('click', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const target = document.querySelector(this.getAttribute('href'));
|
||||||
|
if (target) {
|
||||||
|
target.scrollIntoView({
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
63
core/templates/core/about.html
Normal file
63
core/templates/core/about.html
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
{% extends 'core/base.html' %}
|
||||||
|
|
||||||
|
{% block title %}About - Django Demo Project{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container py-5">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8 mx-auto">
|
||||||
|
<h1 class="mb-4">About This Project</h1>
|
||||||
|
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Purpose</h5>
|
||||||
|
<p class="card-text">This Django demo project is designed as a production-ready template for testing VPS deployment workflows. It serves as a foundation for future Django projects with all the essential configurations already in place.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Features</h5>
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
<li>✓ Minimal dependencies for fast deployment</li>
|
||||||
|
<li>✓ Environment-based configuration</li>
|
||||||
|
<li>✓ SQLite development / PostgreSQL production</li>
|
||||||
|
<li>✓ Static files handling with WhiteNoise</li>
|
||||||
|
<li>✓ Contact form functionality</li>
|
||||||
|
<li>✓ Simple blog system</li>
|
||||||
|
<li>✓ Bootstrap-based responsive UI</li>
|
||||||
|
<li>✓ Production security settings</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Tech Stack</h5>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
<li><strong>Backend:</strong> Django 4.2.7</li>
|
||||||
|
<li><strong>Database:</strong> SQLite/PostgreSQL</li>
|
||||||
|
<li><strong>Server:</strong> Gunicorn</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
<li><strong>Frontend:</strong> Bootstrap 5</li>
|
||||||
|
<li><strong>Static Files:</strong> WhiteNoise</li>
|
||||||
|
<li><strong>Config:</strong> python-decouple</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-center">
|
||||||
|
<a href="{% url 'contact' %}" class="btn btn-primary">Get in Touch</a>
|
||||||
|
<a href="{% url 'home' %}" class="btn btn-outline-primary">Back to Home</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
63
core/templates/core/base.html
Normal file
63
core/templates/core/base.html
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{% block title %}Django Demo Project{% endblock %}</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
{% load static %}
|
||||||
|
<link rel="stylesheet" href="{% static 'core/css/style.css' %}">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||||
|
<div class="container">
|
||||||
|
<a class="navbar-brand" href="{% url 'home' %}">Django Demo</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
|
<ul class="navbar-nav ms-auto">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{% url 'home' %}">Home</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{% url 'blog' %}">Blog</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{% url 'about' %}">About</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{% url 'contact' %}">Contact</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
{% if messages %}
|
||||||
|
<div class="container mt-3">
|
||||||
|
{% for message in messages %}
|
||||||
|
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
|
||||||
|
{{ message }}
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer class="bg-light text-center text-muted py-4 mt-5">
|
||||||
|
<div class="container">
|
||||||
|
<p>© 2024 Django Demo Project. Built for VPS deployment testing.</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
{% load static %}
|
||||||
|
<script src="{% static 'core/js/main.js' %}"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
31
core/templates/core/blog.html
Normal file
31
core/templates/core/blog.html
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{% extends 'core/base.html' %}
|
||||||
|
|
||||||
|
{% block title %}Blog - Django Demo Project{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container py-5">
|
||||||
|
<h1 class="mb-4">Blog</h1>
|
||||||
|
|
||||||
|
{% if posts %}
|
||||||
|
<div class="row">
|
||||||
|
{% for post in posts %}
|
||||||
|
<div class="col-md-6 mb-4">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">{{ post.title }}</h5>
|
||||||
|
<p class="card-text">{{ post.content|truncatewords:30 }}</p>
|
||||||
|
<small class="text-muted">Published on {{ post.created_at|date:"F d, Y" }}</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="text-center py-5">
|
||||||
|
<h3 class="text-muted">No blog posts yet</h3>
|
||||||
|
<p>Blog posts will appear here once they're created in the admin panel.</p>
|
||||||
|
<a href="{% url 'home' %}" class="btn btn-primary">Back to Home</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
68
core/templates/core/contact.html
Normal file
68
core/templates/core/contact.html
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
{% extends 'core/base.html' %}
|
||||||
|
|
||||||
|
{% block title %}Contact - Django Demo Project{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container py-5">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<h1 class="mb-4">Contact Us</h1>
|
||||||
|
<p class="lead">Have questions about this Django demo project? Get in touch!</p>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="{{ form.name.id_for_label }}" class="form-label">Name</label>
|
||||||
|
{{ form.name }}
|
||||||
|
{% if form.name.errors %}
|
||||||
|
<div class="text-danger">{{ form.name.errors }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="{{ form.email.id_for_label }}" class="form-label">Email</label>
|
||||||
|
{{ form.email }}
|
||||||
|
{% if form.email.errors %}
|
||||||
|
<div class="text-danger">{{ form.email.errors }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="{{ form.subject.id_for_label }}" class="form-label">Subject</label>
|
||||||
|
{{ form.subject }}
|
||||||
|
{% if form.subject.errors %}
|
||||||
|
<div class="text-danger">{{ form.subject.errors }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="{{ form.message.id_for_label }}" class="form-label">Message</label>
|
||||||
|
{{ form.message }}
|
||||||
|
{% if form.message.errors %}
|
||||||
|
<div class="text-danger">{{ form.message.errors }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary">Send Message</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h5>Project Info</h5>
|
||||||
|
<p><strong>Django Version:</strong> 4.2.7</p>
|
||||||
|
<p><strong>Purpose:</strong> VPS Deployment Testing</p>
|
||||||
|
<p><strong>Features:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>Contact Form</li>
|
||||||
|
<li>Blog System</li>
|
||||||
|
<li>Environment Configuration</li>
|
||||||
|
<li>Production Ready</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
77
core/templates/core/home.html
Normal file
77
core/templates/core/home.html
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
{% extends 'core/base.html' %}
|
||||||
|
|
||||||
|
{% block title %}Home - Django Demo Project{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="hero-section bg-primary text-white py-5">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<h1 class="display-4">Django Demo Project</h1>
|
||||||
|
<p class="lead">A production-ready Django application template optimized for VPS hosting deployment.</p>
|
||||||
|
<a href="{% url 'contact' %}" class="btn btn-light btn-lg">Get Started</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container py-5">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<h2>Features</h2>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-4">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Production Ready</h5>
|
||||||
|
<p class="card-text">Configured with environment variables, security settings, and optimized for deployment.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-4">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Database Flexible</h5>
|
||||||
|
<p class="card-text">Works with SQLite for development and PostgreSQL for production.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-4">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">VPS Optimized</h5>
|
||||||
|
<p class="card-text">Includes deployment scripts and configuration for easy VPS hosting.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-4">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">Minimal Dependencies</h5>
|
||||||
|
<p class="card-text">Clean, lightweight setup with only essential packages.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4">
|
||||||
|
<h3>Recent Blog Posts</h3>
|
||||||
|
{% if blog_posts %}
|
||||||
|
{% for post in blog_posts %}
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">{{ post.title }}</h5>
|
||||||
|
<p class="card-text">{{ post.content|truncatewords:20 }}</p>
|
||||||
|
<small class="text-muted">{{ post.created_at|date:"M d, Y" }}</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
<a href="{% url 'blog' %}" class="btn btn-outline-primary">View All Posts</a>
|
||||||
|
{% else %}
|
||||||
|
<p class="text-muted">No blog posts yet. Check back later!</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
3
core/tests.py
Normal file
3
core/tests.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
9
core/urls.py
Normal file
9
core/urls.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from django.urls import path
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('', views.home, name='home'),
|
||||||
|
path('contact/', views.contact, name='contact'),
|
||||||
|
path('blog/', views.blog, name='blog'),
|
||||||
|
path('about/', views.about, name='about'),
|
||||||
|
]
|
||||||
31
core/views.py
Normal file
31
core/views.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
from django.shortcuts import render, redirect
|
||||||
|
from django.contrib import messages
|
||||||
|
from .models import BlogPost
|
||||||
|
from .forms import ContactForm
|
||||||
|
|
||||||
|
|
||||||
|
def home(request):
|
||||||
|
blog_posts = BlogPost.objects.filter(is_published=True)[:3]
|
||||||
|
return render(request, 'core/home.html', {'blog_posts': blog_posts})
|
||||||
|
|
||||||
|
|
||||||
|
def contact(request):
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = ContactForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
form.save()
|
||||||
|
messages.success(request, 'Thank you for your message! We will get back to you soon.')
|
||||||
|
return redirect('contact')
|
||||||
|
else:
|
||||||
|
form = ContactForm()
|
||||||
|
|
||||||
|
return render(request, 'core/contact.html', {'form': form})
|
||||||
|
|
||||||
|
|
||||||
|
def blog(request):
|
||||||
|
posts = BlogPost.objects.filter(is_published=True)
|
||||||
|
return render(request, 'core/blog.html', {'posts': posts})
|
||||||
|
|
||||||
|
|
||||||
|
def about(request):
|
||||||
|
return render(request, 'core/about.html')
|
||||||
0
demo_project/__init__.py
Normal file
0
demo_project/__init__.py
Normal file
16
demo_project/asgi.py
Normal file
16
demo_project/asgi.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
"""
|
||||||
|
ASGI config for demo_project project.
|
||||||
|
|
||||||
|
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.core.asgi import get_asgi_application
|
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'demo_project.settings')
|
||||||
|
|
||||||
|
application = get_asgi_application()
|
||||||
162
demo_project/settings.py
Normal file
162
demo_project/settings.py
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
"""
|
||||||
|
Django settings for demo_project project.
|
||||||
|
|
||||||
|
Generated by 'django-admin startproject' using Django 4.2.7.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/4.2/topics/settings/
|
||||||
|
|
||||||
|
For the full list of settings and their values, see
|
||||||
|
https://docs.djangoproject.com/en/4.2/ref/settings/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
from decouple import config
|
||||||
|
|
||||||
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
|
|
||||||
|
# Quick-start development settings - unsuitable for production
|
||||||
|
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
|
||||||
|
|
||||||
|
# SECURITY WARNING: keep the secret key used in production secret!
|
||||||
|
SECRET_KEY = config('SECRET_KEY', default='django-insecure-%%73_#43bfj4u2^usxt&(hmx$*r!&@w6z7!r-psjm%gu_5(s35')
|
||||||
|
|
||||||
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
|
DEBUG = config('DEBUG', default=True, cast=bool)
|
||||||
|
|
||||||
|
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default='localhost,127.0.0.1', cast=lambda v: [s.strip() for s in v.split(',')])
|
||||||
|
|
||||||
|
|
||||||
|
# Application definition
|
||||||
|
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
'django.contrib.admin',
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.staticfiles',
|
||||||
|
'core',
|
||||||
|
]
|
||||||
|
|
||||||
|
MIDDLEWARE = [
|
||||||
|
'django.middleware.security.SecurityMiddleware',
|
||||||
|
'whitenoise.middleware.WhiteNoiseMiddleware',
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
|
]
|
||||||
|
|
||||||
|
ROOT_URLCONF = 'demo_project.urls'
|
||||||
|
|
||||||
|
TEMPLATES = [
|
||||||
|
{
|
||||||
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
|
'DIRS': [],
|
||||||
|
'APP_DIRS': True,
|
||||||
|
'OPTIONS': {
|
||||||
|
'context_processors': [
|
||||||
|
'django.template.context_processors.debug',
|
||||||
|
'django.template.context_processors.request',
|
||||||
|
'django.contrib.auth.context_processors.auth',
|
||||||
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
WSGI_APPLICATION = 'demo_project.wsgi.application'
|
||||||
|
|
||||||
|
|
||||||
|
# Database
|
||||||
|
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
|
||||||
|
|
||||||
|
# Database configuration
|
||||||
|
if config('DATABASE_URL', default=None):
|
||||||
|
import dj_database_url
|
||||||
|
DATABASES = {
|
||||||
|
'default': dj_database_url.parse(config('DATABASE_URL'))
|
||||||
|
}
|
||||||
|
elif config('USE_POSTGRES', default=False, cast=bool):
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.postgresql',
|
||||||
|
'NAME': config('DB_NAME', default='demo_db'),
|
||||||
|
'USER': config('DB_USER', default='demo_user'),
|
||||||
|
'PASSWORD': config('DB_PASSWORD', default=''),
|
||||||
|
'HOST': config('DB_HOST', default='localhost'),
|
||||||
|
'PORT': config('DB_PORT', default='5432'),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
|
'NAME': BASE_DIR / 'db.sqlite3',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Password validation
|
||||||
|
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
|
||||||
|
|
||||||
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Internationalization
|
||||||
|
# https://docs.djangoproject.com/en/4.2/topics/i18n/
|
||||||
|
|
||||||
|
LANGUAGE_CODE = 'en-us'
|
||||||
|
|
||||||
|
TIME_ZONE = 'UTC'
|
||||||
|
|
||||||
|
USE_I18N = True
|
||||||
|
|
||||||
|
USE_TZ = True
|
||||||
|
|
||||||
|
|
||||||
|
# Static files (CSS, JavaScript, Images)
|
||||||
|
# https://docs.djangoproject.com/en/4.2/howto/static-files/
|
||||||
|
|
||||||
|
STATIC_URL = '/static/'
|
||||||
|
STATIC_ROOT = BASE_DIR / 'staticfiles'
|
||||||
|
STATICFILES_DIRS = []
|
||||||
|
|
||||||
|
# Whitenoise settings
|
||||||
|
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
|
||||||
|
|
||||||
|
# Default primary key field type
|
||||||
|
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
|
||||||
|
|
||||||
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
|
|
||||||
|
# Production security settings
|
||||||
|
if not DEBUG:
|
||||||
|
SECURE_BROWSER_XSS_FILTER = True
|
||||||
|
SECURE_CONTENT_TYPE_NOSNIFF = True
|
||||||
|
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
|
||||||
|
SECURE_HSTS_SECONDS = 31536000
|
||||||
|
SECURE_REDIRECT_EXEMPT = []
|
||||||
|
SECURE_SSL_REDIRECT = config('SECURE_SSL_REDIRECT', default=False, cast=bool)
|
||||||
|
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||||
|
SESSION_COOKIE_SECURE = True
|
||||||
|
CSRF_COOKIE_SECURE = True
|
||||||
23
demo_project/urls.py
Normal file
23
demo_project/urls.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
"""
|
||||||
|
URL configuration for demo_project project.
|
||||||
|
|
||||||
|
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||||
|
https://docs.djangoproject.com/en/4.2/topics/http/urls/
|
||||||
|
Examples:
|
||||||
|
Function views
|
||||||
|
1. Add an import: from my_app import views
|
||||||
|
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||||
|
Class-based views
|
||||||
|
1. Add an import: from other_app.views import Home
|
||||||
|
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||||
|
Including another URLconf
|
||||||
|
1. Import the include() function: from django.urls import include, path
|
||||||
|
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||||
|
"""
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.urls import path, include
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('admin/', admin.site.urls),
|
||||||
|
path('', include('core.urls')),
|
||||||
|
]
|
||||||
16
demo_project/wsgi.py
Normal file
16
demo_project/wsgi.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
"""
|
||||||
|
WSGI config for demo_project project.
|
||||||
|
|
||||||
|
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'demo_project.settings')
|
||||||
|
|
||||||
|
application = get_wsgi_application()
|
||||||
103
deploy/deploy.sh
Executable file
103
deploy/deploy.sh
Executable file
@ -0,0 +1,103 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Django Demo Project Deployment Script
|
||||||
|
# Run this script on your VPS to deploy the application
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "🚀 Starting Django Demo Project deployment..."
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
PROJECT_DIR="/home/ubuntu/django-demo"
|
||||||
|
SERVICE_NAME="django-demo"
|
||||||
|
NGINX_SITE="django-demo"
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Check if running as root
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
echo -e "${RED}Please run this script as root (use sudo)${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${YELLOW}📦 Installing system packages...${NC}"
|
||||||
|
apt update
|
||||||
|
apt install -y python3 python3-venv python3-pip nginx postgresql postgresql-contrib
|
||||||
|
|
||||||
|
echo -e "${YELLOW}👤 Setting up project user and directories...${NC}"
|
||||||
|
# Create project directory
|
||||||
|
mkdir -p $PROJECT_DIR
|
||||||
|
mkdir -p /var/log/django
|
||||||
|
mkdir -p /var/run/gunicorn
|
||||||
|
|
||||||
|
# Set ownership
|
||||||
|
chown -R www-data:www-data /var/log/django
|
||||||
|
chown -R www-data:www-data /var/run/gunicorn
|
||||||
|
|
||||||
|
echo -e "${YELLOW}🐍 Setting up Python virtual environment...${NC}"
|
||||||
|
cd $PROJECT_DIR
|
||||||
|
python3 -m venv venv
|
||||||
|
source venv/bin/activate
|
||||||
|
|
||||||
|
echo -e "${YELLOW}📋 Installing Python packages...${NC}"
|
||||||
|
pip install --upgrade pip
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
echo -e "${YELLOW}⚙️ Configuring environment...${NC}"
|
||||||
|
if [ ! -f ".env" ]; then
|
||||||
|
echo "Creating .env file from example..."
|
||||||
|
cp .env.example .env
|
||||||
|
echo -e "${RED}⚠️ IMPORTANT: Edit .env file with your production settings!${NC}"
|
||||||
|
echo " - Set a secure SECRET_KEY"
|
||||||
|
echo " - Set DEBUG=False"
|
||||||
|
echo " - Configure ALLOWED_HOSTS with your domain"
|
||||||
|
echo " - Configure database settings if using PostgreSQL"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${YELLOW}🗄️ Setting up database...${NC}"
|
||||||
|
python manage.py collectstatic --noinput
|
||||||
|
python manage.py migrate
|
||||||
|
|
||||||
|
echo -e "${YELLOW}👤 Creating Django superuser (optional)...${NC}"
|
||||||
|
echo "Would you like to create a superuser? (y/N)"
|
||||||
|
read -r response
|
||||||
|
if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
|
||||||
|
python manage.py createsuperuser
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${YELLOW}🔧 Setting up systemd service...${NC}"
|
||||||
|
cp deploy/systemd.service /etc/systemd/system/${SERVICE_NAME}.service
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable $SERVICE_NAME
|
||||||
|
|
||||||
|
echo -e "${YELLOW}🌐 Setting up Nginx...${NC}"
|
||||||
|
cp deploy/nginx.conf /etc/nginx/sites-available/$NGINX_SITE
|
||||||
|
ln -sf /etc/nginx/sites-available/$NGINX_SITE /etc/nginx/sites-enabled/
|
||||||
|
rm -f /etc/nginx/sites-enabled/default
|
||||||
|
nginx -t
|
||||||
|
|
||||||
|
echo -e "${YELLOW}🔄 Starting services...${NC}"
|
||||||
|
systemctl restart $SERVICE_NAME
|
||||||
|
systemctl restart nginx
|
||||||
|
|
||||||
|
echo -e "${YELLOW}🔍 Checking service status...${NC}"
|
||||||
|
systemctl is-active --quiet $SERVICE_NAME && echo -e "${GREEN}✅ Django service is running${NC}" || echo -e "${RED}❌ Django service failed${NC}"
|
||||||
|
systemctl is-active --quiet nginx && echo -e "${GREEN}✅ Nginx is running${NC}" || echo -e "${RED}❌ Nginx failed${NC}"
|
||||||
|
|
||||||
|
echo -e "${GREEN}🎉 Deployment completed!${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo "1. Edit .env file with your production settings"
|
||||||
|
echo "2. Update domain name in nginx.conf"
|
||||||
|
echo "3. Set up SSL certificate (recommended: Let's Encrypt)"
|
||||||
|
echo "4. Configure firewall (ufw) to allow HTTP/HTTPS traffic"
|
||||||
|
echo ""
|
||||||
|
echo "Useful commands:"
|
||||||
|
echo " - Restart Django: sudo systemctl restart $SERVICE_NAME"
|
||||||
|
echo " - View logs: sudo journalctl -u $SERVICE_NAME -f"
|
||||||
|
echo " - Restart Nginx: sudo systemctl restart nginx"
|
||||||
|
echo " - Check status: sudo systemctl status $SERVICE_NAME"
|
||||||
35
deploy/gunicorn.conf.py
Normal file
35
deploy/gunicorn.conf.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
"""Gunicorn configuration for production deployment"""
|
||||||
|
|
||||||
|
import multiprocessing
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Server socket
|
||||||
|
bind = "127.0.0.1:8000"
|
||||||
|
backlog = 2048
|
||||||
|
|
||||||
|
# Worker processes
|
||||||
|
workers = multiprocessing.cpu_count() * 2 + 1
|
||||||
|
worker_class = "sync"
|
||||||
|
worker_connections = 1000
|
||||||
|
timeout = 30
|
||||||
|
keepalive = 2
|
||||||
|
max_requests = 1000
|
||||||
|
max_requests_jitter = 100
|
||||||
|
|
||||||
|
# Restart workers after this many requests, with up to max_requests_jitter additional requests
|
||||||
|
preload_app = True
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
accesslog = "/var/log/django/access.log"
|
||||||
|
errorlog = "/var/log/django/error.log"
|
||||||
|
loglevel = "info"
|
||||||
|
|
||||||
|
# Process naming
|
||||||
|
proc_name = "django_demo_project"
|
||||||
|
|
||||||
|
# Server mechanics
|
||||||
|
daemon = False
|
||||||
|
pidfile = "/var/run/gunicorn/django_demo.pid"
|
||||||
|
user = "www-data"
|
||||||
|
group = "www-data"
|
||||||
|
tmp_upload_dir = None
|
||||||
66
deploy/nginx.conf
Normal file
66
deploy/nginx.conf
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name yourdomain.com www.yourdomain.com;
|
||||||
|
|
||||||
|
# Security headers
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||||
|
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
|
||||||
|
|
||||||
|
# Gzip compression
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_min_length 1024;
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_comp_level 6;
|
||||||
|
gzip_types
|
||||||
|
text/plain
|
||||||
|
text/css
|
||||||
|
text/xml
|
||||||
|
text/javascript
|
||||||
|
application/json
|
||||||
|
application/javascript
|
||||||
|
application/xml+rss
|
||||||
|
application/atom+xml
|
||||||
|
image/svg+xml;
|
||||||
|
|
||||||
|
# Static files
|
||||||
|
location /static/ {
|
||||||
|
alias /home/ubuntu/django-demo/staticfiles/;
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Media files (if you add file uploads later)
|
||||||
|
location /media/ {
|
||||||
|
alias /home/ubuntu/django-demo/media/;
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Django application
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:8000;
|
||||||
|
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;
|
||||||
|
proxy_redirect off;
|
||||||
|
|
||||||
|
# Timeouts
|
||||||
|
proxy_connect_timeout 60s;
|
||||||
|
proxy_send_timeout 60s;
|
||||||
|
proxy_read_timeout 60s;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Block access to sensitive files
|
||||||
|
location ~* /\.(?!well-known\/) {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~* /(requirements\.txt|\.env|deploy/) {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
22
deploy/systemd.service
Normal file
22
deploy/systemd.service
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Django Demo Project Gunicorn daemon
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=notify
|
||||||
|
User=www-data
|
||||||
|
Group=www-data
|
||||||
|
RuntimeDirectory=gunicorn
|
||||||
|
WorkingDirectory=/home/ubuntu/django-demo
|
||||||
|
ExecStart=/home/ubuntu/django-demo/venv/bin/gunicorn --config /home/ubuntu/django-demo/deploy/gunicorn.conf.py demo_project.wsgi:application
|
||||||
|
ExecReload=/bin/kill -s HUP $MAINPID
|
||||||
|
KillMode=mixed
|
||||||
|
TimeoutStopSec=5
|
||||||
|
PrivateTmp=true
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
Environment=DJANGO_SETTINGS_MODULE=demo_project.settings
|
||||||
|
EnvironmentFile=/home/ubuntu/django-demo/.env
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
52
deploy/update.sh
Executable file
52
deploy/update.sh
Executable file
@ -0,0 +1,52 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Django Demo Project Update Script
|
||||||
|
# Run this script to deploy updates to your running application
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "🔄 Starting application update..."
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
PROJECT_DIR="/home/ubuntu/django-demo"
|
||||||
|
SERVICE_NAME="django-demo"
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Check if running as root
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
echo "Please run this script as root (use sudo)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${YELLOW}📁 Navigating to project directory...${NC}"
|
||||||
|
cd $PROJECT_DIR
|
||||||
|
|
||||||
|
echo -e "${YELLOW}🐍 Activating virtual environment...${NC}"
|
||||||
|
source venv/bin/activate
|
||||||
|
|
||||||
|
echo -e "${YELLOW}📦 Installing/updating dependencies...${NC}"
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
echo -e "${YELLOW}🗄️ Running database migrations...${NC}"
|
||||||
|
python manage.py migrate
|
||||||
|
|
||||||
|
echo -e "${YELLOW}📄 Collecting static files...${NC}"
|
||||||
|
python manage.py collectstatic --noinput
|
||||||
|
|
||||||
|
echo -e "${YELLOW}🔄 Restarting Django service...${NC}"
|
||||||
|
systemctl restart $SERVICE_NAME
|
||||||
|
|
||||||
|
echo -e "${YELLOW}🔍 Checking service status...${NC}"
|
||||||
|
if systemctl is-active --quiet $SERVICE_NAME; then
|
||||||
|
echo -e "${GREEN}✅ Service is running successfully${NC}"
|
||||||
|
else
|
||||||
|
echo "❌ Service failed to start. Check logs:"
|
||||||
|
echo "sudo journalctl -u $SERVICE_NAME -f"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}✅ Update completed successfully!${NC}"
|
||||||
22
manage.py
Executable file
22
manage.py
Executable file
@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""Django's command-line utility for administrative tasks."""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Run administrative tasks."""
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'demo_project.settings')
|
||||||
|
try:
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
except ImportError as exc:
|
||||||
|
raise ImportError(
|
||||||
|
"Couldn't import Django. Are you sure it's installed and "
|
||||||
|
"available on your PYTHONPATH environment variable? Did you "
|
||||||
|
"forget to activate a virtual environment?"
|
||||||
|
) from exc
|
||||||
|
execute_from_command_line(sys.argv)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
6
requirements-dev.txt
Normal file
6
requirements-dev.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# Development requirements
|
||||||
|
-r requirements.txt
|
||||||
|
|
||||||
|
# Development tools (optional)
|
||||||
|
# django-debug-toolbar==4.2.0
|
||||||
|
# django-extensions==3.2.3
|
||||||
13
requirements.txt
Normal file
13
requirements.txt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Core Django
|
||||||
|
Django==4.2.7
|
||||||
|
|
||||||
|
# Configuration & Environment
|
||||||
|
python-decouple==3.8
|
||||||
|
dj-database-url==2.1.0
|
||||||
|
|
||||||
|
# Production Server & Static Files
|
||||||
|
gunicorn==21.2.0
|
||||||
|
whitenoise==6.6.0
|
||||||
|
|
||||||
|
# Database (PostgreSQL)
|
||||||
|
psycopg2-binary==2.9.7
|
||||||
9
requirements_full.txt
Normal file
9
requirements_full.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
asgiref==3.9.1
|
||||||
|
dj-database-url==2.1.0
|
||||||
|
Django==4.2.7
|
||||||
|
gunicorn==21.2.0
|
||||||
|
packaging==25.0
|
||||||
|
python-decouple==3.8
|
||||||
|
sqlparse==0.5.3
|
||||||
|
typing_extensions==4.15.0
|
||||||
|
whitenoise==6.6.0
|
||||||
Loading…
Reference in New Issue
Block a user