Django实现博客项目
一、项目概述
项目运行环境
- 
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 userapppython 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 %}
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号