这个完整的Django博客应用包含了:
-
用户认证系统(注册、登录、注销)
-
博客文章CRUD功能
-
基于类的视图
-
用户权限控制
-
Bootstrap前端界面
-
部署准备
①、安装Python和Django,并创建应用
bash
pip install django
#创建Django项目
django-admin startproject myblog
cd myblog
#创建应用
python manage.py startapp blog
txt
myblog/
├── myblog/ # 项目配置文件
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── blog/ # 博客应用
│ ├── migrations/
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
└── manage.py # 管理脚本
python
# myblog/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog', # 添加我们的应用
]
# 配置数据库(使用SQLite)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# 配置模板
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'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',
],
},
},
]
# 静态文件配置
STATIC_URL = '/static/'
STATICFILES_DIRS = [BASE_DIR / 'static']
③、创建模型
python
# blog/models.py
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
④、创建并应用迁移
bash
python manage.py makemigrations
python manage.py migrate
⑤、创建超级用户
bash
python manage.py createsuperuser
⑥、注册模型到管理后台
python
# blog/admin.py
from django.contrib import admin
from .models import Post
admin.site.register(Post)
⑦、创建视图
python
# blog/views.py
from django.shortcuts import render, get_object_or_404
from .models import Post
from django.views.generic import (
ListView,
DetailView,
CreateView,
UpdateView,
DeleteView
)
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
class PostListView(ListView):
model = Post
template_name = 'blog/home.html'
context_object_name = 'posts'
ordering = ['-date_posted']
class PostDetailView(DetailView):
model = Post
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['title', 'content']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Post
fields = ['title', 'content']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def test_func(self):
post = self.get_object()
return self.request.user == post.author
class PostDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Post
success_url = '/'
def test_func(self):
post = self.get_object()
return self.request.user == post.author
⑧、创建URL路由
python
# blog/urls.py
from django.urls import path
from . import views
from .views import (
PostListView,
PostDetailView,
PostCreateView,
PostUpdateView,
PostDeleteView
)
urlpatterns = [
path('', PostListView.as_view(), name='blog-home'),
path('post/<int:pk>/', PostDetailView.as_view(), name='post-detail'),
path('post/new/', PostCreateView.as_view(), name='post-create'),
path('post/<int:pk>/update/', PostUpdateView.as_view(), name='post-update'),
path('post/<int:pk>/delete/', PostDeleteView.as_view(), name='post-delete'),
]
项目URLs
python
# myblog/urls.py
from django.contrib import admin
from django.urls import path, include
from django.contrib.auth import views as auth_views
from users import views as user_views
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blog.urls')),
path('register/', user_views.register, name='register'),
path('login/', auth_views.LoginView.as_view(template_name='users/login.html'), name='login'),
path('logout/', auth_views.LogoutView.as_view(template_name='users/logout.html'), name='logout'),
]
⑨、创建模板
html
<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
<title>Django Blog</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<header class="site-header">
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="{% url 'blog-home' %}">Django Blog</a>
<div class="navbar-nav">
<a class="nav-item nav-link" href="{% url 'blog-home' %}">Home</a>
<a class="nav-item nav-link" href="{% url 'post-create' %}">New Post</a>
</div>
<div class="navbar-nav ms-auto">
{% if user.is_authenticated %}
<a class="nav-item nav-link" href="{% url 'logout' %}">Logout</a>
{% else %}
<a class="nav-item nav-link" href="{% url 'login' %}">Login</a>
<a class="nav-item nav-link" href="{% url 'register' %}">Register</a>
{% endif %}
</div>
</div>
</nav>
</header>
<main role="main" class="container">
<div class="row">
<div class="col-md-8">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% block content %}{% endblock %}
</div>
</div>
</main>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
博客主页模板
html
<!-- templates/blog/home.html -->
{% extends "base.html" %}
{% block content %}
{% for post in posts %}
<article class="media content-section">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="#">{{ post.author }}</a>
<small class="text-muted">{{ post.date_posted|date:"F d, Y" }}</small>
</div>
<h2><a class="article-title" href="{% url 'post-detail' post.id %}">{{ post.title }}</a></h2>
<p class="article-content">{{ post.content }}</p>
</div>
</article>
{% endfor %}
{% endblock content %}
文章详情模板
html
<!-- templates/blog/post_detail.html -->
{% extends "base.html" %}
{% block content %}
<article class="media content-section">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="#">{{ object.author }}</a>
<small class="text-muted">{{ object.date_posted|date:"F d, Y" }}</small>
{% if object.author == user %}
<div>
<a class="btn btn-secondary btn-sm mt-1 mb-1" href="{% url 'post-update' object.id %}">Update</a>
<a class="btn btn-danger btn-sm mt-1 mb-1" href="{% url 'post-delete' object.id %}">Delete</a>
</div>
{% endif %}
</div>
<h2 class="article-title">{{ object.title }}</h2>
<p class="article-content">{{ object.content }}</p>
</div>
</article>
{% endblock content %}
⑩、创建用户认证系统
bash
#创建用户应用
python manage.py startapp users
python
# users/models.py 用户模型
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
def __str__(self):
return f'{self.user.username} Profile'
python
# users/views.py 用户视图
from django.shortcuts import render, redirect
from django.contrib import messages
from django.contrib.auth.forms import UserCreationForm
from .forms import UserRegisterForm
def register(request):
if request.method == 'POST':
form = UserRegisterForm(request.POST)
if form.is_valid():
form.save()
username = form.cleaned_data.get('username')
messages.success(request, f'Account created for {username}!')
return redirect('login')
else:
form = UserRegisterForm()
return render(request, 'users/register.html', {'form': form})
python
# users/forms.py 用户表单
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
html
<!-- templates/users/register.html -->
{% extends "base.html" %}
{% block content %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Join Today</legend>
{{ form.as_p }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Sign Up</button>
</div>
</form>
<div class="border-top pt-3">
<small class="text-muted">
Already Have An Account? <a class="ml-2" href="{% url 'login' %}">Sign In</a>
</small>
</div>
</div>
{% endblock content %}
运行开发服务器
bash
python manage.py runserver
bash
#安装必要包
pip install gunicorn whitenoise
python
# 添加静态文件配置
STATIC_ROOT = BASE_DIR / 'staticfiles'
# 添加安全配置
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
创建Procfile
txt
web: gunicorn myblog.wsgi --log-file -
bash
#requirements.txt
pip freeze > requirements.txt
部署到Heroku
- 注册Heroku账号并安装CLI
- 登录Heroku CLI: heroku login
- 创建Heroku应用: heroku create your-app-name
- 部署代码: git push heroku main
- 运行迁移: heroku run python manage.py migrate
- 创建超级用户: heroku run python manage.py createsuperuser
- 打开应用: heroku open
Web应用部署到服务器
以Ubuntu + Nginx + Gunicorn为例
一、准备工作
1.1 服务器准备
- 购买云服务器(如AWS EC2、阿里云ECS、腾讯云CVM等)
- 确保服务器有公网IP
- 开放必要端口(80/HTTP, 443/HTTPS, 22/SSH)
1.2 本地开发环境
- 确保应用在本地开发环境可以正常运行
- 收集所有依赖项:pip freeze > requirements.txt
二、服务器初始设置(以Ubuntu 20.04为例)
连接服务器
bash
ssh root@your_server_ip
创建新用户(推荐)
bash
adduser deploy
usermod -aG sudo deploy
su - deploy
更新系统
bash
sudo apt update
sudo apt upgrade -y
安装必要软件
bash
sudo apt install -y python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx curl git
三、配置数据库(PostgreSQL)
登录PostgreSQL
bash
sudo -u postgres psql
创建数据库和用户
sql
CREATE DATABASE myproject;
CREATE USER myprojectuser WITH PASSWORD 'password';
ALTER ROLE myprojectuser SET client_encoding TO 'utf8';
ALTER ROLE myprojectuser SET default_transaction_isolation TO 'read committed';
ALTER ROLE myprojectuser SET timezone TO 'UTC';
GRANT ALL PRIVILEGES ON DATABASE myproject TO myprojectuser;
\q
四、部署Django项目
克隆项目代码
bash
git clone https://github.com/yourusername/yourproject.git
cd yourproject
创建虚拟环境
bash
python3 -m venv venv
source venv/bin/activate
安装依赖
bash
pip install -r requirements.txt
配置生产环境设置
python
from .settings import *
DEBUG = False
ALLOWED_HOSTS = ['yourdomain.com', 'your_server_ip']
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'myproject',
'USER': 'myprojectuser',
'PASSWORD': 'password',
'HOST': 'localhost',
'PORT': '',
}
}
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATIC_URL = '/static/'
# 安全设置
SECURE_HSTS_SECONDS = 31536000 # 1 year
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
应用数据库迁移
bash
python manage.py migrate --settings=myproject.settings_prod
收集静态文件
bash
python manage.py collectstatic --settings=myproject.settings_prod
创建超级用户
bash
python manage.py createsuperuser --settings=myproject.settings_prod
五、配置Gunicorn
安装Gunicorn
bash
pip install gunicorn
测试Gunicorn
bash
gunicorn --bind 0.0.0.0:8000 myproject.wsgi --settings=myproject.settings_prod
创建Gunicorn系统服务
ini
#/etc/systemd/system/gunicorn.service
[Unit]
Description=gunicorn daemon
After=network.target
[Service]
User=deploy
Group=www-data
WorkingDirectory=/home/deploy/yourproject
ExecStart=/home/deploy/yourproject/venv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/home/deploy/yourproject/yourproject.sock myproject.wsgi --settings=myproject.settings_prod
[Install]
WantedBy=multi-user.target
启动Gunicorn服务
bash
sudo systemctl start gunicorn
sudo systemctl enable gunicorn
六、配置Nginx
创建Nginx配置文件
/etc/nginx/sites-available/yourproject
nginx
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/deploy/yourproject;
}
location / {
include proxy_params;
proxy_pass http://unix:/home/deploy/yourproject/yourproject.sock;
}
}
启用配置
bash
sudo ln -s /etc/nginx/sites-available/yourproject /etc/nginx/sites-enabled
sudo nginx -t # 测试配置
sudo systemctl restart nginx
七、配置域名和SSL(可选但推荐)
安装Certbot
bash
sudo apt install certbot python3-certbot-nginx
获取SSL证书
bash
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
设置自动续期
bash
sudo certbot renew --dry-run
八、防火墙配置
bash
sudo ufw allow 'Nginx Full'
sudo ufw delete allow 'Nginx HTTP'
sudo ufw allow 'OpenSSH'
sudo ufw enable
部署完成后的维护
更新代码后的操作
bash
git pull origin main
source venv/bin/activate
pip install -r requirements.txt
python manage.py migrate --settings=myproject.settings_prod
python manage.py collectstatic --settings=myproject.settings_prod --noinput
sudo systemctl restart gunicorn
常用命令
- 查看Gunicorn日志:sudo journalctl -u gunicorn
- 查看Nginx日志:sudo tail -F /var/log/nginx/error.log
- 重启服务:sudo systemctl restart gunicorn nginx
备份策略
数据库备份
bash
sudo -u postgres pg_dump myproject > backup_$(date +%Y-%m-%d).sql
项目文件备份
bash
tar -czvf backup_$(date +%Y-%m-%d).tar.gz /home/deploy/yourproject
常见问题解决
502 Bad Gateway错误
- 检查Gunicorn是否运行:sudo systemctl status gunicorn
- 检查socket文件权限
静态文件404错误
- 确保STATIC_ROOT设置正确
- 确保Nginx配置中的静态文件路径正确
数据库连接问题
- 检查PostgreSQL用户权限
- 检查settings_prod.py中的数据库配置
域名解析问题
- 确保DNS记录已正确设置
- 使用ping yourdomain.com测试