音乐播放器
Ericam_blog
 
Powered by Gridea | Theme: Fog
载入天数...
载入时分秒...
总访问量:  |   访问人数:

django开发个人博客系统

  热度: loading...

前言

    当初是在2018年写下的这篇文章,那时的django更新到了版本2.0,使用人数还是比较少的,网上的教程大多也很少。而现在随着python web框架的流行,越来越多人开始接触到了django这门技术。如今,django已经更新到了版本3.0,添加了支持异步等重要特性,但是这些特性和本篇文章基本无关。写下这篇文章的初衷是为了让刚接触django框架的童鞋可以入门上手一个项目,了解如何快速搭建一个项目,毕竟python web的优势便是快速易上手嘛【最近接触了springboot后的感触】。如今再次更新这篇博文(2020-2月),希望能够给予大家入门一定的帮助。
    
    
p.s:其实建立一个web项目需要处理的事情比较杂,我就按照我的建立习惯给大家写下这篇教程。
另外,本项目是我在重新写时完整重新搭建,所以一定可以跑通,我会写的比较仔细一点,如果你能耐心看完,必定有所收获。
【-------------------------原创不易,请勿侵权------------------------------------------------------------------】

项目预览

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
    
    

项目版本

运行平台:windows
Python版本:3.7
Django版本:3.0
数据库工具:sqlite
开发工具:Pycharm+vscode
依赖:pillow,django-simpleui,django_summernote
    
    

项目构思

    个人博客系统属于一个非常小型的项目,不会存在高并发的情况,同时注册用户主要也就是为了评论博客内容,其实用户账号安全性也可以完全不用考虑。项目采取前后端分离的形式进行开发,前后端信息交互多数采取ajax形式(按理说动态更新页面比较友好交互,但是为了让大家感受一下这两种方式,在登录这一块采取静态跳转)。剩下的部分一次性在这里写下来大家也不一定能看的很明白,在建立项目的过程中再给大家介绍。
    
    

项目实战

请先下载静态文件
下载连接:https://pan.baidu.com/s/1Er2S63MThOfzhlbuUkTEkw
之后替换相应的文件

我们首先给我们的项目起个名字:Ericam
    
(1)利用命令行创建项目。
    

django-admin startproject Ericam

    
(2)创建APP
    
解释一下:项目中会存在登录子系统,博文管理子系统等,这些子系统每个都可以作为一个app,如此分离方便日后开发维护。但是作为一个入门项目,便不如此麻烦了。我们在整个项目只建立一个app。
由于我们准备搭建的是一个博客系统,所以就给这个APP起名为:blog
    
在命令行下继续输入

python manage.py startapp blog

    
此时文件目录结构:
在这里插入图片描述
    
介绍一下各个文件的用处
在这里插入图片描述
    
删除test.py,新建一个urls.py文件
在这里插入图片描述
为什么需要两个urls.py文件呢?方便分层管理,类似于一级目录,二级目录。
    
    
(3)注册app并配置静态文件目录
在这里插入图片描述
    
在settings.py文件里添加如下内容
    

# 配置静态文件目录
STATICFILES_DIRS = [
        os.path.join(BASE_DIR, 'static'),
    ]

    
【静态文件:css,js和图片文件等,我们在这里配置了文件目录路径】
此时运行项目,用浏览器访问 http://127.0.0.1:8000/
在这里插入图片描述
出现一个小火箭代表我们的项目搭建成功了。
    
    
(4)
新建文件夹,如下所示(建议直接将static文件夹复制过来,本博客不会讲解css以及js):
在这里插入图片描述
(所有html文件存放于templates文件夹下)
新建index_unlog.html文件【作为首页,未登录时显示的页面】
我们先简单测试一下,给大家展示一下django如何通过view.py视图层显示html页面。
在index_unlog.html里:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>首页</title>
</head>
<body>
    个人博客系统测试
</body>
</html>

在views.py文件里:

def index_unlog(request):
    return render(request,'index_unlog.html')

最后我们添加一下路由地址,在与settings.py同级目录的urls.py:

urlpatterns = [
    path('admin/', admin.site.urls),
    path('blog/',include('blog.urls')),
    path('',views.index_unlog,name='index_unlog')
]

在新建的urls.py文件【以后我加个标号2代表该文件】里添加如下内容:

app_name = 'blog'
urlpatterns = [
]

ps:在pycharm里按alt+enter可以添加未引入的类包。
此时刷新项目,用浏览器访问 http://127.0.0.1:8000/

在这里插入图片描述

搞懂了每个文件的大致作用,我们便可以开始正式开发博客系统啦。
我们按照模块化进行开发。
    
    
    

登录子系统开发

(1)首页(未登录)-编写index_unlog.html
    

{% load static %}
<html lang="zh">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="author" content="Ericam_">

    <!-- CSS -->
	<title>Ericam_blog</title>
	<link rel="shortcut icon" href="{% static 'images/gt_favicon.png' %}">
	<link rel="stylesheet" media="screen" href="http://fonts.googleapis.com/css?family=Open+Sans:300,400,700">
	<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
	<link rel="stylesheet" href="{% static 'css/font-awesome.min.css' %}">
	<!-- Custom styles for our template -->
	<link rel="stylesheet" href="{% static 'css/bootstrap-theme.css'%}"  >
	<link rel="stylesheet" href="{% static 'css/log.css'%}">
	<link rel="stylesheet" href="{% static 'css/blog.css'%}">
	</head>


<body class="back">
  <!--导航栏-->
	<div class="navbar navbar-inverse navbar-fixed-top headroom" >
		<div class="container">
			<div class="navbar-header">
				<!-- Button for smallest screens -->
				<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"><span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button>
				<a class="navbar-brand"><img src="{%static 'css/images/lo.png'%}" alt="Progressus HTML5 template"></a>
			</div>
			<div class="nav navbar-nav navbar-right">
								<!--在这里填写登录提交  1-->
			</div>
		</div>
	</div>

	  <center>
    <div class="container" style="padding-top:300px;min-height:800px">
      <div class="row">,.
        <p class="lead"><font color="white"">个人博客系统,尽情的享用吧(〃'▽'〃)</font></p>
        <p class="tagline"><font color="white">如果您有优秀的建议,欢迎投递哦</font></p>
							<!--在这里填写登录提交  2-->
      </div>
    </div>
	</center>


   <footer id="footer" class="top-space">
		<div class="footer2">
			<div class="container">
				<p class="text-center">Copyright &copy; 2020, Ericam_blog</p>
			</div>
		</div>
  </footer>
</body>
</html>

第一句话{%load static%}代表引入静态文件(css,js等)
img,css,js等文件的引用与下述语句类似:

 <link rel="stylesheet" href="{%static 'css/log.css' %}">

此时打开浏览器查看效果:
在这里插入图片描述
    
    
(2)登录页-login.html
    

{%load static%}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
    <link rel="shortcut icon" href="{% static 'images/gt_favicon.png' %}">
    <link rel="stylesheet" href="{%static 'css/log.css' %}">
    <link rel="stylesheet" href="{%static 'css/semantic.css' %}">
    <link rel="stylesheet" href="{% static 'css/font-awesome.min.css' %}">
</head>
<body class="login">
    <div class="ui center aligned grid" style="margin-top: 200px">
        <div class="ui six wide column">
            <h1 class="ui teal header">Ericam blog-登录</h1>
            <div class="ui segment">
                <div class="ui content">
    <form class="ui large form" method="post" action="{%url 'login'%}">
      <div class="ui stacked segment">
        <div class="field">
          <div class="ui left icon input">
            <input type="text" name="username" placeholder="请输入用户名">
          </div>
        </div>
        <div class="field">
          <div class="ui left icon input">
            <input type="password" name="password" placeholder="请输入密码">
          </div>
        </div>
        {{ error }}<br>
        <a class="pull-right field" >忘记密码</a>
        <button class="ui fluid large teal button" type="submit">登陆</button>
      </div>

      <div class="ui error message"></div>
    </form>

    <div class="ui message">
      New to us? <a>注册</a>
    </div>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

    
添加路由信息:
    

urlpatterns = [
    path('admin/', admin.site.urls),
    path('blog/',include('blog.urls')),
    path('',views.index_unlog,name='index_unlog'),
    path('login',views.login,name='login')
]

    
添加视图层views.py内容:
    

def login(request):
    return render(request,'login.html')

    
此时预览:
    
在这里插入图片描述
    
在index_unlog.html里添加内容:

	<!--在这里填写登录提交  1-->
<a class="btn" href="{% url 'login' %}">登录 / 注册</a></li>

    <!--在这里填写登录提交  2-->
<p><a class="btn btn-action btn-lg" role="button" href="{% url 'login' %}">START NOW</a></p>

    
为了实现登录功能,我们首先需要构造一个用户表
    
打开models.py文件

from django.db import models
from django.contrib import  admin
from django.urls import reverse
from django.utils.timezone import now

# Create your models here.

class User(models.Model):
    username = models.CharField(max_length = 50)
    password = models.CharField(max_length = 200)
    nickname = models.CharField(max_length = 50,default='匿名')
    email = models.EmailField()
    created_time = models.CharField(max_length=50,default=now)
    comment_num = models.PositiveIntegerField(verbose_name='评论数', default=0)
    avatar = models.ImageField(upload_to = 'media', default="media/default.png")

    def __str__(self):
        return self.username

    def comment(self):
        self.comment_num += 1
        self.save(update_fields=['comment_num'])

    def comment_del(self):
        self.comment_num -= 1
        self.save(update_fields=['comment_num'])

class UserAdmin(admin.ModelAdmin):
    list_display = ('username','email')
#修饰器

然后打开命令行

python manage.py makemigrations
python manage.py migrate

这便生成了sqlite数据库文件
在这里插入图片描述
通过sqliteStudio打开浏览:
在这里插入图片描述
    
在views.py文件里编写登录逻辑

def login(request):
    if request.method == 'POST':
        user_name = request.POST.get('username','')
        pass_word = request.POST.get('password','')
        user = User.objects.filter(username=user_name)  #查看数据库里是否有该用户名
        if user:#如果存在
            user = User.objects.get(username = user_name)#读取该用户信息
            if pass_word==user.password:#检查密码是否匹配
                request.session['IS_LOGIN'] = True
                request.session['nickname'] = user.nickname
                request.session['username'] = user_name
                return render(request,'Home_log.html',{'user':user})
            else:
                return render(request,'login.html',{'error': '密码错误!'})
        else:
            return render(request, 'login.html', {'error': '用户名不存在!'})
    else:
        return render(request,'login.html')

因为我们需要记录cookies,所以我们打开settings.py文件,注释该语句
在这里插入图片描述
此时登录功能已经实现了。登录成功我们希望页面进行跳转,所以我们需要新建一个index.html

{% load static %}
<html lang="zh">
  <head>
    <meta charset="utf-8">
    <title>首页-Ericamblog</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="author" content="Ericam_">

    <!-- CSS -->
	<link rel="shortcut icon" href="{% static 'images/gt_favicon.png' %}">
	<link rel="stylesheet" media="screen" href="http://fonts.googleapis.com/css?family=Open+Sans:300,400,700">
	<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
	<link rel="stylesheet" href="{% static 'css/font-awesome.min.css' %}">
	<!-- Custom styles for our template -->
	<link rel="stylesheet" href="{% static 'css/bootstrap-theme.css'%}" media="screen" >
	<link rel="stylesheet" href="{% static 'css/blog.css' %}">
	<link rel="stylesheet" href="{% static 'css/log.css' %}">
</head>

<body class="back">
  <script src = "E:/bootstrap/bootstrap-3.3.7-dist/bootstrap-3.3.7-dist/js/jquery.min.js"></script>
  <!--导航栏-->
	<div class="navbar navbar-inverse navbar-fixed-top headroom" >
		<div class="container">
			<div class="container-fluid">
			  <div class="navbar-header">
				<!-- Button for smallest screens -->
				  <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"><span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button>
				  <a class="navbar-brand"><img src="{%static 'css/images/lo.png'%}" alt="Progressus HTML5 template"></a>
			  </div>

        <ul class="nav navbar-nav navbar-right">
            <li><a><font size="4" color="white">{{user.nickname}}</font></a></li>
            <li><a href="{%url 'index_unlog'%}"><i class="fa fa-sighout"></i><font size="4">注销</font></a></li>
        </ul>
    </div>
	</div>
</div>

	  <center>
    <div class="container" style="padding-top:300px;min-height:800px">
      <div class="row">,.
        <p class="lead"><font color="white"">Easy-Download,这里有好多好多资源喔,尽情的享用吧(〃'▽'〃)</font></p>
        <p class="tagline"><font color="white">如果您有优秀的资源,欢迎投递哦</font></p>
        <p><a class="btn btn-action btn-lg" role="button" href="#">START NOW</a></p>
      </div>
    </div>
	</center>

   <footer id="footer" class="top-space">
		<div class="footer2">
			<div class="container">
				<p class="text-center">Copyright &copy; 2020, Ericamblog</p>
			</div>
		</div>
  </footer>
</body>
</html>

views.py

def logsuccess(request):
    return render(request,'index.html')

添加路由信息

urlpatterns = [
    path('admin/', admin.site.urls),
    path('blog/',include('blog.urls')),
    path('',views.index_unlog,name='index_unlog'),
    path('login',views.login,name='login'),
    path('/log',views.logsuccess,name='login-success')
]

手动在数据库中添加一个用户信息
在这里插入图片描述
我们进行预览
在这里插入图片描述
    
    
完成了登录验证功能后,我们就需要添加注册功能,因为刚才用户的信息是我们手动在数据库内添加的,正常情况下应该是前端页面将内容发送给后端,后端经过处理存储在数据库内。
    
    
注册模块
    
    
(1)views.py

def register(request):
    if request.method =='POST':
        user_name = request.POST.get('username','')
        pass_word_1 = request.POST.get('password_1','')
        pass_word_2 = request.POST.get('password_2','')
        nick_name = request.POST.get('nickname','')
        email = request.POST.get('email','')
        avatar = request.FILES.get('avatar')
        if User.objects.filter(username = user_name):
            return render(request,'register.html',{'error':'用户已存在'})
            #将表单写入数据库
        if(pass_word_1 != pass_word_2):
            return render(request, 'register.html', {'error': '两次密码请输入一致'})
        user = User()
        if avatar:
            user.avatar = 'media/' + user_name + '.png'
            img = Image.open(avatar)
            size = img.size
            print(size)
            # 因为是要圆形,所以需要正方形的图片
            r2 = min(size[0], size[1])
            if size[0] != size[1]:
                img = img.resize((r2, r2), Image.ANTIALIAS)
            # 最后生成圆的半径
            r3 = int(r2/2)
            img_circle = Image.new('RGBA', (r3 * 2, r3 * 2), (255, 255, 255, 0))
            pima = img.load()  # 像素的访问对象
            pimb = img_circle.load()
            r = float(r2 / 2)  # 圆心横坐标
            for i in range(r2):
                for j in range(r2):
                    lx = abs(i - r)  # 到圆心距离的横坐标
                    ly = abs(j - r)  # 到圆心距离的纵坐标
                    l = (pow(lx, 2) + pow(ly, 2)) ** 0.5  # 三角函数 半径

                    if l < r3:
                        pimb[i - (r - r3), j - (r - r3)] = pima[i, j]
            img_circle.save('blog/static/media/'+user_name+'.png')
        user.username = user_name
        user.password = pass_word_1
        user.email = email
        user.nickname = nick_name
        user.save()
            #返回注册成功页面
        return render(request,'index_unlog.html')
    else:
        return render(request,'register.html')

    
    
代码含义很好理解,就是将前端提交的信息以user对象存储到数据库中,中间一部分代码是将用户提交的头像切割成圆形(无需理解)
    
    
(2)添加路由信息

urlpatterns = [
    path('admin/', admin.site.urls),
    path('blog/',include('blog.urls')),
    path('',views.index_unlog,name='index_unlog'),
    path('login',views.login,name='login'),
    path('log',views.logsuccess,name='login-success'),
    path('register',views.register,name='register')
]

(3)添加register.html

{%load static%}
<!DOCTYPE html>
<html lang="zh-hans">
<head>
    <meta charset="UTF-8">
    <title>注册</title>
    <link rel="shortcut icon" href="{% static 'images/gt_favicon.png' %}">
    <link rel="stylesheet" href="{%static 'css/log.css' %}">
    <link rel="stylesheet" href="{%static 'css/semantic.css' %}">
</head>
<body class="register">
    <div class="ui center aligned grid" style="margin-top: 200px">
        <div class="ui six wide column">
            <h1 class="ui teal header"><font color="black">EricamBlog-用户注册</font></h1>
            <div class="ui segment">
                <div class="ui content">
                    <form class="ui form" method="post" action="{%url 'register'%}"  enctype="multipart/form-data">
                        <div class="field">
                            <input type="text" name="username" placeholder="请输入用户名"><br>
                        </div>
                        <div class="field">
                            <input type="password" name="password_1" placeholder="请输入密码"><br>
                        </div>
                        <div class="field">
                            <input type="password" name="password_2" placeholder="请确认密码"><br>
                        </div>
                        <div class="field">
                            <input type="text" name="nickname" placeholder="请输入昵称"><br>
                        </div>
                        <div class="field">
                            <input type="text" name="email" placeholder="请输入邮箱"><br>
                        </div>
                        <div>头像<input type="file" name="avatar"></div>
                        {{ error }}<br>
                        <button class="ui fluid large teal button" type="submit">注册</button>
                    </form>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

    
    
现在注册模块已经完成了,但是刚才我们在编写登录页面时没有添加对于注册页面的超链接跳转,现在需要进行添加。
在login.html里修改如下内容:
在这里插入图片描述
此时注册功能已经完成,大家可以自行测试。
    
    
接下来我们再来添加忘记密码模块。
正常情况下,忘记密码时应该发送邮件给邮箱,邮箱确认后再填写新密码。这里为了简易操作,便忽略。
(1)添加views.py内容

def forget_password(request):
    if request.method == 'POST':
        user_name = request.POST.get('username','')
        email = request.POST.get('email','')
        user = User.objects.filter(username = user_name)
        if user:
            user = User.objects.get(username = user_name)
            if(user.email == email):
                request.session['user_name'] = user_name
                return render(request,'reset.html')
            else:
                return render(request,'forget.html',{'error':'您的用户名和邮箱不匹配!'})
        else:
            return render(request,'forget.html',{'error':'请输入正确的用户名'})
    else:
        return  render(request,'forget.html')

def reset(request):
    if request.method == 'POST':
        pass_word1 = request.POST.get('password1','')
        pass_word2 = request.POST.get('password2','')
        user_name = request.session['user_name']
        user = User.objects.get(username = user_name)
        if pass_word1 == pass_word2:
            user.password = pass_word1
            user.save()
            return render(request,'login.html')
        else:
            return render(request,'reset.html', {'error': '两次密码输入不一致!'})
    else:
        return render(request,'reset.html')

(2)添加forget.html和reset.html
forget.html

{%load static%}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>忘记密码</title>
    <link rel="stylesheet" href="{%static 'css/log.css' %}">
    <link rel="stylesheet" href="{%static 'css/semantic.css' %}">
</head>
<body class="forget">
    <div class="ui center aligned grid" style="margin-top: 200px">
        <div class="ui six wide column">
            <h1 class="ui teal header">Ericam blog-忘记密码</h1>
            <div class="ui segment">
                <div class="ui content">
                    <form class="ui form" method="post" action="{%url 'forget'%}">
                        <div class="field">
                            <input type="text" name="username" placeholder="请输入用户名"><br>
                        </div>
                        <div class="field">
                            <input type="text" name="email" placeholder="请输入邮箱"><br>
                        </div>
                        {{ error }}<br>
                        <button class="ui fluid large teal button" type="submit">下一步</button>
                    </form>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

    
    
reset.html

{%load static%}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>重置密码</title>
    <link rel="shortcut icon" href="{% static 'images/gt_favicon.png' %}">
    <link rel="stylesheet" href="{%static 'css/semantic.css' %}">
    <link rel="stylesheet" href="{%static 'css/log.css' %}">
</head>
<body class="reset">
    <div class="ui center aligned grid" style="margin-top: 200px">
        <div class="ui six wide column">
            <h1 class="ui teal header">Ericam blog-重置密码</h1>
            <div class="ui segment">
                <div class="ui content">
                    <form class="ui form" method="post" action="{%url 'reset'%}">
                        <div class="field">
                            <input type="password" name="password1" placeholder="请输入新密码"><br>
                        </div>
                        <div class="field">
                            <input type="password" name="password2" placeholder="请确认新密码"><br>
                        </div>
                        {{ error }}<br>
                        <button class="ui fluid large teal button" type="submit">确认修改</button>
                    </form>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

    
    
(3)添加路由

urlpatterns = [
    path('admin/', admin.site.urls),
    path('blog/',include('blog.urls')),
    path('',views.index_unlog,name='index_unlog'),
    path('login',views.login,name='login'),
    path('log',views.logsuccess,name='login-success'),
    path('register',views.register,name='register'),
    path('forget',views.forget_password,name='forget'),
    path('reset',views.reset,name='reset')
]

    
    
到了这里忘记密码功能已经完成,最后我们还需要添加一下超链接跳转
打开login.html文件,修改:
在这里插入图片描述
    
    
【至此,登录子系统已经全部完成】

后台系统

django默认自带后台管理系统
创建一个超级管理员账户:

python manage.py createsuperuser

访问:http://127.0.0.1:8000/admin/
在这里插入图片描述
接下来我们对后台进行美化

pip install django-simpleui
pip install django_summernote

然后在settings.py添加:

INSTALLED_APPS = [
    "simpleui",   #添加内容,一定要加在admin前
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',
    'django_summernote'#后台富文本
]

SUMMERNOTE_CONFIG = {
    # Using SummernoteWidget - iframe mode
    'iframe': True,  # or set False to use SummernoteInplaceWidget - no iframe mode

    # Using Summernote Air-mode
    'airMode': False,

    # Use native HTML tags (`<b>`, `<i>`, ...) instead of style attributes
    'styleWithSpan': False,

    # Change editor size
    'width': '80%',
    'height': '480',

    # Use proper language setting automatically (default)
    'lang': 'zh-CN',
}

刷新,重新进入admin页面:
在这里插入图片描述
给后台添加注册,方便管理数据内容:
修改admin.py文件

from django.contrib import admin
from blog.models import Article,User,Category,Tag,ArticleComment,Message
from django_summernote.admin import SummernoteModelAdmin

# Register your models here.
class PostAdmin(SummernoteModelAdmin):
    summernote_fields = ('content')  # 给content字段添加富文本
    list_display = ['article_id', 'title', 'created_time']
    search_fields = ['title']  # 搜索框
    list_filter = ['created_time']  # 过滤器

#ass ArticleAdmin(admin.ModelAdmin):
class CommentAdmin(admin.ModelAdmin):
    list_display = ['username', 'body', 'title']
    search_fields = ['title']  # 搜索框


admin.site.register(Article, PostAdmin)
admin.site.register(Category)
admin.site.register(Tag)
admin.site.register(User)
admin.site.register(ArticleComment,CommentAdmin)

修改models.py文件(完整版):

from django.db import models
from django.contrib import  admin
from django.urls import reverse
from django.utils.timezone import now


#---------------------------------用户---------------------------------------
class User(models.Model):
    username = models.CharField(max_length = 50)
    password = models.CharField(max_length = 200)
    nickname = models.CharField(max_length = 50,default='匿名')
    email = models.EmailField()
    created_time = models.CharField(max_length=50,default=now)
    comment_num = models.PositiveIntegerField(verbose_name='评论数', default=0)   #评论数
    avatar = models.ImageField(upload_to = 'media', default="media/default.png")  #用户头像

    def __str__(self):
        return self.username

    def comment(self):
        self.comment_num += 1
        self.save(update_fields=['comment_num'])

    def comment_del(self):
        self.comment_num -= 1
        self.save(update_fields=['comment_num'])

#---------------------------------文章评论---------------------------------------
class ArticleComment(models.Model):
    body = models.TextField()
    username = models.CharField(max_length=50)
    userimg = models.CharField(max_length=70)
    nickname = models.CharField(max_length=50,default="匿名")
    createtime = models.DateTimeField(verbose_name='创建时间', default=now)
    article = models.CharField(max_length=50)
    title = models.CharField(max_length=50)
    # 使对象在后台显示更友好
    def __str__(self):
        return self.article

    class Meta:
        ordering = ['-createtime']
        verbose_name = '评论'  # 指定后台显示模型名称
        verbose_name_plural = '评论列表'  # 指定后台显示模型复数名称
        db_table = "comment"  # 数据库表名

    list_display = ('article', 'body')

#---------------------------------博客文章标签---------------------------------------
class Tag(models.Model):
    name = models.CharField(verbose_name='标签名', max_length=64)

    # 使对象在后台显示更友好
    def __str__(self):
        return self.name

    class Meta:
        ordering = ['name']
        verbose_name = '标签名称'  # 指定后台显示模型名称
        verbose_name_plural = '标签列表'  # 指定后台显示模型复数名称
        db_table = "tag"  # 数据库表名

#---------------------------------博客文章分类---------------------------------------
class Category(models.Model):
    name = models.CharField(verbose_name='类别名称', max_length=64)

    class Meta:
        ordering = ['name']
        verbose_name = "类别名称"
        verbose_name_plural = '分类列表'
        db_table = "category"  # 数据库表名

    # 使对象在后台显示更友好
    def __str__(self):
        return self.name

#---------------------------------博客文章---------------------------------------
class Article(models.Model):
    STATUS_CHOICES = (
        ('d', '草稿'),
        ('p', '发表'),
    )
    article_id = models.CharField(verbose_name='标号', max_length=100)
    title = models.CharField(verbose_name='标题', max_length=100)
    content = models.TextField(verbose_name='正文', blank=True, null=True)
    status = models.CharField(verbose_name='状态', max_length=1, choices=STATUS_CHOICES, default='p')
    views = models.PositiveIntegerField(verbose_name='浏览量', default=0)
    created_time = models.DateTimeField(verbose_name='创建时间', default=now)
    category = models.ForeignKey(Category, verbose_name='分类', on_delete=models.CASCADE, blank=False, null=False)
    tags = models.ManyToManyField(Tag, verbose_name='标签集合', blank=True)

    # 使对象在后台显示更友好
    def __str__(self):
        return self.title

    # 更新浏览量
    def viewed(self):
        self.views += 1
        self.save(update_fields=['views'])

    # 下一篇
    def next_article(self):  # id比当前id大,状态为已发布,发布时间不为空
        return Article.objects.filter(id__gt=self.id, status='p', pub_time__isnull=False).first()

    # 前一篇
    def prev_article(self):  # id比当前id小,状态为已发布,发布时间不为空
        return Article.objects.filter(id__lt=self.id, status='p', pub_time__isnull=False).first()

    class Meta:
        ordering = ['-created_time']  # 按文章创建日期降序
        verbose_name = '文章'  # 指定后台显示模型名称
        verbose_name_plural = '文章列表'  # 指定后台显示模型复数名称
        db_table = 'article'  # 数据库表名
        get_latest_by = 'created_time'

记得使用python manage.py migrate进行生成数据库文件。
此时访问后台:
在这里插入图片描述
写文章时富文本工具也加载了出来:
在这里插入图片描述

完整项目获取

重构了代码,同时更新了博客内容,关键难点已经写下来了。
如果有问题欢迎在评论区提问。
如果想要获取完整项目,请扫码赞助该项目。
获取方式:扫码赞助,留言邮箱地址。
在这里插入图片描述

尾言

其他子模块和该模块大同小异,暂时就不写了。
其中评论提交和删除采取的是ajax交互形式。
如果大家看了觉得有帮助请点赞,为了大家重新写了这篇文章,同时重构了代码,确保一定能跑通。