Python之Django框架开发Web应用,并部署到服务器

这个完整的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        # 管理脚本

②、修改settings.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

settings.py

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

  1. 注册Heroku账号并安装CLI
  2. 登录Heroku CLI: heroku login
  3. 创建Heroku应用: heroku create your-app-name
  4. 部署代码: git push heroku main
  5. 运行迁移: heroku run python manage.py migrate
  6. 创建超级用户: heroku run python manage.py createsuperuser
  7. 打开应用: 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测试