您的位置:首页 > 房产 > 建筑 > 【Django】开发个人博客系统【1】

【Django】开发个人博客系统【1】

2024/10/5 14:43:34 来源:https://blog.csdn.net/mucheng771/article/details/139236233  浏览:    关键词:【Django】开发个人博客系统【1】

使用Django开发个人博客系统,博客系统包括用户(博主)注册和登录、博主资料信息、图片墙功能、留言板功能、文章列表、文章正文内容和Admin后台系统。

项目架构设计

在这里插入图片描述

在这里插入图片描述

功能配置

下一步将上述设置写入Django的配置文件settings.py,当Django运行的时候能自动加载相应的功能应用。
将项目应用account、album、article和interflow写入配置属性INSTALLED_APPS,并在配置属性MIDDLEWARE中添加中间件LocaleMiddleware,使Admin后台系统支持中文语言,配置代码如下:

# myblog的settings.py
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','article','album','account','interflow',
]
MIDDLEWARE = ['django.middleware.security.SecurityMiddleware','django.contrib.sessions.middleware.SessionMiddleware',# 添加中间件LocaleMiddleware'django.middleware.locale.LocaleMiddleware','django.middleware.common.CommonMiddleware','django.middleware.csrf.CsrfViewMiddleware','django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware',
]

然后连接sql server数据库:
具体连接参考其他博客。

pip install pymssqlpip install mssql-django==1.2
pip install pyodbc django-pyodbc-azure

配置:

DATABASES = {'default': {'ENGINE': 'sql_server.pyodbc','NAME': 'blogdb',  # 数据库名称'HOST': '127.0.0.1',  # 数据库地址,本机 ip 地址 127.0.0.1'PORT': 1433,  # 端口'USER': 'bloger',  # 数据库用户名'PASSWORD': '123456',  # 数据库密码'OPTIONS': {'driver': 'ODBC Driver 17 for SQL Server',},}
}
# account的models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class MyUser(AbstractUser):name=models.CharField('姓名',max_length=50,default='匿名用户')introduce = models.TextField('简介', default='暂无介绍')company=models.CharField('公司',max_length=100,default='暂无信息')profession=models.CharField('职业',max_length=100,default='暂无信息')address=models.CharField('住址',max_length=100,default='暂无信息')telephone=models.CharField('电话',max_length=11,default='暂无信息')wx = models.CharField('微信', max_length=50, default='暂无信息')qq = models.CharField('QQ', max_length=50, default='暂无信息')wb = models.CharField('微博', max_length=100, default='暂无信息')photo=models.ImageField('头像',blank=True,upload_to='images/user/')# 设置返回值def __str__(self):return self.name

继续配置:

# 配置自定义用户模型MyUser
AUTH_USER_MODEL = 'account.MyUser'
STATICFILES_DIRS = [BASE_DIR / 'publicStatic']
# 设置媒体资源的保存路径
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'

项目应用album使用模型AlbumInfo存储图片墙的图片信息,它设有外键字段关联模型MyUser,与模型MyUser组成一对多的数据关系,使每个用户(博主)的图片墙只能显示自己上传的图片信息。我们在项目应用album的models.py中定义模型AlbumInfo:

# album 的models.py
from django.db import models
from account.models import MyUserclass AlbumInfo(models.Model):id = models.AutoField(primary_key=True)user = models.ForeignKey(MyUser, on_delete=models.CASCADE,verbose_name='用户')title = models.CharField('标题',max_length=50,blank=True)introduce = models.CharField('描述',max_length=200,blank=True)photo = models.ImageField('图片',blank=True,upload_to='images/album/')def __str__(self):return str(self.id)class Meta:verbose_name = '图片墙管理'verbose_name_plural ='图片墙管理'

项目应用article实现用户(博主)的文章管理,每篇文章设有分类标签、正文内容和评论信息,三者分别对应模型ArticleTag、ArticleInfo和Comment,每个模型之间的数据关系说明如下:
(1)模型ArticleTag设有外键字段关联模型MyUser,与模型MyUser组成一对多的数据关系。
(2)模型ArticleInfo不仅与模型MyUser组成一对多的数据关系,并且与模型ArticleTag组成多对多的数据关系。
(3)模型Comment只对模型ArticleInfo组成一对多的数据关系。

# article 的models.pyfrom django.db import models
from django.utils import timezone
from account.models import MyUserclass ArticleTag(models.Model):id = models.AutoField(primary_key=True)tag = models.CharField('标签',max_length=500)user = models.ForeignKey(MyUser,on_delete=models.CASCADE,verbose_name='用户')def __str__(self):return self.tagclass Meta:verbose_name = '博文分类'verbose_name_plural ='博文分类'class ArticleInfo(models.Model):author = models.ForeignKey(MyUser,on_delete=models.CASCADE,verbose_name='用户')title = models.CharField('标题',max_length=200)content = models.TextField('内容')articlephoto = models.ImageField('文章图片',blank=True,upload_to='images/article/')reading =models.IntegerField('阅读量',default=0)liking = models.IntegerField('点赞量',default=0)created = models.DateTimeField('创建时间',default=timezone.now)updated = models.DateTimeField('更新时间',auto_now=True)article_tag = models.ManyToManyField(ArticleTag,blank=True,verbose_name='文章标签')def __str__(self):return self.titleclass Meta:verbose_name = '博文管理'verbose_name_plural = '博文管理'class Comment(models.Model):article = models.ForeignKey(ArticleInfo,on_delete=models.CASCADE,verbose_name='所属文章')commentor = models.CharField('评论用户',max_length=90)content = models.TextField('评论内容')created = models.DateTimeField('创建时间',auto_now_add=True)def __str__(self):return self.article.titleclass Meta:verbose_name = '评论管理'verbose_name_plural = '评论管理'

项目应用interflow使用模型Board存储留言板信息,它与模型MyUser组成一对多的数据关系,从而区分每个用户(博主)的留言板信息。在项目应用interflow的models.py中定义模型Board,定义过程如下:

# interflow的 models.pyfrom django.db import models
from django.utils import timezone
from account.models import MyUserclass Board(models.Model):id = models.AutoField(primary_key=True)name = models.CharField('留言用户',max_length=50)email = models.CharField('邮箱地址',max_length=50)content = models.CharField('留言内容',max_length=500)created = models.DateTimeField('创建时间',default=timezone.now)user = models.ForeignKey(MyUser,on_delete=models.CASCADE,verbose_name='用户')def __str__(self):return self.emailclass Meta:verbose_name = '博客留言'verbose_name_plural = '博客留言'
# 根据models.py生成相关的.py文件,该文件用于创建数据表
python manage.py makemigrations# 创建数据表
python manage.py migrate

刷新:
在这里插入图片描述

接下来定义路由列表:

from django.contrib import admin
from django.urls import path, include,re_path
from django.views.static import serve
from django.conf import settings
urlpatterns = [# Admin 后台系统path('admin/', admin.site.urls),# 用户注册和登录path('user/',include('account.urls')),# 博客文章path('',include('article.urls')),# 图片墙path('ablum/',include('album.urls')),# 留言板path('board/',include('interflow.urls')),# 配置媒体资源的路由信息re_path('media/(?P<path>.*)',serve,{'document_root':settings.MEDIA_ROOT},name='media'),]

然后编写共用模板:

<html>
<head>
{% load staticfiles %}<title> {% block title %} 我的博客 {% endblock %}</title> 
<link href="{% static "css/base.css" %}" rel="stylesheet">
<link href="{% static "css/index.css" %}" rel="stylesheet">
<link href="{% static "css/m.css" %}" rel="stylesheet">
<link href="{% static "css/info.css" %}" rel="stylesheet">
<script src="{% static "js/jquery.min.js" %}"></script>
<script src="{% static "js/comm.js" %}"></script>
<script src="{% static "js/modernizr.js" %}"></script>
<script src="{% static "js/scrollReveal.js" %}"></script>
</head>
<body>
<header class="header-navigation" id="header"><nav><div class="logo"><a href="javascript:;">博客首页</a></div><h2 id="mnavh"><span class="navicon"></span></h2><ul id="startlist"><li><a href="{% url 'article' id 1 %}">我的日记</a></li><li><a href="{% url 'album' id 1 %}">我的相册</a></li><li><a href="{% url 'about' id  %}">关于我</a></li><li><a href="{% url 'board' id 1 %}">留言</a></li><li><a href="{% url 'admin:index' %}">博客后台管理</a></li></ul></nav>
</header>
{% block body %}
<article>
<aside class="l_box"><div class="about_me"><h2>关于我</h2><ul>{% if user.photo %}<i><img src="{{ user.photo.url }}"></i>{% else %}<i><img src="{% static 'image/user.jpg' %}"></i>{% endif %}<p><b>{{ user.name }}</b>,{{ user.introduce }}</p></ul></div>
<div class="wdxc"><h2>我的相册</h2><ul>{% for a in album %}<li><a href="javascript:;"></a><img src="{{ a.photo.url }}"></li>{% endfor %}</ul>
</div>
<div class="fenlei"><h2>文章分类</h2><ul>{% for t in tag %}<li><a href="/"></a></li>{% endfor %}</ul>
</div>
<div class="tuijian"><h2>站长推荐</h2><ul><li><a href="https://gitbook.cn/gitchat/author/5b28bff00ac95342c847f199" target="new">个人专题</a></li><li><a href="https://item.jd.com/12460562.html" target="new">玩转django2.0-京东</a></li><li><a href="http://product.dangdang.com/25535043.html" target="new">玩转django2.0-当当网</a></li></ul>
</div>
<div class="links"> <h2>友情链接</h2><ul><a href="https//blog.csdn.net/HuangZhang_123" target="new">CSDN博客</a><a herf="https://book.jd.com/writer">出版图书</a></ul>
</div>
</aside>
{% endblock %}
{% block content %} {% endblock %}
</article>
<a href="#"class="cd-top">Top</a>
{% block script %} {% endblock %}
</body>
</html>

在项目应用account的urls.py中定义用户注册与登录的路由信息,分别命名为路由register和userLogin,两者的HTTP请求处理由视图函数register和userLogin执行,代码如下:

# account的urls.py
from django.urls import path
from .views import *
urlpatterns = [# 用户注册path('register.html', register, name='register'),# 用户登录path('login.html', userLogin, name='userLogin'),
]

打开项目应用account的views.py定义视图函数register和userLogin,功能代码如下:

from django.shortcuts import render,redirect
from .models import MyUser
from django.contrib.auth import authenticate
from django.contrib.auth import login,logout
from django.urls import reversedef register(request):title='注册博客'pageTitle='用户注册'confirmPassword = TrueurlText = '注册'urlName = 'userLogin'if request.method == 'POST':u = request.POST.get('username','')p = request.POST.get('password','')cp = request.POST.get('cp','')if MyUser.objects.filter(username=u):tips = '用户已存在'elif cp != p:tips = '两次密码输入不一致'else:d={'username':u,'password':p,'is_superuser':1,'is_staff':1}user = MyUser.objects.create_user(**d)user.save()tips = '注册成功,请登录'logout(request)return redirect(reverse('userLogin'))return render(request,'user.html',locals())def userLogin(request):title='登录博客'pageTitle='用户登录'button ='登录'urlText = '用户注册'urlName = 'register'if request.method == 'POST':u = request.POST.get('username','')p = request.POST.get('password','')if MyUser.objects.filter(username=u):user = authenticate(username=u,password=p)if user:if user.is_active:login(request,user)kwargs = {'id':request.user.id,'page':1}return redirect(reverse('article',kwargs=kwargs))else:tips = '账号密码错误,请重新输入'else:tips = '用户不存在,请注册'else:if request.user.username:kwargs = {'id':request.user.id,'page':1}return redirect(reverse('article',kwargs=kwargs))return render(request,'user.html',locals())

视图函数register和userLogin都是使用模板文件user.html生成不同的网页内容。打开模板文件夹templates的user.html并编写用户注册和登录的页面内容,代码如下:

<!DOCTYPE html>
<html>
<head>{% load staticfiles %}<title>{{ title }}</title><link rel="stylesheet" href="{% static "css/reset.css"  %}"><link rel="stylesheet" href="{% static "css/user.css"  %}"><script src="{% static "js/jquery.min.js" %}"></script><script src="{% static "js/user.js" %}"></script>
</head>
<body>
<div class="page"><div class="loginwarrp"><div class="logo">{{ pageTitle }}</div><div class="login_form"><form name="Login" method="post" action="">{% csrf_token %}<li class="login-item"><span>用户名:</span><input type="text" name="username" class="login_input"><span id="count-msg" class="error"></span></li><li class="login-item"><span>密码:</span><input type="password" name="password" class="login_input"><span id="password-msg" class="error"></span></li>{% if confirmPassword %}<li class="login-item"><span>确认密码:</span><input type="password" name="cp" class="login_input" ><span id="password-msg" class="error"></span></li>{% endif %}<div>{{ tips }}</div><li class="login-sun"><input type="submit" name="Submit" value="{{ button }}"><div class="turn-url"><a style="color: #45B572;"href="{% url urlName %}">>>{{ urlText }}</a></div></li></form></div></div>
</div>
<script type="text/javascript">window.onload = function (){var config={vx : 4,vy : 4,height:2,width:2,count:100,color:"121,162,185",stroke:"100,200,180",dist:6000,e_dist:20000,max_conn:10}CanvasParticle(config);}
</script>
<script src="{% static "js/canvas-particle.js" %}"></script>
</body>
</html>

博主资料信息使用模板文件about.html生成网页内容,用户信息来自模型MyUser,在项目应用account中实现博主资料信息的网页功能。我们已在项目应用account的urls.py中定义了路由register和userLogin,两者分别实现用户注册和用户登录功能。首先在项目应用account的urls.py中新增路由about,代码如下:

# account的urls.py
from django.urls import path
from .views import *
urlpatterns = [# 用户注册path('register.html', register, name='register'),# 用户登录path('login.html', userLogin, name='userLogin'),# 关于我path('about/<int:id>.html', about, name='about'),
]

博客系统的所有页面(除了用户注册和登录页面之外)都需要设置路由变量id,这样便于区分不同用户的博客网站。
视图函数about接收并处理路由about的HTTP请求,我们在account的views.py中定义视图函数about,函数代码新增如下:

# account的views.py
from album.models import AlbumInfo
from article.models import ArticleTag
from django.shortcuts import render
def about(request, id):album = AlbumInfo.objects.filter(user_id=id)tag = ArticleTag.objects.filter(user_id=id)user = MyUser.objects.filter(id=id).first()return render(request, 'about.html', locals())

共用模板文件base.html设置了博客系统的网页架构,我们只需继承并重写模板文件base.html的接口即可实现博主资料信息页。
博主资料信息页只重写模板文件base.html的content接口,其他的模板继承接口使用默认值即可,在模板文件about.html中编写以下代码:

{% extends "base.html" %}
{% block content %}
<main class="r_box"><div class="about"><div>{{ user.introduce }}</div><br><hr><br><h2>我的资料</h2><br><p>名字:{{ user.name }}</p><br/><p>微博:{{ user.wb }}</p><br/><p>微信:{{ user.wx }}</p><br/><p>Q Q:{{ user.qq}}</p></div>
</main>{% endblock %}

接下来,写照片墙功能:

# album的urls.py
from django.urls import path
from .views import *
urlpatterns = [# 图片墙path('<int:id>/<int:page>.html', album, name='album'),
]

路由album设置路由变量id和page,路由变量id代表模型MyUser的主键id,它可获取某个用户(博主)的博客信息;路由变量page代表所有图片分页后的某一页的页数,它可获取不同页数的图片信息

在album的views.py中定义视图函数album,函数代码如下:

from django.shortcuts import render
from django.core.paginator import Paginator, PageNotAnInteger,EmptyPage
from .models import AlbumInfodef album(request,id,page):albumList = AlbumInfo.objects.filter(user_id=id).order_by('id')paginator = Paginator(albumList,8)try:pageInfo = paginator.page(page)except PageNotAnInteger:# 如果参数page的数据结构不是整型,就返回第一页数据pageInfo = paginator.page(1)except EmptyPage:# 如果用户访问的页数大于实际页数,则返回最后一页的数据pageInfo = paginator.page(paginator.num_pages)return render(request,'ablum.html'.locals())
# templates 的album.html
{% extends "base.html" %}
{% block body %} {% endblock %}
{% block content %}
<article>
<div class="picbox">
{% for list in pageInfo.object_list %}<div class="picvalue" data-scroll-reveal="enter bottom over 1s"><a href="javascript:;"><i><img src="{{ list.photo.url }}"></i><div class="picinfo">{% if list.title %}<h3>{{ list.title }}</h3>{% else %}<h3>相册图片</h3>{% endif %}{% if list.introduce %}<span>{{ list.introduce }}</span>{% else %}<span>图片简介</span>{% endif %}</div></a></div>
{% endfor %}
</div>
<!--分页功能-->
<div class="pagelist">{% if pageInfo.has_previous %}<a href="{% url 'album' id pageInfo.previous_page_number %}">上一页</a>{% endif %}{% for page in pageInfo.paginator.page_range %}{% if pageInfo.number == page %}<a href="javascript:;" class="curPage">{{ page }}</a>{% else %}<a href="{% url 'album' id page %}">{{ page }}</a>{% endif %}{% endfor %}{% if pageInfo.has_next %}<a href="{% url 'album' id pageInfo.next_page_number %}">下一页</a>{% endif %}
</div>
</article>
{% endblock %}{% block script %}
<script>if (!(/msie [6|7|8|9]/i.test(navigator.userAgent))){(function (){window.scrollReveal = new scrollReveal({reset:true});})();};
</script>
{% endblock %}

留言板功能在项目应用interflow中实现,网页功能包括:留言信息展示和留言提交。我们在项目应用interflow的urls.py中定义留言板的路由信息。打开interflow的urls.py定义路由board,代码如下:

# interflow的urls.py
from django.urls import path
from .views import *
urlpatterns = [# 留言板path('<int:id>/<int:page>.html', board, name='board'),
]
# interflow的views.py
from django.shortcuts import render,redirect
from django.core.paginator import Paginator,PageNotAnInteger,EmptyPage
from article.models import ArticleTag
from account.models import MyUser
from album.models import AlbumInfo
from .models import Board
from django.urls import reversedef board(request,id,page):album = AlbumInfo.objects.filter(user_id=id)tag = ArticleTag.objects.filter(user_id=id)user = MyUser.objects.filter(id=id).first()if not user:return redirect(reverse('register'))if request.method == "GET":boardList = Board.objects.filter(user_id=id).order_by('-created')paginator = Paginator(boardList,10)try:pageInfo = paginator.page(page)except PageNotAnInteger:pageInfo = paginator.page(1)except EmptyPage:pageInfo = paginator.page(paginator.num_pages)return render(request,'board.html',locals())else:name = request.POST.get('name')email = request.POST.get('email')content = request.POST.get('content')value = {'name':name,'email':email,'content':content,'user_id':id}Board.objects.create(**value)kwargs = {'id':id,'page':1}return redirect(reverse('board',kwargs=kwargs))
{% extends "base.html" %}
{% load staticfiles %}
{% block content %}
<main class="r_box"><div class="gbook">{% for list in pageInfo.object_list %}<div class="fb"><ul style="background: url({% static "image/user.jpg" %}) no-repeat top 20px left 10px;"><p class="fbtime"><span>{{ list.created|data:"Y-m-d" }}</span>{{ list.name }}</p><p class="fbinfo">{{ list.content }}</p></ul></div>{% endfor %}<!--分页功能--><div class="pagelist">{% if pageInfo.has_previous %}<a href="{% url 'board' id pageInfo.previous_page_number %}">上一页</a>{% endif %}{% if pageInfo.object_list %}{% for page in pageInfo.paginator.page_range %}{% if pageInfo.number == page %}<a href="javascript:;" class="curPage">{{ page }}</a>{% else %}<a href="{% url 'board' id page %}">{{ page }}</a>{% endif %}{% endfor %}{% endif %}{% if pageInfo.has_next %}<a href="{% url 'board' id pageInfo.next_page_number %}">下一页</a>{% endif %}</div><hr><!--网页表单--><div class="gbox"><form action="" method="post" name="saypl" onsubmit="return CheckPl(document.saypl)">{% csrf_token %}<p><strong>来说点什么吧...</strong></p><p><span>您的姓名:</span><input name="name" type="text" id="name">*</p><p><span>联系邮箱:</span><input name="email" type="text" id="email">*</p><p><span class="tnr">留言内容:</span><textarea name="content" cols="60" rows="12" id="lytext"></textarea></p><p><input type="submit" name="Submit3" value="提交"></p></form></div></div>
</main>
{% endblock %}<!--模板重写-->
{% block script %}
<script>function CheckPl(obj){if(obj.lytext.value==""){alert("请写下您想说的话!");obj.lytext.focus();return false;}if(obj.name.value==""){alter("请写下您的名字!");obj.name.focus();return false;}if(obj.email.value==""){alter("请写下您的邮箱地址!");obj.email.focus();return false;}return true;}
</script>
{% endblock %}

文章列表以模板文件base.html作为网页框架,它将当前用户(博主)的所有文章进行分页显示。我们在项目应用article中实现文章列表的功能开发,首先在article的urls.py中定义文章列表的路由article,代码如下:

# article的urls.py
from django.urls import path
from django.views.generic import RedirectView
from .views import *
urlpatterns = [# 首页地址自动跳转到用户登录页面path('', RedirectView.as_view(url='user/login.html')),# 文章列表path('<int:id>/<int:page>.html', article, name='article'),
]

路由article由视图函数article负责接收和处理HTTP请求,下一步在项目应用article的views.py中定义视图函数article,代码如下:

from django.shortcuts import render,redirect
from account.models import MyUser
from album.models import AlbumInfo
from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger
from .models import ArticleInfo,ArticleTag
from django.urls import reversedef article(request,id,page):album = AlbumInfo.objects.filter(user_id=id)tag = ArticleTag.objects.filter(user_id=id)user = MyUser.objects.filter(id=id).first()if not user:return redirect(reverse('register'))ats = ArticleInfo.objects.filter(user_id=id).order_by('-created')paginator = Paginator(ats,10)try:pageInfo = paginator.page(page)except PageNotAnInteger:pageInfo = paginator.page(1)except EmptyPage:pageInfo = paginator.page(paginator.num_pages)return render(request,'article.html',locals())
# templates 的article.html
{% extends "base.html" %}
{% load staticfiles%}
{% block content %}
<main class="r_box">
{% for list in pageInfo.object_list %}
<li><i><a href="{% url 'detail' id list.id %}">{% if list.articlephoto %}<img src="{% static 'images/pic.png' %}">{% endif %}</a></i><h3><a href="{% url 'detail' id list.id %}">{{ list.title }}</a></h3><p>{{ list.content|safe }}</p>
</li>
{% endfor %}
<div class="pagelist">{% if pageInfo.has_previous %}<a href="{% url 'article' id pageInfo.previous_page_number %}">上一页</a>{% endif %}{% if pageInfo.object_list %}{% for page in pageInfo.paginator.page_range %}{% if pageInfo.number==page %}<a href="javascript:;" class="curPage">{{ page }}</a>{% else %}<a href="{% url 'article' id page %}">{{ page }}</a>{% endif %}{% endfor %}{% endif %}{% if pageInfo.has_next %}<a href="{% url 'article' id pageInfo.next_page_number %}">下一页</a>{% endif %}
</div>
</main>
{% endblock %}

文章正文内容显示文章的标签、阅读量、发布时间、作者、文章内容和评论内容,我们在项目应用article中实现文章正文内容的功能开发,在article的urls.py中定义添加路由detail,代码如下:

# article的urls.py
from django.urls import path
from django.views.generic import RedirectView
from .views import *
urlpatterns = [# 首页地址自动跳转到用户登录页面path('', RedirectView.as_view(url='user/login.html')),# 文章列表path('<int:id>/<int:page>.html', article, name='article'),# 文章正文内容path('detail/<int:id>/<int:aId>.html', detail, name='detail')
]
# article的views.py
from django.shortcuts import render,redirect
from account.models import MyUser
from album.models import AlbumInfo
from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger
from .models import ArticleInfo,ArticleTag,Comment
from django.db.models import F
from django.urls import reversedef article(request,id,page):album = AlbumInfo.objects.filter(user_id=id)tag = ArticleTag.objects.filter(user_id=id)user = MyUser.objects.filter(id=id).first()if not user:return redirect(reverse('register'))ats = ArticleInfo.objects.filter(user_id=id).order_by('-created')paginator = Paginator(ats,10)try:pageInfo = paginator.page(page)except PageNotAnInteger:pageInfo = paginator.page(1)except EmptyPage:pageInfo = paginator.page(paginator.num_pages)return render(request,'article.html',locals())def detail(request,id,aId):album = AlbumInfo.objects.filter(user_id=id)tag = ArticleTag.objects.filter(user_id=id)user = MyUser.objects.filter(id=id).first()if request.method=='GET':ats = ArticleInfo.objects.filter(id=aId).first()atags = ArticleTag.objects.get(id=aId).article_tag.all()cms = Comment.objects.filter(article_id=aId).order_by('-created')# 添加阅读量if not request.session.get('reading'+str(id)+str(aId),''):reading = ArticleInfo.objects.filter(id=aId)reading.update(reading=F('reading')+1)request.session['reading'+str(id)+str(aId)] = Truereturn render(request,'detail.html',locals())else:commentator = request.POST.get('name')email = request.POST.get('email')content = request.POST.get('content')value = {'commentator':commentator,'content':content,'article_id':aId}Comment.objects.create(**value)kwargs = {'id':id,'aId':aId}return redirect(reverse('detail',kwargs=kwargs))
# templates 的detail.html
{% extends "base.html" %}
{% load staticfiles %}
<!--模板重写-->
{% block content %}
<main><div class="infosbox"><div class="newsview"><!--文章标题--><h3 class="news_title">{{ ats.title }}</h3><!--作者 发布时间 阅读量--><div class="bloginfo"><ul><li class="author">作者:<a href="javascript:;">{{ user.name }}</a></li><li class="timer">时间:{{ ats.created|date:"Y-m-d" }}</li><li class="view">阅读量:{{ ats.reading }}</li></ul></div><!--文章标签--><div class="tags">{% for t in atags %}<a href="javascript:;">{{ t }}</a>{% endfor %}</div><!--正文内容--><div class="news_con">{{ ats.content|safe }}</div></div><div class="news_pl"><h2>文章评论</h2><div class="gbko"><!--评论展示-->{% for c in cms %}<div class="fb"><ul style="background: url({% static "images/user.jpg" %}) no-repeat top 20px left 10px;"><p class="fbtime"><span>{{ c.created|date:"Y-m-d" }}</span>{{ c.commentator }}</p></ul></div>{% endfor %}<!--提交评论的网页表单--><form action="" method="post" name="saypl" onsubmit="return CheckPl(document.saypl)"><div id="plpost">{% csrf_token %}<p class="saying"><span><a href="javascript:;">共有{{ cms|length }}条评论</a></span>来说两句吧...</p><p class="yname"><span>名称:</span><input name="name" id="name" type="text" class="inputText" size="16"></p><textarea name="content" row="6" id="saytext"></textarea><inpu name="submit" type="submit" value="提交"></inpu></div></form></div></div></div>
</main>
{% endblock %}
{% block script %}
<script>function CheckPl(obj){if(obj.saytext.value==""){alter("请写下您想说的话!");obj.saytext.focus();return false;}if(obj.name.value==""){alter("请写下您的名字!");obj.name.focus();return false;}return true;}
</script>
{% endblock %}

项目的模型存储了所有用户的数据信息,但每个用户登录Admin后台系统只能管理自己的数据信息,因此每个模型的ModelAdmin必须重写方法formfield_for_foreignkey()和get_queryset()。

每个模型的ModelAdmin设置了属性list_display,并且重写类方法get_queryset()和formfield_for_foreignkey(),具体说明如下:
(1)list_display:在模型的数据列表页设置显示的模型字段。
(2)get_queryset():在模型的数据列表页过滤数据,只显示当前用户的数据。(3)formfield_for_foreignkey():在模型的数据修改页或数据新增页设置外键字段的值,确保新增或修改数据隶属于当前用户。

# account 的admin.py
from django.contrib import admin
from .models import MyUser
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import gettext_lazy as _
@admin.register(MyUser)
class MyUserAdmin(UserAdmin):list_display = ['username', 'email', 'name','introduce','company','profession','address','telephone','wx','qq','wb','photo']# 在修改页面添加'mobile','qq','weChat'的信息输入框# 将源码的UserAdmin.fieldsets转换为列表格式fieldsets = list(UserAdmin.fieldsets)# 重写UserAdmin的fieldsets,添加模型字段信息录入fieldsets[1] = (_('Personal info'), {'fields': ('name', 'introduce','email','company','profession','address','telephone','wx','qq','wb','photo')})#根据当前用户名设置数据访问权限def get_queryset(self, request):qs = super().get_queryset(request)return qs.filter(id=request.user.id)
# album 的admin.py
from django.contrib import admin
from .models import AlbumInfo
from account.models import MyUser
@admin.register(AlbumInfo)
class AlbumInfoAdmin(admin.ModelAdmin):list_display = ['id','user','title','introduce','photo']# 根据当前用户名设置数据的访问权限def get_queryset(self, request):qs = super().get_queryset(request)return qs.filter(user_id=request.user.id)# 新增或修改数据时,设置外键可选值def formfield_for_foreignkey(self, db_field, request, **kwargs):if db_field.name == 'user':id = request.user.idkwargs['queryset'] = MyUser.objects.filter(id=id)return super().formfield_for_foreignkey(db_field, request, **kwargs)
# article 的admin.py
from django.contrib import admin
from .models import *
admin.site.site_title='博客管理后台'
admin.site.site_header='博客管理'
@admin.register(ArticleTag)class ArticleTagAdmin(admin.ModelAdmin):list_display = ['id','tag','user']def get_queryset(self, request):qs = super().get_queryset(request)return qs.filter(user_id=request.user.id)def formfield_for_manytomany(self, db_field, request, **kwargs):if db_field.name == 'article_tag':id = request.user.idkwargs["queryset"] = ArticleTag.objects.filter(user_id=id)return super().formfield_for_manytomany(db_field, request, **kwargs)def formfield_for_foreignkey(self, db_field, request,**kwargs):if db_field.name == 'author':id = request.user.idkwargs["queryset"] = MyUser.objects.filter(id=id)return  super().formfield_for_foreignkey(db_field, request, **kwargs)@admin.register(Comment)class CommentAdmin(admin.ModelAdmin):list_display = ['article','commentator','content','created']def get_queryset(self, request):qs = super().get_queryset(request)return qs.filter(article_author_id=request.user.id)def formfield_for_foreignkey(self, db_field, request, **kwargs):if db_field.name == 'article':id = request.user.idkwargs["queryset"] = Comment.objects.filter(article_author_id=id)return super().formfield_for_foreignkey(db_field, request, **kwargs)
# interflow 的admin.py
from django.contrib import admin
from .models import Board
from account.models import MyUser
@admin.register(Board)
class BoardAdmin(admin.ModelAdmin):list_display = ['id','name','email','content','created','user']def get_queryset(self, request):qs = super().get_queryset(request)return qs.filter(user_id=request.user.id)def formfield_for_foreignkey(self, db_field, request, **kwargs):if db_field.name == 'user':id = request.user.idkwargs['queryset'] = MyUser.objects.filter(id=id)return super().formfield_for_foreignkey(db_field, request, **kwargs)

下一步在每个项目应用的初始化文件__init__.py中设置项目应用名称,项目应用名称将显示在Admin后台系统的首页,每个项目应用的代码如下:

# account的__init__.py
from django.apps import AppConfig
import os
# 修改app在admin后台显示的名称
# default_app_config的值来自apps.py的类名
default_app_config = 'account.IndexConfig'
# 获取当前app的命名
def get_current_app_name(_file):return os.path.split(os.path.dirname(_file))[-1]
# 重写类IndexConfig
class IndexConfig(AppConfig):name = get_current_app_name(__file__)verbose_name = '用户管理'# album的__init__.py
from django.apps import AppConfig
import os
# 修改app在admin后台显示的名称
# default_app_config的值来自apps.py的类名
default_app_config = 'album.IndexConfig'
# 获取当前app的命名
def get_current_app_name(_file):return os.path.split(os.path.dirname(_file))[-1]
# 重写类IndexConfig
class IndexConfig(AppConfig):name = get_current_app_name(__file__)verbose_name = '我的图片墙'# article的__init__.py
from django.apps import AppConfig
import os
# 修改app在admin后台显示的名称
# default_app_config的值来自apps.py的类名
default_app_config = 'article.IndexConfig'
# 获取当前app的命名
def get_current_app_name(_file):return os.path.split(os.path.dirname(_file))[-1]
# 重写类IndexConfig
class IndexConfig(AppConfig):name = get_current_app_name(__file__)verbose_name = '博客管理'# interflow的__init__.py
from django.apps import AppConfig
import os
# 修改app在admin后台显示的名称
# default_app_config的值来自apps.py的类名
default_app_config = 'interflow.IndexConfig'
# 获取当前app的命名
def get_current_app_name(_file):return os.path.split(os.path.dirname(_file))[-1]
# 重写类IndexConfig
class IndexConfig(AppConfig):name = get_current_app_name(__file__)verbose_name = '留言管理'

将Admin后台系统的登录页面改为项目应用account实现的登录页面。

在myadmin.py文件中定义MyAdminSite类,该类继承父类AdminSite并重写admin_view()和get_urls()方法,从而更改Admin后台系统的用户登录地址,实现代码如下:

# myblog的myadmin.py
from django.contrib import admin
from functools import update_wrapper
from django.views.generic import RedirectView
from django.urls import reverse
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_protect
from django.http import HttpResponseRedirect
from django.urls import include, path,re_path
from django.contrib.contenttypes import views as contenttype_views
from django.contrib.auth.views import redirect_to_loginclass MyAdminSite(admin.AdminSite):def admin_view(self,view,cacheable=False):def inner(request,*args,**kwargs):if not self.has_permission(request):if request.path == reverse('admin:logout',current_app=self.name):index_path = reverse('admin:index',current_app=self.name)return HttpResponseRedirect(index_path)# 修改注销后重新登录的路由地址return redirect_to_login(request.get_full_path(),'/user/login.html')return view(request,*args,**kwargs)if not cacheable:inner = never_cache(inner)if not getattr(view,'csrf_exempt',False):inner = csrf_protect(inner)return update_wrapper(inner,view)def get_urls(self):def wrap(view,cacheable=False):def wrapper(*args,**kwargs):return self.admin_view(view,cacheable)(*args,**kwargs)wrapper.admin_site = selfreturn update_wrapper(wrapper,view)urlpatterns = [path('',wrap(self.index),name='index'),# 修改登录页面的路由地址path('login/',RedirectView.as_view(url='/user/login.html')),path('logout/',wrap(self.logout),name='logout'),path('password_change/',wrap(self.password_change,cacheable=True),name='password_change'),path('password_change/done/',wrap(self.password_change_done,cacheable=True),name='password_change_done'),path('jsi18n/',wrap(self.i18n_javascript, cacheable=True),name='jsi18n'),path('r/<int:content_type_id>/<path:object_id>',wrap(contenttype_views.shortcut),name='view_on_site',),]valid_app_labels = []for model,model_admin in self._registry.items():urlpatterns+=[path('%s/%s/' % (model._meta.app_label,model._meta.model_name),include(model_admin.urls)),]if model._meta.app_label not in valid_app_labels:valid_app_labels.append(model._meta.app_label)if valid_app_labels:regex=r'^(?P<app_label>'+'|'.join(valid_app_labels)+')/$'urlpatterns+=[re_path(regex,wrap(self.app_index),name='app_list'),]return urlpatterns
# myblog的myapps.py
from django.contrib.admin.apps import AdminConfig
class MyAdminConfig(AdminConfig):default_site = 'myblog.myadmin.MyAdminSite'

最后在配置文件settings.py的INSTALLED_APPS中配置系统注册类MyAdminConfig,代码如下:

# myblog的settings.py
INSTALLED_APPS = [# 'django.contrib.admin','myblog.myapps.MyAdminConfig','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','article','album','account','interflow',
]

模型字段content只负责存储文章内容,它不会对文章内容进行排版布局,如果要编写排版整齐、布局精美的博客文章,就需要引入Django的第三方功能应用Django CKEditor生成文章编辑器。

# 安装Django CKEditor
pip install django-ckeditor

完成Django CKEditor安装后,在项目的配置文件settings.py中添加Django CKEditor功能应用,并且设置该应用的功能配置,配置过程如下:

INSTALLED_APPS = [# 'django.contrib.admin','myblog.myapps.MyAdminConfig','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','article','album','account','interflow',# 添加Django CKEditor'ckeditor','ckeditor_uploader',
]
# 编辑器的配置信息
CKEDITOR_UPLOAD_PATH = "article_images"
CKEDITOR_CONFIGS = {'default':{'toolbar':'Full'}
}
CKEDITOR_ALLOW_NONIMAGE_FILTER = False
CKEDITOR_BROWSE_SHOW_DIRS = True

如果需要在文章正文内容中添加图片,那么可以使用Django CKEditor上传的图片,并且上传的图片都会保存在配置属性CKEDITOR_UPLOAD_PATH设置的文件夹中,该文件夹必须在媒体资源文件夹(项目的media文件夹)的目录下。
在这里插入图片描述

想深入了解Django
CKEditor的功能配置和使用方法,那么可以查阅官方文档(pypi.org/project/django-ckeditor/)。

下一步在项目中定义Django CKEditor的路由信息,打开myblog的urls.py并添加路由ckeditor,路由信息如下:

from django.contrib import admin
from django.urls import path, include,re_path
from django.views.static import serve
from django.conf import settings
urlpatterns = [# Admin 后台系统path('admin/', admin.site.urls),# 用户注册和登录path('user/',include('account.urls')),# 博客文章path('',include('article.urls')),# 图片墙path('ablum/',include('album.urls')),# 留言板path('board/',include('interflow.urls')),# 配置媒体资源的路由信息re_path('media/(?P<path>.*)',serve,{'document_root':settings.MEDIA_ROOT},name='media'),# 设置编辑器的路由信息path('ckeditor/',include('ckeditor_uploader.urls')),]

最后将模型ArticleInfo的content字段重新定义,将字段改为Django CKEditor定义的字段类型,代码如下:

# article 的models.pyfrom django.db import models
from django.utils import timezone
from account.models import MyUser
# 导入编辑器定义的字段类型
from ckeditor_uploader.fields import RichTextUploadingField
class ArticleTag(models.Model):id = models.AutoField(primary_key=True)tag = models.CharField('标签',max_length=500)user = models.ForeignKey(MyUser,on_delete=models.CASCADE,verbose_name='用户')def __str__(self):return self.tagclass Meta:verbose_name = '博文分类'verbose_name_plural ='博文分类'class ArticleInfo(models.Model):author = models.ForeignKey(MyUser,on_delete=models.CASCADE,verbose_name='用户')title = models.CharField('标题',max_length=200)content = RichTextUploadingField('内容')articlephoto = models.ImageField('文章图片',blank=True,upload_to='images/article/')reading =models.IntegerField('阅读量',default=0)liking = models.IntegerField('点赞量',default=0)created = models.DateTimeField('创建时间',default=timezone.now)updated = models.DateTimeField('更新时间',auto_now=True)article_tag = models.ManyToManyField(ArticleTag,blank=True,verbose_name='文章标签')def __str__(self):return self.titleclass Meta:verbose_name = '博文管理'verbose_name_plural = '博文管理'class Comment(models.Model):article = models.ForeignKey(ArticleInfo,on_delete=models.CASCADE,verbose_name='所属文章')commentator = models.CharField('评论用户',max_length=90)content = models.TextField('评论内容')created = models.DateTimeField('创建时间',auto_now_add=True)def __str__(self):return self.article.titleclass Meta:verbose_name = '评论管理'verbose_name_plural = '评论管理'

测试和部署

如果网站系统在试运行阶段符合开发需求,并且没有检测出BUG,我们就可以将网站系统设置为上线模式。首先在配置文件settings.py中设置STATIC_ROOT,并修改运行模式,代码如下:

#myblog的settings.py
DEBUG = False
ALLOWED_HOSTS = ['*']
STATIC_ROOT = BASE_DIR / 'static'

现在项目中存在两个静态资源文件夹static和publicStatic,Django根据不同的运行模式读取不同的静态资源文件夹,详细说明如下:
(1)如果将Django设为调试模式(DEBUG=True),那么项目运行时将读取publicStatic文件夹的静态资源。
(2)如果将Django设为上线模式(DEBUG=False),那么项目运行时将读取static文件夹的静态资源。

当Django设为上线模式时,它不再提供静态资源服务,该服务应交由Nginx或Apache服务器完成,因此在项目的路由列表添加静态资源的路由信息,让Django知道如何找到静态资源文件,否则无法在浏览器上访问static文件夹的静态资源文件,路由信息如下:

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com