当前位置: 首页 > news >正文

《Python星球日记》第35天:全栈开发(综合项目)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》
创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊)
专栏:《Python星球日记》,限时特价订阅中ing

目录

    • 一、全栈开发概述
      • 1. 全栈开发的优势
      • 2. 全栈开发技能组合
    • 二、博客系统项目需求分析
      • 1. 功能需求
      • 2. 技术栈选择
      • 3. 项目结构规划
    • 三、数据库设计
      • 1. 实体关系分析
      • 2. Django模型设计
    • 四、后端开发
      • 1. Django项目创建
      • 2. 视图函数开发
      • 3. URL配置
      • 4. 表单处理
    • 五、前端开发
      • 1. Base模板
      • 2. 博客首页
      • 3. 文章详情页
    • 六、数据库集成
      • 1. 数据库配置
      • 2. 模型创建与迁移
      • 3. 管理后台配置
    • 七、集成测试
      • 1. 模型测试
      • 2. 视图测试
    • 八、部署上线
      • 1. 生产环境设置
      • 2. Docker部署
      • 3. 启动服务
      • 4. Python星球博客系统界面预览
    • 九、总结与进阶
      • 1. 项目亮点
      • 2. 进阶方向
    • 十、今日练习
      • 1. 创建项目框架
      • 2. 实现核心功能
      • 3. 优化和测试
      • 4. 部署项目
      • 挑战任务
    • 参考资源
    • 结语

👋 专栏介绍: Python星球日记专栏介绍(持续更新ing)
上一篇: 《Python星球日记》第34天:Web 安全基础

欢迎来到Python星球的第35天!🪐

大家好,今天我们将通过一个综合项目来实践全栈开发,将前面学习的所有技术融会贯通。

一、全栈开发概述

全栈开发是指同时负责前端和后端开发的工程师,能够独立完成一个完整的应用系统。在Python生态中,全栈开发已经成为一项极具价值的技能

在这里插入图片描述

1. 全栈开发的优势

   - 开发流程更加流畅,减少沟通成本- 技术栈统一,提高开发效率- 更好地理解和解决系统整体问题- 职业发展更具竞争力

2. 全栈开发技能组合

   - 前端:HTML、CSS、JavaScript以及框架(React、Vue等)- 后端:Python框架(Django、Flask)- 数据库:关系型数据库(MySQL、SQLite)- 部署运维:Git、Docker、云服务等

在这里插入图片描述

二、博客系统项目需求分析

让我们以一个博客系统为例,从零开始构建一个全栈应用。

1. 功能需求

  • 用户模块:注册、登录、个人信息管理
  • 文章模块:发布、编辑、删除、查看文章
  • 评论模块:发表评论、回复评论
  • 分类与标签:文章分类、标签管理
  • 搜索功能:按关键词搜索文章

2. 技术栈选择

  • 前端:HTML/CSS/JavaScript + Bootstrap(简化响应式设计)
  • 后端:Django(适合快速开发完整应用)
  • 数据库:SQLite(开发阶段)/ MySQL(生产环境)

3. 项目结构规划

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

三、数据库设计

数据库设计是全栈应用的基础,良好的数据库结构设计能够为应用提供坚实的后盾。

1. 实体关系分析

我们需要先明确博客系统中的几个主要实体:用户(User)、文章(Post)、评论(Comment)、分类(Category)和标签(Tag)。

在这里插入图片描述

2. Django模型设计

让我们将ER图转换为Django模型代码:

# users/models.py
from django.db import models
from django.contrib.auth.models import AbstractUserclass User(AbstractUser):profile_pic = models.ImageField(upload_to='profile_pics', blank=True)bio = models.TextField(max_length=500, blank=True)def __str__(self):return self.username
# blog/models.py
from django.db import models
from django.urls import reverse
from users.models import Userclass Category(models.Model):name = models.CharField(max_length=100, unique=True)description = models.TextField(blank=True)def __str__(self):return self.nameclass Meta:verbose_name_plural = "Categories"class Tag(models.Model):name = models.CharField(max_length=50, unique=True)def __str__(self):return self.nameclass Post(models.Model):title = models.CharField(max_length=200)content = models.TextField()created_at = models.DateTimeField(auto_now_add=True)updated_at = models.DateTimeField(auto_now=True)views = models.IntegerField(default=0)author = models.ForeignKey(User, on_delete=models.CASCADE)category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)tags = models.ManyToManyField(Tag, blank=True)def __str__(self):return self.titledef get_absolute_url(self):return reverse('post-detail', kwargs={'pk': self.pk})
# comments/models.py
from django.db import models
from users.models import User
from blog.models import Postclass Comment(models.Model):content = models.TextField()created_at = models.DateTimeField(auto_now_add=True)user = models.ForeignKey(User, on_delete=models.CASCADE)post = models.ForeignKey(Post, on_delete=models.CASCADE)parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True)def __str__(self):return f"Comment by {self.user.username} on {self.post.title}"

四、后端开发

后端是应用的核心,负责处理业务逻辑和数据交互。我们使用Django框架来实现博客系统的后端功能。

1. Django项目创建

# 创建Django项目
django-admin startproject pythonblog# 创建应用
cd pythonblog
python manage.py startapp blog
python manage.py startapp users
python manage.py startapp comments

2. 视图函数开发

以博客文章的CRUD操作为例:

# blog/views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from .models import Post, Category, Tag
from .forms import PostFormclass PostListView(ListView):model = Posttemplate_name = 'blog/home.html'context_object_name = 'posts'ordering = ['-created_at']paginate_by = 5class PostDetailView(DetailView):model = Postdef get_context_data(self, **kwargs):context = super().get_context_data(**kwargs)# 增加阅读量post = self.objectpost.views += 1post.save()return contextclass PostCreateView(LoginRequiredMixin, CreateView):model = Postform_class = PostFormdef form_valid(self, form):form.instance.author = self.request.userreturn super().form_valid(form)class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):model = Postform_class = PostFormdef form_valid(self, form):form.instance.author = self.request.userreturn super().form_valid(form)def test_func(self):post = self.get_object()# 确保只有文章作者才能编辑return self.request.user == post.authorclass PostDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):model = Postsuccess_url = '/'def test_func(self):post = self.get_object()# 确保只有文章作者才能删除return self.request.user == post.author

3. URL配置

# blog/urls.py
from django.urls import path
from . import viewsurlpatterns = [path('', views.PostListView.as_view(), name='blog-home'),path('post/<int:pk>/', views.PostDetailView.as_view(), name='post-detail'),path('post/new/', views.PostCreateView.as_view(), name='post-create'),path('post/<int:pk>/update/', views.PostUpdateView.as_view(), name='post-update'),path('post/<int:pk>/delete/', views.PostDeleteView.as_view(), name='post-delete'),path('category/<int:category_id>/', views.category_posts, name='category-posts'),path('tag/<int:tag_id>/', views.tag_posts, name='tag-posts'),
]

4. 表单处理

# blog/forms.py
from django import forms
from .models import Post, Commentclass PostForm(forms.ModelForm):class Meta:model = Postfields = ['title', 'content', 'category', 'tags']widgets = {'content': forms.Textarea(attrs={'class': 'markdown-editor'}),'tags': forms.CheckboxSelectMultiple(),}

五、前端开发

前端负责用户交互界面的实现,我们使用Bootstrap框架来快速构建响应式界面。

1. Base模板

<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>{% block title %}Python星球博客{% endblock %}</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"><link rel="stylesheet" href="{% static 'css/main.css' %}">{% block extra_css %}{% endblock %}
</head>
<body><!-- 导航栏 --><nav class="navbar navbar-expand-lg navbar-dark bg-dark"><div class="container"><a class="navbar-brand" href="{% url 'blog-home' %}">Python星球博客</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 me-auto"><li class="nav-item"><a class="nav-link" href="{% url 'blog-home' %}">首页</a></li><li class="nav-item"><a class="nav-link" href="#">分类</a></li></ul><div class="navbar-nav">{% if user.is_authenticated %}<a class="nav-link" href="{% url 'post-create' %}">写文章</a><a class="nav-link" href="{% url 'profile' %}">个人中心</a><a class="nav-link" href="{% url 'logout' %}">退出</a>{% else %}<a class="nav-link" href="{% url 'login' %}">登录</a><a class="nav-link" href="{% url 'register' %}">注册</a>{% endif %}</div></div></div></nav><!-- 主内容区 --><main class="container mt-4">{% if messages %}{% for message in messages %}<div class="alert alert-{{ message.tags }}">{{ message }}</div>{% endfor %}{% endif %}{% block content %}{% endblock %}</main><!-- 页脚 --><footer class="bg-dark text-white text-center py-3 mt-5"><div class="container"><p>&copy; 2025 Python星球博客 | Powered by Django</p></div></footer><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>{% block extra_js %}{% endblock %}
</body>
</html>

2. 博客首页

<!-- templates/blog/home.html -->
{% extends 'base.html' %}{% block title %}Python星球博客 - 首页{% endblock %}{% block content %}
<div class="row"><!-- 文章列表 --><div class="col-md-8"><h1 class="mb-4">最新文章</h1>{% for post in posts %}<div class="card mb-4"><div class="card-body"><h2 class="card-title"><a href="{% url 'post-detail' post.pk %}">{{ post.title }}</a></h2><p class="card-text text-muted"><small>由 {{ post.author.username }} 发布于 {{ post.created_at|date:"Y-m-d H:i" }}| 分类: <a href="{% url 'category-posts' post.category.id %}">{{ post.category.name }}</a>| 阅读量: {{ post.views }}</small></p><p class="card-text">{{ post.content|truncatewords:50 }}</p><a href="{% url 'post-detail' post.pk %}" class="btn btn-primary">阅读全文</a></div></div>{% empty %}<p>暂无文章</p>{% endfor %}<!-- 分页 -->{% if is_paginated %}<nav aria-label="Page navigation"><ul class="pagination">{% if page_obj.has_previous %}<li class="page-item"><a class="page-link" href="?page=1">首页</a></li><li class="page-item"><a class="page-link" href="?page={{ page_obj.previous_page_number }}">上一页</a></li>{% endif %}{% for num in page_obj.paginator.page_range %}{% if page_obj.number == num %}<li class="page-item active"><span class="page-link">{{ num }}</span></li>{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}<li class="page-item"><a class="page-link" href="?page={{ num }}">{{ num }}</a></li>{% endif %}{% endfor %}{% if page_obj.has_next %}<li class="page-item"><a class="page-link" href="?page={{ page_obj.next_page_number }}">下一页</a></li><li class="page-item"><a class="page-link" href="?page={{ page_obj.paginator.num_pages }}">末页</a></li>{% endif %}</ul></nav>{% endif %}</div><!-- 侧边栏 --><div class="col-md-4"><div class="card mb-4"><div class="card-header">搜索</div><div class="card-body"><form action="{% url 'search' %}" method="get"><div class="input-group"><input type="text" name="q" class="form-control" placeholder="搜索文章..."><button class="btn btn-outline-secondary" type="submit">搜索</button></div></form></div></div><div class="card mb-4"><div class="card-header">分类</div><div class="card-body"><ul class="list-group list-group-flush">{% for category in categories %}<li class="list-group-item d-flex justify-content-between align-items-center"><a href="{% url 'category-posts' category.id %}">{{ category.name }}</a><span class="badge bg-primary rounded-pill">{{ category.post_set.count }}</span></li>{% empty %}<li class="list-group-item">暂无分类</li>{% endfor %}</ul></div></div><div class="card"><div class="card-header">热门标签</div><div class="card-body"><div class="tags">{% for tag in tags %}<a href="{% url 'tag-posts' tag.id %}" class="badge bg-secondary me-1 mb-1">{{ tag.name }} ({{ tag.post_set.count }})</a>{% empty %}<p>暂无标签</p>{% endfor %}</div></div></div></div>
</div>
{% endblock %}

3. 文章详情页

<!-- templates/blog/post_detail.html -->
{% extends 'base.html' %}{% block title %}{{ post.title }} - Python星球博客{% endblock %}{% block content %}
<div class="row"><div class="col-md-8"><!-- 文章内容 --><article class="card mb-4"><div class="card-body"><h1 class="card-title">{{ post.title }}</h1><p class="text-muted"><small>由 {{ post.author.username }} 发布于 {{ post.created_at|date:"Y-m-d H:i" }}| 分类: <a href="{% url 'category-posts' post.category.id %}">{{ post.category.name }}</a>| 阅读量: {{ post.views }}{% if post.updated_at != post.created_at %}| 最后编辑: {{ post.updated_at|date:"Y-m-d H:i" }}{% endif %}</small></p><!-- 文章内容 --><div class="card-text mt-4">{{ post.content|safe|linebreaks }}</div><!-- 标签 --><div class="mt-4">{% for tag in post.tags.all %}<a href="{% url 'tag-posts' tag.id %}" class="badge bg-secondary me-1">{{ tag.name }}</a>{% endfor %}</div><!-- 操作按钮 -->{% if user == post.author %}<div class="mt-4"><a href="{% url 'post-update' post.pk %}" class="btn btn-outline-primary">编辑</a><a href="{% url 'post-delete' post.pk %}" class="btn btn-outline-danger">删除</a></div>{% endif %}</div></article><!-- 评论区 --><div class="card"><div class="card-header">评论 ({{ post.comment_set.count }})</div><div class="card-body"><!-- 评论表单 -->{% if user.is_authenticated %}<form method="post" action="{% url 'add-comment' post.pk %}">{% csrf_token %}<div class="mb-3"><textarea name="content" class="form-control" rows="3" placeholder="写下你的评论..."></textarea></div><button type="submit" class="btn btn-primary">提交评论</button></form>{% else %}<p><a href="{% url 'login' %}">登录</a> 后发表评论</p>{% endif %}<!-- 评论列表 --><div class="mt-4">{% for comment in post.comment_set.all %}<div class="mb-3 pb-3 border-bottom"><div class="d-flex"><div class="flex-shrink-0"><img src="{{ comment.user.profile_pic.url|default:'static/img/default-avatar.jpg' }}" class="rounded-circle" width="50" height="50" alt=""></div><div class="ms-3"><h5 class="mt-0">{{ comment.user.username }}</h5><p class="text-muted"><small>{{ comment.created_at|date:"Y-m-d H:i" }}</small></p><p>{{ comment.content }}</p><!-- 回复按钮 -->{% if user.is_authenticated %}<button class="btn btn-sm btn-link reply-btn" data-comment-id="{{ comment.id }}">回复</button><!-- 回复表单 --><div class="reply-form mt-2" id="reply-form-{{ comment.id }}" style="display: none;"><form method="post" action="{% url 'add-reply' post.pk comment.pk %}">{% csrf_token %}<div class="mb-3"><textarea name="content" class="form-control" rows="2" placeholder="回复 {{ comment.user.username }}..."></textarea></div><button type="submit" class="btn btn-sm btn-primary">提交回复</button></form></div>{% endif %}<!-- 子评论 -->{% for reply in comment.comment_set.all %}<div class="ms-4 mt-3"><div class="d-flex"><div class="flex-shrink-0"><img src="{{ reply.user.profile_pic.url|default:'static/img/default-avatar.jpg' }}" class="rounded-circle" width="40" height="40" alt=""></div><div class="ms-3"><h6 class="mt-0">{{ reply.user.username }}</h6><p class="text-muted"><small>{{ reply.created_at|date:"Y-m-d H:i" }}</small></p><p>{{ reply.content }}</p></div></div></div>{% endfor %}</div></div></div>{% empty %}<p>暂无评论,发表第一条评论吧!</p>{% endfor %}</div></div></div></div><!-- 侧边栏 --><div class="col-md-4"><!-- 作者信息 --><div class="card mb-4"><div class="card-header">作者信息</div><div class="card-body text-center"><img src="{{ post.author.profile_pic.url|default:'static/img/default-avatar.jpg' }}" class="rounded-circle mb-3" width="100" height="100" alt=""><h5>{{ post.author.username }}</h5><p>{{ post.author.bio|default:"这个人很懒,什么都没写..." }}</p><p>文章数: {{ post.author.post_set.count }}</p></div></div><!-- 相关文章 --><div class="card"><div class="card-header">相关文章</div><div class="card-body"><ul class="list-group list-group-flush">{% for related_post in related_posts %}<li class="list-group-item"><a href="{% url 'post-detail' related_post.pk %}">{{ related_post.title }}</a><p class="text-muted mb-0"><small>{{ related_post.created_at|date:"Y-m-d" }}</small></p></li>{% empty %}<li class="list-group-item">暂无相关文章</li>{% endfor %}</ul></div></div></div>
</div>{% block extra_js %}
<script>// 回复功能的交互逻辑document.addEventListener('DOMContentLoaded', function() {const replyButtons = document.querySelectorAll('.reply-btn');replyButtons.forEach(button => {button.addEventListener('click', function() {const commentId = this.getAttribute('data-comment-id');const replyForm = document.getElementById(`reply-form-${commentId}`);// 切换显示/隐藏回复表单if (replyForm.style.display === 'none') {replyForm.style.display = 'block';} else {replyForm.style.display = 'none';}});});});
</script>
{% endblock %}
{% endblock %}

六、数据库集成

Django的ORM系统使数据库操作变得简单而优雅。

1. 数据库配置

# pythonblog/settings.py
DATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3',  # 开发环境使用SQLite'NAME': BASE_DIR / 'db.sqlite3',}
}# 生产环境可以使用MySQL
"""
DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql','NAME': 'pythonblog','USER': 'bloguser','PASSWORD': 'your_secure_password','HOST': 'localhost','PORT': '3306',}
}
"""

2. 模型创建与迁移

# 创建迁移文件
python manage.py makemigrations# 应用迁移
python manage.py migrate# 创建超级用户
python manage.py createsuperuser

3. 管理后台配置

# blog/admin.py
from django.contrib import admin
from .models import Post, Category, Tag@admin.register(Post)
class PostAdmin(admin.ModelAdmin):list_display = ('title', 'author', 'category', 'created_at', 'updated_at', 'views')list_filter = ('category', 'created_at')search_fields = ('title', 'content')date_hierarchy = 'created_at'filter_horizontal = ('tags',)readonly_fields = ('views',)@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):list_display = ('name', 'description')search_fields = ('name',)@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):list_display = ('name',)search_fields = ('name',)

七、集成测试

测试是确保应用质量的重要环节,Django提供了强大的测试框架。

1. 模型测试

# blog/tests/test_models.py
from django.test import TestCase
from django.contrib.auth import get_user_model
from blog.models import Post, Category, TagUser = get_user_model()class PostModelTest(TestCase):@classmethoddef setUpTestData(cls):# 创建测试用户test_user = User.objects.create_user(username='testuser',email='test@example.com',password='testpassword')# 创建测试分类test_category = Category.objects.create(name='测试分类',description='这是一个测试分类')# 创建测试标签test_tag = Tag.objects.create(name='测试标签')# 创建测试文章test_post = Post.objects.create(title='测试文章',content='这是一篇测试文章的内容。',author=test_user,category=test_category)test_post.tags.add(test_tag)def test_post_content(self):post = Post.objects.get(id=1)self.assertEqual(post.title, '测试文章')self.assertEqual(post.content, '这是一篇测试文章的内容。')self.assertEqual(post.author.username, 'testuser')self.assertEqual(post.category.name, '测试分类')self.assertEqual(post.tags.first().name, '测试标签')self.assertEqual(post.views, 0)def test_post_str_method(self):post = Post.objects.get(id=1)self.assertEqual(str(post), '测试文章')def test_get_absolute_url(self):post = Post.objects.get(id=1)self.assertEqual(post.get_absolute_url(), '/post/1/')

2. 视图测试

# blog/tests/test_views.py
from django.test import TestCase, Client
from django.urls import reverse
from django.contrib.auth import get_user_model
from blog.models import Post, Category, TagUser = get_user_model()class PostViewsTest(TestCase):@classmethoddef setUpTestData(cls):# 创建测试用户cls.test_user = User.objects.create_user(username='testuser',email='test@example.com',password='testpassword')# 创建测试分类cls.test_category = Category.objects.create(name='测试分类',description='这是一个测试分类')# 创建测试标签cls.test_tag = Tag.objects.create(name='测试标签')# 创建测试文章cls.test_post = Post.objects.create(title='测试文章',content='这是一篇测试文章的内容。',author=cls.test_user,category=cls.test_category)cls.test_post.tags.add(cls.test_tag)def setUp(self):self.client = Client()def test_post_list_view(self):response = self.client.get(reverse('blog-home'))self.assertEqual(response.status_code, 200)self.assertContains(response, '测试文章')self.assertTemplateUsed(response, 'blog/home.html')def test_post_detail_view(self):response = self.client.get(reverse('post-detail', args=[1]))self.assertEqual(response.status_code, 200)self.assertContains(response, '测试文章')self.assertContains(response, '这是一篇测试文章的内容。')self.assertTemplateUsed(response, 'blog/post_detail.html')# 测试阅读量增加post = Post.objects.get(id=1)self.assertEqual(post.views, 1)def test_post_create_view(self):# 测试未登录用户无法访问response = self.client.get(reverse('post-create'))self.assertNotEqual(response.status_code, 200)# 测试登录用户可以访问self.client.login(username='testuser', password='testpassword')response = self.client.get(reverse('post-create'))self.assertEqual(response.status_code, 200)self.assertTemplateUsed(response, 'blog/post_form.html')# 测试创建文章post_data = {'title': '新测试文章','content': '这是一篇新的测试文章内容。','category': self.test_category.id,'tags': [self.test_tag.id]}response = self.client.post(reverse('post-create'), post_data)self.assertEqual(Post.objects.count(), 2)new_post = Post.objects.get(title='新测试文章')self.assertEqual(new_post.author, self.test_user)

八、部署上线

将应用部署到生产环境是全栈开发的最后一步。

1. 生产环境设置

# pythonblog/settings.py# 生产环境设置
DEBUG = False
ALLOWED_HOSTS = ['www.pythonplanet.com', 'pythonplanet.com']# 静态文件设置
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static_collected')# 媒体文件设置
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')# 安全设置
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 3600
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True

2. Docker部署

创建Dockerfile文件:

FROM python:3.10-slimWORKDIR /appCOPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txtCOPY . .RUN python manage.py collectstatic --noinputEXPOSE 8000CMD ["gunicorn", "pythonblog.wsgi:application", "--bind", "0.0.0.0:8000"]

创建docker-compose.yml文件:

version: '3'services:web:build: .restart: alwaysvolumes:- static_data:/app/static_collected- media_data:/app/mediadepends_on:- dbenvironment:- DB_HOST=db- DB_NAME=pythonblog- DB_USER=bloguser- DB_PASSWORD=your_secure_password- SECRET_KEY=your_secret_key- DEBUG=Falsedb:image: mysql:8.0restart: alwaysvolumes:- db_data:/var/lib/mysqlenvironment:- MYSQL_DATABASE=pythonblog- MYSQL_USER=bloguser- MYSQL_PASSWORD=your_secure_password- MYSQL_ROOT_PASSWORD=mysql_root_passwordnginx:image: nginx:latestrestart: alwaysports:- "80:80"- "443:443"volumes:- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf- static_data:/var/www/static- media_data:/var/www/media- ./nginx/ssl:/etc/nginx/ssldepends_on:- webvolumes:db_data:static_data:media_data:

配置Nginx(nginx/nginx.conf):

server {listen 80;server_name pythonplanet.com www.pythonplanet.com;# 重定向HTTP到HTTPSreturn 301 https://$host$request_uri;
}server {listen 443 ssl;server_name pythonplanet.com www.pythonplanet.com;ssl_certificate /etc/nginx/ssl/pythonplanet.crt;ssl_certificate_key /etc/nginx/ssl/pythonplanet.key;# SSL配置ssl_protocols TLSv1.2 TLSv1.3;ssl_ciphers HIGH:!aNULL:!MD5;ssl_prefer_server_ciphers on;# 静态文件location /static/ {alias /var/www/static/;expires 30d;}# 媒体文件location /media/ {alias /var/www/media/;expires 30d;}# 主应用location / {proxy_pass http://web: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;}
}

3. 启动服务

# 构建并启动容器
docker-compose up -d# 执行数据库迁移
docker-compose exec web python manage.py migrate# 创建超级用户
docker-compose exec web python manage.py createsuperuser

4. Python星球博客系统界面预览

为了让您更好地理解博客系统的外观和操作流程,我将为您展示几个关键页面的界面效果图。这些图示展示了系统运行后的实际效果,帮助您直观地了解用户交互体验。

1️⃣博客系统首页

这是用户访问博客时看到的首页界面。呈现了最新文章列表、分类和标签等核心功能。

在这里插入图片描述

2️⃣文章详情页

这是用户点击某篇文章后看到的详情页面,展示了完整的文章内容、作者信息以及相关文章推荐。

在这里插入图片描述

3️⃣管理员后台界面

Django提供了强大的管理后台功能,管理员可以通过这个界面对博客系统的所有内容进行管理,包括文章、用户、评论等。

在这里插入图片描述

4️⃣文章创建页面

这是用户创建新文章的界面,提供了直观的编辑器和相关选项,方便用户发布内容。

在这里插入图片描述

5️⃣用户个人主页

这是用户的个人中心页面,展示了用户的基本信息和已发布的文章列表,方便用户管理自己的内容。

在这里插入图片描述

通过以上界面效果图,我们可以看到我们的博客系统已经实现了一个功能完善的全栈应用:

  1. 博客首页:展示了文章列表、分类导航和热门标签,提供良好的浏览体验
  2. 文章详情页:清晰展示文章内容、作者信息和相关文章推荐,增强用户粘性
  3. 后台管理界面:提供强大的内容管理功能,方便管理员高效运营网站
  4. 文章创建页面:提供直观的编辑器和操作界面,降低内容创作门槛
  5. 用户个人中心:便于用户管理个人资料和已发布内容

这个系统基于我们在前面课程中学习的Django框架构建后端,使用Bootstrap实现响应式前端设计,SQLite/MySQL作为数据库存储。通过这个项目,我们将前面学习的各种技术点进行了综合运用,实现了从前端到后端的完整开发流程。

九、总结与进阶

我们成功构建了一个全栈博客系统,它具备完整的前后端功能和数据库交互。

1. 项目亮点

  • 完整的用户认证与授权
  • 响应式前端设计
  • RESTful API设计规范
  • 完善的数据库模型
  • 良好的代码组织结构
  • Docker容器化部署

2. 进阶方向

  • 添加用户通知系统
  • 集成Markdown编辑器
  • 实现文章搜索功能(ElasticSearch)
  • 添加文章统计分析
  • 优化网站性能(缓存、CDN等)
  • 实现CI/CD自动化部署

十、今日练习

现在,让我们开始实践吧!按照以下步骤完成今天的全栈项目开发练习:

1. 创建项目框架

   - 使用Django创建项目结构- 配置数据库连接- 设计数据模型

2. 实现核心功能

   - 用户认证系统- 文章CRUD操作- 评论系统- 前端页面设计

3. 优化和测试

   - 添加单元测试- 优化用户体验- 确保响应式设计

4. 部署项目

   - 准备部署环境- 配置服务器- 上线应用

挑战任务

尝试为博客系统添加以下高级功能中的一个或多个:

- 文章点赞系统
- 用户关注功能
- 文章订阅功能
- 图片上传与管理
- 站内搜索优化

参考资源

  1. Django官方文档: https://docs.djangoproject.com/
  2. Bootstrap文档:https://getbootstrap.com/docs/5.3/
  3. MDN Web文档:https://developer.mozilla.org/
  4. SQLite文档:https://www.sqlite.org/docs.html
  5. Git版本控制: https://git-scm.com/doc
  6. Docker容器化: https://docs.docker.com/

结语

通过这个全栈项目,我们已经将前面所学的Python基础Web开发数据库前后端分离等知识进行了综合应用。全栈开发需要不断实践和学习,希望这个项目能够帮助你巩固知识,并为你的Python学习之旅添加一份成就感!

记得将你的项目分享到GitHub,这不仅是对自己学习成果的展示,也是展示你的技能的好方式。

创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊)
如果你对今天的内容有任何问题,或者想分享你的学习心得,欢迎在评论区留言讨论!

http://www.xdnf.cn/news/303877.html

相关文章:

  • 基于 Flask的深度学习模型部署服务端详解
  • Linux 工具
  • docker + K3S + Jenkins + Harbor自动化部署
  • Opentack基础架构平台运维
  • iPhone或iPad想要远程投屏到Linux系统电脑,要怎么办?
  • react-12父子组件间的数据传递(子传父)(父传子)- props实现
  • Axure :列表详情、列表总数
  • Spring Boot 3.x集成SaToken使用swagger3+knife4j 4.X生成接口文档
  • 开源与商业:图形化编程工具的博弈与共生
  • ExtraMAME:复古游戏的快乐“时光机”
  • 信息论01:从通信到理论的飞跃
  • 第七章,VLAN技术
  • Github 2025-05-06Python开源项目日报 Top10
  • Kotlin与Java在Android生态中的竞争与互补关系
  • RT-Thread自用记录(暂定)
  • 第四章-初始化Direct3D
  • 餐饮部绩效考核管理制度与综合评估方法
  • 【java】程序设计基础 八股文版
  • 开放的力量:新零售生态的共赢密码
  • 每日算法-250506
  • weapp-vite - 微信小程序工具链的另一种选择
  • OpenGL超大分辨率图像显示
  • Windows玩游戏的时候,一按字符键就显示桌面
  • imapal sql优化之hint
  • Codeforces Round 1023 (Div. 2) (A-D)
  • USB学习【2】通讯的基础-反向不归零编码
  • 优势演员-评论家A2C详解:python从零实现
  • 【KWDB 创作者计划】一文掌握KWDB的时序表管理
  • 计算机中的逻辑运算
  • DVWA靶场保姆级通关教程--03CSRF跨站请求伪造