使用Django开发一个完整的博客项目
2018-06-06 本文已影响67人
f050d162bcc1
title: 博客项目(Django版)项目
tags: Django
notebook: 7.0第五月 Python_web后端
[toc]
一、项目概述
项目运行环境
- Python3.6+
- Django 1.11
- MySQL 5.7
- 其他插件(图片处理、分页、验证码....)
项目详细功能介绍
前台功能
- 项目首页展示
- 轮播图
- 博客推荐
- 最新发布
- 博客分类
- 最新评论文章
- widgets小插件
- 搜索功能
- 博客分类功能
- 博客标签查询
- 友情链接
- 博客分页功能
- 博客详细
- 最新评论文章
- 发表评论
- 评论展示
- 评论数
- 阅读数
- 登录功能
- 注册功能
- 邮箱验证功能
- 注销功能
- 页面模板
- 标签云功能
- 读者墙功能
后台功能
- 用户维护
- 权限管理
- 博客分类维护
- 标签维护
- 友情链接
- 轮播图维护
项目演示
项目演示
项目代码演示
代码展示
二、开发环境搭建
使用virtualenv 和 virtualenwrapper
- MySQL 5.7
sudo apt install mysql-server mysql-client
- 安装mysql驱动
pip install pymysql
- 安装Django
pip install django==1.11
三、创建项目
创建项目和应用
- 创建项目
django-admin startproject django-blog
- 创建应用
python manage.py startapp userapp
python manage.py startapp blogapp
配置数据库
- 在settings中配置数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'django_blog_db',
'USER': 'root',
'PASSWORD': 'wwy123',
'HOST': '127.0.0.1',
'PORT': '3306',
}
}
创建数据库(执行迁移文件)
python manage.py migrate
创建超级管理员
python manage.py createsuperuser
四、创建数据模型
USERAPP
USER(用户模型)
from django.contrib.auth.models import AbstractUser
class BlogUser(AbstractUser):
nikename = models.CharField('昵称', max_length=20, default='')
提示:需要在settings配置文件中设置:AUTH_USER_MODEL = 'users.BlogUser'
EMAIL(邮箱验证数据模型)
class EmailVerifyRecord(models.Model):
code = models.CharField(verbose_name='验证码', max_length=50,default='')
email = models.EmailField(max_length=50, verbose_name="邮箱")
send_type = models.CharField(verbose_name="验证码类型", choices=(("register",u"注册"),("forget","找回密码"), ("update_email","修改邮箱")), max_length=30)
send_time = models.DateTimeField(verbose_name="发送时间", default=datetime.now)
class Meta:
verbose_name = "邮箱验证码"
# 复数
verbose_name_plural = verbose_name
def __str__(self):
return '{0}({1})'.format(self.code, self.email)
BLOGAPP
Banner(轮播图模型)
class Banner(models.Model):
title = models.CharField('标题', max_length=50)
cover = models.ImageField('轮播图', upload_to='static/images/banner')
link_url = models.URLField('图片链接', max_length=100)
idx = models.IntegerField('索引')
is_active = models.BooleanField('是否是active', default=False)
def __str__(self):
return self.title
class Meta:
verbose_name = '轮播图'
verbose_name_plural = '轮播图'
Category(博客分类模型)
class BlogCategory(models.Model):
name = models.CharField('分类名称', max_length=20, default='')
class Meta:
verbose_name = '博客分类'
verbose_name_plural = '博客分类'
def __str__(self):
return self.name
Tags(标签模型)
class Tags(models.Model):
name = models.CharField('标签名称', max_length=20, default='')
class Meta:
verbose_name = '标签'
verbose_name_plural = '标签'
def __str__(self):
return self.name
Blog(博客模型)
class Post(models.Model):
user = models.ForeignKey(BlogUser, verbose_name='作者')
category = models.ForeignKey(BlogCategory, verbose_name='博客分类', default=None)
tags = models.ManyToManyField(Tags, verbose_name='标签')
title = models.CharField('标题', max_length=50)
content = models.TextField('内容')
pub_date = models.DateTimeField('发布日期', default=datetime.now)
cover = models.ImageField('博客封面', upload_to='static/images/post', default=None)
views = models.IntegerField('浏览数', default=0)
recommend = models.BooleanField('推荐博客', default=False)
def __str__(self):
return self.title
class Meta:
verbose_name = '博客'
verbose_name_plural = '博客'
Comment(评论模型)
class Comment(models.Model):
post = models.ForeignKey(Post, verbose_name='博客')
user = models.ForeignKey(BlogUser, verbose_name='作者')
pub_date = models.DateTimeField('发布时间')
content = models.TextField('内容')
def __str__(self):
return self.content
class Meta:
verbose_name = '评论'
verbose_name_plural = '评论'
FriendlyLink(友情链接模型)
class FriendlyLink(models.Model):
title = models.CharField('标题', max_length=50)
link = models.URLField('链接', max_length=50, default='')
def __str__(self):
return self.title
class Meta:
verbose_name = '友情链接'
verbose_name_plural = '友情链接'
五、实现首页页面模板
创建模板文件夹
创建模板文件templates,并在settings.py中设置
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'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',
],
},
},
]
配置静态文件路径
STATIC_URL = '/static/'
STATICFILES_DIRS = (
os.path.join(BASE_DIR, "static"),
)
六、创建首页路由
- 创建视图函数
def index(request):
return render(request, 'index.html', {})
- 配置url
url(r'^$', index, name='index' )
- 修改模板CSS JS等静态文件的路径
七、实现幻灯片功能(Banner)
- 注册模型
from blogs.models import Banner
admin.site.register(Banner)
- 编写views
from .models import Banner
def index(request):
banner_list = Banner.objects.all()
ctx = {
'banner_list': banner_list,
}
return render(request, 'index.html', ctx)
- 模板
<!-- banner 开始 -->
<div id="focusslide" class="carousel slide" data-ride="carousel">
<ol class="carousel-indicators">
{% for banner in banner_list %}
{% if banner.is_active %}
<li data-target="#focusslide" data-slide-to="{{banner.idx}}" class="active"></li>
{% else %}
<li data-target="#focusslide" data-slide-to="{{banner.idx}}"></li>
{% endif %}
{% endfor %}
</ol>
<div class="carousel-inner" role="listbox">
{% for banner in banner_list %}
{% if banner.is_active %}
<div class="item active">
<a href="{{banner.link_url}}" target="_blank" title="{{banner.title}}" >
<img src="{{banner.cover}}" alt="{{banner.title}}" class="img-responsive"></a>
</div>
{% else %}
<div class="item">
<a href="{{banner.link_url}}" target="_blank" title="{{banner.title}}" >
<img src="{{banner.cover}}" alt="{{banner.title}}" class="img-responsive"></a>
</div>
{% endif %}
{% endfor %}
</div>
<a class="left carousel-control" href="#focusslide" role="button" data-slide="prev" rel="nofollow">
<span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
<span class="sr-only">上一个</span> </a>
<a class="right carousel-control" href="#focusslide" role="button" data-slide="next" rel="nofollow">
<span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
<span class="sr-only">下一个</span> </a>
</div>
<!-- banner 结束 -->
八、实现博客推荐
- 注册模型
from blogs.models import Banner,Post,BlogCategory,Tags
...
admin.site.register(BlogCategory)
admin.site.register(Tags)
admin.site.register(Post)
- 编写views
# 视图函数 HTTPRequest
def index(request):
banner_list = Banner.objects.all()
recommend_list = Post.objects.filter(recommend=1)
ctx = {
'banner_list': banner_list,
'recommend_list': recommend_list,
}
return render(request, 'index.html', ctx)
- 模板
<!-- 推荐开始 -->
{% for post in recommend_list %}
<article class="excerpt-minic excerpt-minic-index">
<h2><span class="red">【推荐】</span><a target="_blank" href="/blog/{{post.id}}/" title="{{post.title}}" >{{post.title}}</a>
</h2>
<p class="note">{{post.content}}</p>
</article>
{% endfor %}
<!-- 推荐结束 -->
九、实现最新发布
- 编写views
# 视图函数 HTTPRequest
def index(request):
...
post_list = Post.objects.order_by('-pub_date').all()[:10]
....
ctx = {
'banner_list': banner_list,
'recommend_list': recommend_list,
'post_list': post_list,
}
return render(request, 'index.html', ctx)
- 模板
<!-- 最新发布的博客开始 -->
{% for post in post_list%}
<article class="excerpt excerpt-1" style="">
<a class="focus" href="/blog/{{post.id}}/" title="{{post.title}}" target="_blank" ><img class="thumb" data-original="/{{post.cover}}" src="/{{post.cover}}" alt="{{post.title}}" style="display: inline;"></a>
<header><a class="cat" href="#" title="{{post.category.name}}" >{{post.category.name}}<i></i></a>
<h2><a href="/blog/{{post.id}}/" title="{{post.title}}" target="_blank" >{{post.title}}</a>
</h2>
</header>
<p class="meta">
<time class="time"><i class="glyphicon glyphicon-time"></i>{{post.pub_date|date:'Y-m-d'}}</time>
<span class="views"><i class="glyphicon glyphicon-eye-open"></i>{{post.views}}</span> <a class="comment" href="##comment" title="评论" target="_blank" ><i class="glyphicon glyphicon-comment"></i>{{post.comment_set.count}}</a>
</p>
<p class="note">
{% autoescape off %}
{{post.content | truncatechars_html:200}}
{% endautoescape %}
</p>
</article>
{% endfor %}
<!-- 最新发布的博客结束 -->
十、实现博客分类功能
- 编写视图
# 视图函数 HTTPRequest
def index(request):
banner_list = Banner.objects.all()
recommend_list = Post.objects.filter(recommend=1)
post_list = Post.objects.order_by('-pub_date').all()[:10]
blogcategory_list = BlogCategory.objects.all()
ctx = {
'banner_list': banner_list,
'recommend_list': recommend_list,
'post_list': post_list,
'blogcategory_list': blogcategory_list,
}
return render(request, 'index.html', ctx)
- 模型
<div class="title">
<h3>最新发布</h3>
<div class="more">
{%for c in blogcategory_list%}
<a href="/category/{{c.id}}" title="{{c.name}}" >{{c.name}}</a>
{% endfor %}
</div>
</div>
十一、实现最新评论功能
- 编写views
<ul>
{% for post in new_comment_list %}
<li><a title="{{ post.title }}" href="#"><span class="thumbnail">
<img class="thumb" data-original="/{{ post.cover }}"
src="/{{ post.cover }}"
alt="{{ post.title }}" style="display: block;">
</span><span class="text">{{ post.title }}</span><span class="muted"><i
class="glyphicon glyphicon-time"></i>
{{ post.pub_date }}
</span><span class="muted"><i class="glyphicon glyphicon-eye-open"></i>{{ post.views }}</span></a></li>
{% endfor %}
</ul>
十二、实现搜索功能
- 编写views
from django.views.generic.base import View
from django.db.models import Q
class SearchView(View):
# def get(self, request):
# pass
def post(self, request):
kw = request.POST.get('keyword')
post_list = Post.objects.filter(Q(title__icontains=kw)|Q(content__icontains=kw))
ctx = {
'post_list': post_list
}
return render(request, 'list.html', ctx)
- urls
url(r'^search$', SearchView.as_view(), name='search'),
十三、实现友情链接
- 编写视图 (数据源)
def index(request):
....
friendlylink_list = FriendlyLink.objects.all()
.....
- 模板
<div class="widget widget_sentence">
<h3>友情链接</h3>
<div class="widget-sentence-link">
{% for friendlylink in friendlylink_list %}
<a href="{{ friendlylink.link }}" title="{{ friendlylink.title }}"
target="_blank">{{ friendlylink.title }}</a>
{% endfor %}
</div>
</div>
十四、实现博客列表功能
- 编写views
def blog_list(request):
post_list = POST.objects.all()
ctx = {
'post_list': post_list,
}
return render(request, 'list.html', ctx)
- 编写路由
url(r'^list$', blog_list, name='blog_list'),
- base.html
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}知奇博客首页 {% endblock %}</title>
<meta name="keywords" content="">
<meta name="description" content="">
{% block custom_css %}{% endblock %}
<link rel="stylesheet" type="text/css" href="/static/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="/static/css/nprogress.css">
<link rel="stylesheet" type="text/css" href="/static/css/style.css">
<link rel="stylesheet" type="text/css" href="/static/css/font-awesome.min.css">
<link rel="apple-touch-icon-precomposed" href="/static/images/icon.png">
<link rel="shortcut icon" href="/static/images/favicon.ico">
<script src="/static/js/jquery-2.1.4.min.js"></script>
<script src="/static/js/nprogress.js"></script>
<script src="/static/js/jquery.lazyload.min.js"></script>
</head>
<body class="user-select">
<header class="header">
<nav class="navbar navbar-default" id="navbar">
<div class="container">
<div class="header-topbar hidden-xs link-border">
<ul class="site-nav topmenu">
<li><a href="#" >标签云</a></li>
<li><a href="#" rel="nofollow" >读者墙</a></li>
<li><a href="#" title="RSS订阅" >
<i class="fa fa-rss">
</i> RSS订阅
</a></li>
</ul>
爱学习 更爱分享
</div>
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#header-navbar" aria-expanded="false"> <span class="sr-only"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button>
<h1 class="logo hvr-bounce-in"><a href="#" title="知奇课堂博客"><img src="/static/images/201610171329086541.png" alt="知奇课堂博客"></a></h1>
</div>
<div class="collapse navbar-collapse" id="header-navbar">
<ul class="nav navbar-nav navbar-left">
<li><a data-cont="知奇课堂博客" title="知奇课堂博客" href="/">首页</a></li>
<li><a data-cont="博客" title="博客" href="/list">博客</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
{% if user.is_authenticated %}
<li><a data-cont="用户" title="用户" href="#">欢迎,{{user.username}}</a></li>
<li><a data-cont="注册" title="注册" href="/logout">注销</a></li>
{% else %}
<li><a data-cont="登录" title="登录" href="/login">登录</a></li>
<li><a data-cont="注册" title="注册" href="/register">注册</a></li>
{% endif %}
</ul>
</div>
</div>
</nav>
</header>
{% block content %}
{% endblock %}
<footer class="footer">
<div class="container">
<p>Copyright © 2016.Company name All rights reserved.</p>
</div>
<div id="gotop"><a class="gotop"></a></div>
</footer>
<script src="/static/js/bootstrap.min.js"></script>
<!-- <script src="/static/js/jquery.ias.js"></script> -->
<script src="/static/js/scripts.js"></script>
</body>
</html>
十五、实现分页功能
- 安装包
pip install django-pure-pagination
参考链接: https://github.com/jamespacileo/django-pure-pagination
def blog_list(request):
post_list = Post.objects.all()
try:
page = request.GET.get('page', 1)
except PageNotAnInteger:
page = 1
p = Paginator(post_list, per_page=1, request=request)
post_list = p.page(page)
ctx = {
'post_list': post_list,
}
return render(request, 'list.html', ctx)
- 模板
<section class="container">
<div class="content-wrap">
<div class="content">
<div class="title">
<h3 style="line-height: 1.3">博客列表</h3>
</div>
{% for post in post_list.object_list %}
<article class="excerpt excerpt-1"><a class="focus" href="/blog/{{post.id}}" title="{{post.title}}" target="_blank" >
<img class="thumb" data-original="/{{post.cover}}" src="/{{post.cover}}" alt="{{post.title}}" style="display: inline;"></a>
<header><a class="cat" href="/category/{{post.category.id}}" title="{{post.category.name}}" >{{post.category.name}}<i></i></a>
<h2><a href="/blog/{{post.id}}" title="{{post.title}}" target="_blank" >{{post.title}}</a></h2>
</header>
<p class="meta">
<time class="time"><i class="glyphicon glyphicon-time"></i> {{post.pub_date|date:'Y-m-d'}}</time>
<span class="views"><i class="glyphicon glyphicon-eye-open"></i> {{post.views}}</span>
<a class="comment" href="##comment" title="评论" target="_blank" ><i class="glyphicon glyphicon-comment"></i>{{post.comment_set.count}}</a></p>
<p class="note">{{post.content}}</p>
</article>
{% endfor %}
{% include "_pagination.html" %}
- _pagination.html
{% load i18n %}
<div class="pagination">
{% if post_list.has_previous %}
<a href="?{{ post_list.previous_page_number.querystring }}" class="prev">‹‹ 上一页</a>
{% else %}
<span class="disabled prev">‹‹ 上一页</span>
{% endif %}
{% for page in post_list.pages %}
{% if page %}
{% ifequal page post_list.number %}
<span class="current page">{{ page }}</span>
{% else %}
<a href="?{{ page.querystring }}" class="page">{{ page }}</a>
{% endifequal %}
{% else %}
...
{% endif %}
{% endfor %}
{% if post_list.has_next %}
<a href="?{{ post_list.next_page_number.querystring }}" class="next">下一页 ››</a>
{% else %}
<span class="disabled next">下一页 ››</span>
{% endif %}
</div>
十六、实现标签云功能
def blog_list(request):
post_list = Post.objects.all()
try:
page = request.GET.get('page', 1)
except PageNotAnInteger:
page = 1
p = Paginator(post_list, per_page=1, request=request)
post_list = p.page(page)
tags = Tags.objects.all()
tag_message_list = []
for t in tags:
count = len(t.post_set.all())
tm = TagMessage(t.id, t.name, count)
tag_message_list.append(tm)
ctx = {
'post_list': post_list,
'tags': tag_message_list
}
return render(request, 'list.html', ctx)
- 模板
<h3>标签云</h3>
<div class="widget-sentence-content">
<ul class="plinks ptags">
{% for t in tags %}
<li><a href="/tags/{{t.tid}}" title="{{t.name}}" draggable="false">{{t.name}} <span class="badge">{{t.count}}</span></a></li>
{% endfor %}
</ul>
</div>
</div>
十七、实现分类查询功能
- 编写视图
def blog_list(request, cid=-1):
post_list = None
if cid != -1:
cat = BlogCategory.objects.get(id=cid)
post_list = cat.post_set.all()
else:
post_list = Post.objects.all()
....
ctx = {
'post_list': post_list,
'tags': tag_message_list
}
return render(request, 'list.html', ctx)
- 编写路由
url(r'^category/(?P<cid>[0-9]+)/$', blog_list),
- 模板 index
<div class="title">
<h3>最新发布</h3>
<div class="more">
{%for c in blogcategory_list%}
<a href="/category/{{c.id}}" title="{{c.name}}" >{{c.name}}</a>
{% endfor %}
</div>
</div>
十八、实现按标签查询功能
- 编写views
def blog_list(request, cid=-1, tid=-1):
post_list = None
if cid != -1:
cat = BlogCategory.objects.get(id=cid)
post_list = cat.post_set.all()
elif tid != -1:
tag = Tags.objects.get(id=tid)
post_list = tag.post_set.all()
else:
post_list = Post.objects.all()
....
ctx = {
'post_list': post_list,
'tags': tag_message_list
}
return render(request, 'list.html', ctx)
- 路由设置
url(r'^tags/(?P<tid>[0-9]+)/$', blog_list),
- 模板
<h3>标签云</h3>
<div class="widget-sentence-content">
<ul class="plinks ptags">
{% for t in tags %}
<li><a href="/tags/{{t.tid}}" title="{{t.name}}" draggable="false">{{t.name}} <span class="badge">{{t.count}}</span></a></li>
{% endfor %}
</ul>
</div>
</div>
</div>
十九、实现博客详情功能
- 定义视图函数
def blog_detail(request,bid):
post = Post.objects.get(id=bid)
post.views = post.views + 1
post.save()
# 博客标签
tag_list = post.tags.all()
ctx = {
'post': post,
}
return render(request, 'show.html', ctx)
- 路由设置
url(r'^blog/(?P<bid>[0-9]+)/$', blog_detail, name='blog_detail'),
- 前端展示
{% extends 'base.html' %}
{% block title %}知奇博客-详细 {% endblock %}
{% block content %}
<section class="container">
<div class="content-wrap">
<div class="content">
<header class="article-header">
<h1 class="article-title"><a href="#" title="{{post.title}}" >{{post.title}}</a></h1>
<div class="article-meta"> <span class="item article-meta-time">
<time class="time" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="发表时间:{{post.pub_date|date:'Y-m-d'}}">
<i class="glyphicon glyphicon-time"></i> {{post.pub_date|date:'Y-m-d'}}</time>
</span>
<span class="item article-meta-source" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="来源:{{post.user.username}}">
<i class="glyphicon glyphicon-globe"></i> {{post.user.username}}</span>
<span class="item article-meta-category" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="{{post.category.name}}">
<i class="glyphicon glyphicon-list"></i> <a href="#" title="{{post.category.name}}" >{{post.category.name}}</a></span>
<span class="item article-meta-views" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="浏览量:{{post.views}}">
<i class="glyphicon glyphicon-eye-open"></i> {{post.views}}</span>
<span class="item article-meta-comment" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="评论量">
<i class="glyphicon glyphicon-comment"></i> </span> </div>
</header>
<article class="article-content">
<p>{{post.content}}</p>
</article>
<div class="article-tags">标签:
{% for tag in post.tags.all %}
<a href="/tags/{{tag.id}}">{{tag.name}}</a>
{% endfor %}
</div>
{% endblock %}
二十、实现相关推荐功能
- 编写视图
def blog_detail(request, pid):
post = Post.objects.get(id=pid)
post.views = post.views + 1
post.save()
comment_list = Comment.objects.order_by('-pub_date')
# 最新评论的博客 列表
new_comment_list = []
# 去重
for c in comment_list:
if c.post not in new_comment_list:
new_comment_list.append(c.post)
# 相关推荐
# 首先 我们需要取到 这篇文章的tag
tag_post_list = []
for tag in post.tags.all():
tag_post_list.extend(tag.post_set.all())
ctx = {
'post': post,
'new_comment_list': new_comment_list,
'tag_post_list': tag_post_list
}
return render(request, 'show.html', ctx)
- 模板
{% for tag_post in tag_post_list %}
<li><a href="{% url 'blog:detail' tag_post.id %}" title="">{{ tag_post.title }}</a></li>
{% endfor %}