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:
thecyberlearn 2025-08-29 21:50:08 +05:30
commit 2f3f1ea867
35 changed files with 1542 additions and 0 deletions

20
.env.example Normal file
View 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
View 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
View 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
View 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
View File

21
core/admin.py Normal file
View 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
View 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
View 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...'
}),
}

View 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'],
},
),
]

View File

31
core/models.py Normal file
View 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']

View 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;
}

View 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'
});
}
});
});
});

View 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 %}

View 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>&copy; 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>

View 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 %}

View 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 %}

View 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
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

9
core/urls.py Normal file
View 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
View 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
View File

16
demo_project/asgi.py Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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