博客系统主要有以下几大功能点
用户注册和登录
博主资料信息
图片墙功能
留言板功能
文章列表
文章正文内容
Admin后台管理系统
下面按照开发的逻辑从架构设计,功能配置,数据库架构设计,路由列表定义,共同模板编写等大的模块写一下开发的流程
项目应用account实现用户注册登录和用户资料信息页
项目应用album实现图片墙功能
项目应用article实现用户的文章管理
项目应用interflow实现博客留言板的功能
媒体资源文件夹media存放用户的图片,头像等资源文件
静态资源文件夹publicStatic存放网页的CSS/JS文件和网页图片
模板文件夹templates存放模板文件
新建myadmin和myapps用于自定义admin后台系统
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'
]
#添加中间件LocaleMiddleware
'django.middleware.locale.LocaleMiddleware',
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
#将模板文件夹templates引入django
'DIRS': [os.path.join(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',
],
},
},
]
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'blogdb',
'USER':'root',
'PASSWORD':'123456',
'HOST':'127.0.0.1',
'PORT':'3306',
},
}
#配置自定义用户模型MyUser
AUTH_USER_MODEL = 'account.MyUser'
STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR,'publicStatic')]
#设置媒体资源的保存路径
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
# 编辑器的配置信息
CKEDITOR_UPLOAD_PATH = "article_images"
CKEDITOR_CONFIGS = {
'default': {
'toolbar': 'Full'
}
}
CKEDITOR_ALLOW_NONIMAGE_FILES = False
CKEDITOR_BROWSE_SHOW_DIRS = True
从项目的架构设计得知,account的模型MyUser是项目的核心数据,他与每个项目应用的模型都存在数据关联。此处不展示所有的数据表设计过程,仅以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='image/user/')
#设置返回值
def __str__(self):
return self.name
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 = [
path('admin/', admin.site.urls),
path('user/', include('account.urls')),
path('', include('article.urls')),
path('album/', include('album.urls')),
path('board/', include('interflow.urls')),
re_path('media/(?P.*)' , serve, {'document_root': settings.MEDIA_ROOT}, name='media'),
# 设置编辑器的路由信息
path('ckeditor/', include('ckeditor_uploader.urls')),
]
从项目设计得知,博主资料信息展示,图片墙功能,留言板功能和文章列表,文章正文内容的页面布局存在相同之处,因此可以将这些网页功能编写在公用的模板文件中,这里的模板文件不做展示
url.py配置
from django.urls import path
from .views import *
urlpatterns = [
# 用户注册
path('register.html', register, name='register'),
# 用户登录
path('login.html', userLogin, name='userLogin'),
# 关于我
path('about/.html', about, name='about'),
]
views.py配置
from django.shortcuts import render, redirect
from .models import MyUser
from django.contrib.auth import login
from django.contrib.auth import logout
from django.contrib.auth import authenticate
from django.urls import reverse
from album.models import AlbumInfo
from article.models import ArticleTag
def register(request):
title = '注册博客'
pageTitle = '用户注册'
confirmPassword = True
button = '注册'
urlText = '用户登录'
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())
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())
模板文件省略
url.py配置
from django.urls import path
from .views import *
urlpatterns = [
# 图片墙
path('/.html', album, name='album'),
]
views.py配置
from django.shortcuts import render
from django.core.paginator import Paginator
from django.core.paginator import PageNotAnInteger
from django.core.paginator import EmptyPage
from .models import AlbumInfo
def 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, 'album.html', locals())
模板文件省略
url.py的配置
from django.urls import path
from .views import *
urlpatterns = [
# 留言板
path('/.html' , board, name='board'),
]
views.py的配置
from django.shortcuts import render, redirect
from django.core.paginator import Paginator
from django.core.paginator import PageNotAnInteger
from django.core.paginator import EmptyPage
from article.models import ArticleTag
from account.models import MyUser
from album.models import AlbumInfo
from .models import Board
from django.urls import reverse
def 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:
# 如果参数page 的数据类型不是整型,就返回第一页数据
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))
模板文件省略
url.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('/.html' , article, name='article'),
# 文章正文内容
path('detail//.html' , detail, name='detail')
]
views.py的配置
from django.shortcuts import render, redirect
from account.models import MyUser
from album.models import AlbumInfo
from django.core.paginator import Paginator
from django.core.paginator import PageNotAnInteger
from django.core.paginator import EmptyPage
from .models import ArticleInfo, ArticleTag, Comment
from django.db.models import F
from django.urls import reverse
def 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(author_id=id).order_by('-created')
paginator = Paginator(ats, 10)
try:
pageInfo = paginator.page(page)
except PageNotAnInteger:
# 如果参数page 的数据类型不是整型,就返回第一页数据
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 = ArticleInfo.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)] = True
return 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))
模板文件省略
我们在每个项目应用的admin.py中定义每个模型的ModelAdmin
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']
fieldsets = list(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(MyUserAdmin, self).get_queryset(request)
return qs.filter(id=request.user.id)
下一步在每个项目应用的初始化文件__init__.py中设置项目应用名称,项目应用名称将显示在Admin后台系统的首页,每个项目应用的代码如下:
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 = '用户管理'
自定义登录界面使后台登录和博客登录变为一个界面,然后添加Django CKEditor文章编辑器并且配置,这段代码略去