Django写个人博客

环境搭建

创建虚拟环境:
conda create -n Blog python==3.6

进入虚拟环境并安装django:
source activate Blog
pip install django==1.10

用命令创建一个BlogProject的django项目:
django-admin startproject BlogProject
在这里插入图片描述
打开BlogProject:
在这里插入图片描述
设置环境项目的环境:
在这里插入图片描述

在settings.py中指定templates所在位置:
在这里插入图片描述

设置语言和时区:
LANGUAGE_CODE = ‘zh-hans’
TIME_ZONE = ‘Asia/Shanghai’
在这里插入图片描述

后台管理

在settings.py中设置数据库连接:
在这里插入图片描述

假装MySQLdb数据库:
pip install pymysql
在这里插入图片描述

在mysql数据库中建立Blog表:(注意编码格式)
create database if not exists Blog default charset utf8;
在这里插入图片描述

数据库迁移(检测数据库连接是否设置正确):
python manage.py makemigrations
python manage.py migrate
在这里插入图片描述

创建超级用户:
python manage.py createsuperuser
在这里插入图片描述

运行服务:
python manage.py runserver
在这里插入图片描述
用超级用户登陆后台管理系统:
在这里插入图片描述

blog app

创建blog app:
python manage.py startapp blog
注册blog app:
在这里插入图片描述
数据库表的搭建blog/models.py:

from django.contrib.auth.models import User
from django.db import models

# Create your models here.
from django.urls import reverse


class Category(models.Model):
    class Meta:
        verbose_name = "文章分类"
        verbose_name_plural = "文章分类"
    name = models.CharField(max_length=100,unique=True,verbose_name="文章分类")

    def __str__(self):
        return "%s" %(self.name)

class Tag(models.Model):
    class Meta:
        verbose_name = "文章标签"
        verbose_name_plural = "文章标签"

    name = models.CharField(max_length=50, unique=True, verbose_name="标签名")

    def __str__(self):
        return "%s" % (self.name)

class Post(models.Model):
    class Meta:
        verbose_name = "博客"
        verbose_name_plural = "博客"
    title = models.CharField(max_length=50,unique=True,verbose_name="博客标题")
    body = models.TextField(verbose_name="博客正文")
    create_time = models.DateTimeField(verbose_name="发表时间")
    # blank=True, 文章摘要可以为空;
    summary = models.CharField(max_length=200,unique=True,blank=True, verbose_name="摘要")

    category = models.ForeignKey(Category,verbose_name="博客分类")
    tags = models.ManyToManyField(Tag,verbose_name="博客标签")
    author = models.ForeignKey(User,verbose_name="作者")
    views = models.PositiveIntegerField(default=0,verbose_name="阅读量")

    def __str__(self):
        return "%s" % (self.title)

    def get_url(self):
        return reverse("blog:detail",kwargs={'id':self.id})


    def add_views(self):
        self.views +=1
        self.save(update_fields=['views'])


    def save(self, *args, **kwargs):
        if not self.summary:
            # 如果没有摘要, 则设置摘要文章的前200个字符;
            self.summary = self.body[:200]
        super(Post, self).save(*args, **kwargs)

#注意要进行数据库迁移,把表建在数据库中:
#python manage.py makemigrations
#python manage.py migrate

注册模型blog/admin.py:

from django.contrib import admin
from blog.models import Category,Tag,Post
# Register your models here.
admin.site.register(Category)
admin.site.register(Tag)
admin.site.register(Post)

python manage.py runserver:
在这里插入图片描述
views.py:

from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.db.models import Q
from django.db.models.sql import OR
from django.shortcuts import render, get_object_or_404

# Create your views here.
from markdown import markdown, Markdown

from blog.models import Post, Tag
from comments.forms import CommentForm
from Blog.settings import  PER_PAGE

def index(request):

    # 对文章列表对象进行分页, 目前可以却四嗯每页多少条数据;
    pageinator  = Paginator(Post.objects.all(), PER_PAGE)
    # /
    # /?page=1
    # /?page=2
    # /?page=3
    # 获取当前的页数
    page = request.GET.get('page')

    # 假设要看-1页? 总共10页, 要看12页?要做异常处理;
    try:
        # post对象拥有的方法查看源代码:Page类;
        posts = pageinator.page(page)
        # 获取宗页数;
        print(pageinator.num_pages)
        # 获取页数显示列表; range(1,11), 将舍page_range有100页, 如何让显示参考flask的设置;
        print(pageinator.page_range)
    except (PageNotAnInteger, EmptyPage):
        # 如果传进来的时字符串, 默认去看第一页;
        posts = pageinator.page(1)


    return render(request, 'blog/index.html',
                  context={'title': '博客首页',
                           'posts': posts,
                           'paginator': pageinator,
                           'page_nums': pageinator.page_range })

def detail(request, id):
    post = Post.objects.get(id=id)
    post.add_views()
    
    # md对象里面包含toc属性, 可以获取里面的目录;
    md = Markdown(
            extensions=[
                'markdown.extensions.extra',
                'markdown.extensions.codehilite',
                'markdown.extensions.toc',
            ],
        output_format='html'
    )
    post.body = md.convert(post.body)

    # 动态给post对象添加toc属性, 并没有保存到数据库中; md.toc是该博客对象目录的html代码
    post.toc = md.toc
    # print(post.toc)

    # 用户评论的表单;(去comment app 里面创建表单对象)
    form = CommentForm()

    # 用户的所有评论;(获取当前文章的所有评论)
    comments = post.comment_set.all()

    return render(request, 'blog/detail.html',
                  context={
                      'post': post,
                      'form': form,
                      'comments': comments,
                  })


def archive(request, year, month):  # 2018 10
    posts = Post.objects.filter(
        create_time__year=year,
        create_time__month=month
    ).order_by('-create_time')

    return render(request, 'blog/index.html',
                  context={
                      'posts': posts
                  })


def category(request, id):
    #分类
    posts = Post.objects.filter(category_id=id).order_by('-create_time')
    return render(request, 'blog/index.html',
                  context={
                      'posts': posts
                  })


def tag(request, id):
    # 先找出TagId对应的tag对象;
    tag = get_object_or_404(Tag, id=id)
    posts = Post.objects.filter(tags=tag).order_by('-create_time')
    return render(request, 'blog/index.html',
                  context={
                      'posts': posts
                  })


def search(request):
    # get方法提交数据的。 如何获取get方法提交的数据;
    query = request.GET.get('query', None)  # westos
    # Q对象包装查询逻辑, 找出标题中包含query或者文章内容中包含query的博客对象;
    posts = Post.objects.filter(Q(title__icontains=query) | Q(body__icontains=query))

    # 如果没有找到, 返回一个报错信息;
    if not posts:
        return  render(request, 'blog/index.html',
                       context={
                           'posts':posts,
                           'message':"没有找到相关信息"
                       })
    else:
        return  render(request, 'blog/index.html',
                       context={
                           'posts':posts,

                       })

在settings.py中设置static及分页的条数PER_PAGE:

在这里插入图片描述

url.py

from django.conf.urls import url, include
from django.contrib import admin
from blog import views

app_name='blog'
urlpatterns = [
    url(r'^$', views.index,name="index"),
    url(r'^(?P<id>[0-9]+)/$',views.detail,name="detail" ), #name是给视图函数起个别名
    url(r'^archive/(?P<year>\d{4})/(?P<month>\d{1,})/$', views.archive,name='archive'),
    url(r"^category/(?P<id>\d+)/$", views.category, name="category"),
    url(r"^tag/(?P<id>\d+)/$", views.tag, name="tag"),
    url(r"^search/$", views.search, name="search")
]

blog/templatetags/blog_tags.py:
主要是在主页中显示所有的标签,分类等。

from django.db.models import Count

from blog.models import Post, Category, Tag
from django import template

register = template.Library()


# {% get_recent_post %}
@register.simple_tag
def get_recent_post(num=5):
    return Post.objects.all().order_by('-create_time')[:num]


@register.simple_tag
def archives():
    return Post.objects.dates(
        'create_time',
        'month',
        order='DESC'
    )


@register.simple_tag
def get_category():
    """
     这个 Category.objects.annotate 方法和 Category.objects.all 有点类似,它会返回数据库中全部 Category 的记录,但同时它还会做一些额外的事情,
    在这里我们希望它做的额外事情就是去统计返回的 Category 记录的集合中每条记录下的文章数。
    :return:
    """
    # 给category对象中添加统计每个分类包含文章的个数.
    return Category.objects.annotate(num_posts=Count('post')
                                     # 筛选出分类中文章个数大于0的传递给html代码;
                                     ).filter(num_posts__gt=0)


@register.simple_tag
def get_tag():
    """
    :return:
    """
    # return  Tag.objects.all()
    return Tag.objects.annotate(num_posts=Count('post')
                                ).filter(num_posts__gt=0)

comments app:

创建comments app:
python manage.py startapp comments
注册comments app:
在这里插入图片描述
数据库表的搭建comments/models.py:

from django.db import models

# Create your models here.

from django.contrib.auth.models import User

# 用户:评论===(1:n)
# 文章:评论====(1 : n)
class Comment(models.Model):
	user = models.ForeignKey(User)
	email = models.EmailField(max_length=50)
	text = models.TextField()
	# 如果修改评论的内容, 重新保存时, 会把created_time的值更新为当前的时间;
	created_time = models.DateTimeField(auto_now_add=True)
	post = models.ForeignKey('blog.Post')

	def __str__(self):
		return self.text[:10]
#注意要进行数据库迁移,把表建在数据库中:
#python manage.py makemigrations
#python manage.py migrate

建立表单forms.py:

from django import  forms

# 将表单和数据库结合;
from comments.models import Comment


class CommentForm(forms.ModelForm):
    class Meta:
        # 表明这个表单对应的数据库模型
        model = Comment
        # 表明表单需要显示的字段
        fields= ['user', 'email', 'text']

views.py:

from django.http import HttpResponse
from django.shortcuts import render, redirect, get_object_or_404

# Create your views here.



# 处理用户提交的评论信息;
# 确定url(路由)
from blog.models import Post
from  comments.forms import CommentForm
from comments.models import Comment


def post_comment(request, pk):
    # 1). 找出要评论的文章对象
    post = get_object_or_404(Post, pk=pk)
    # print(post)
    # 2). 如果用户提交评论;
    if request.method == 'POST':
        print(request.POST)
        form = CommentForm(request.POST)
        # 判断是否合法?
        if form.is_valid():
            # # 保存到数据库中;

            # 会保存用户提交的相关信息到数据库中, 但是用户给哪篇博客评论, 并没有保存;
            # commit=False, 并没有提交到数据库中;
            comment = form.save(commit=False)

            # 将评论和被评论的博客关联;
            comment.post = post

            # 保存并写入数据库;
            comment.save()

            # data = request.POST
            #
            # comment = Comment(user=data['user'], email=data['email'],
            #         text=data['text'], post = post)
            # comment.save()
            return redirect(post.get_url())
        else:
            return render(request, 'blog/detail.html',
                          context={
                              'errors': form.errors
                          })

    # 3). 如果不是post请求, 访问用户的详情页
    return redirect(post.get_url())

url.py:

from django.conf.urls import url, include
from django.contrib import admin
from comments import views

app_name="comments"
urlpatterns = [
    url(r'^comment/blog/(?P<pk>\d+)/$', views.post_comment, name='post_comment'),

]

Blog/urls.py即项目的总urls:

"""Blog URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.10/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.conf.urls import url, include
    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'',include('blog.urls')),
    url(r'',include('comments.urls')),
]

templates/blog/detail.html:

{% extends 'base.html' %}
{% block title %}博客详情页{% endblock %}
{% block main %}

    <article class="post post-1">
        <header class="entry-header">
            <h1 class="entry-title">{{ post.title }}</h1>
            <div class="entry-meta">
                <span class="post-category"><a href="#">{{ post.category }}</a></span>
                <span class="post-date"><a href="#"><time class="entry-date"
                                                          datetime={{ post.create_time }}>{{ post.create_time }}</time></a></span>
                <span class="post-author"><a href="#">{{ post.author }}</a></span>
                <span class="comments-link"><a href="#">{{ comments.count }} 评论</a></span>
                <span class="views-count"><a href="#">{{ post.views }} 阅读</a></span>
            </div>
        </header>
        <div class="entry-content clearfix">
            {{ post.body | safe }}
        </div>
    </article>
    <section class="comment-area" id="comment-area">
        <hr>
        <h3>发表评论</h3>
        <form action="{% url 'comments:post_comment'  post.pk %}"
              method="POST" class="comment-form">


            <div class="row">
                <div class="col-md-4">

                    {% csrf_token %}

                    <label for="{{ form.user.id_for_label }}">用户</label>
                    {#                    <input type="text" id="id_name" name="name" required>#}
                    {{ form.user }}  <span> {{ form.errors.user }}</span>
                </div>
                <div class="col-md-4">
                    <label for="{{ form.email.id_for_label }}">邮箱</label>
                    {{ form.email }}
                </div>

                <div class="col-md-12">
                    <label for="{{ form.text.id_for_label }}">评论信息</label>
                    {#                    <textarea name="comment" id="id_comment" required></textarea>#}
                    {{ form.text }}
                    <button type="submit" class="comment-btn">发表</button>
                </div>
            </div>    <!-- row -->
        </form>



     <div class="comment-list-panel">
                        <h3>评论列表,共 <span>{{ comments.count }}</span> 条评论</h3>
                        <ul class="comment-list list-unstyled">


                            {% for comment in comments %}
                            <li class="comment-item">
                                <span class="nickname">{{ comment.user }}</span>
                                <time class="submit-date"
                                      datetime="{{ comment.created_time }}">
                                    {{ comment.created_time }}</time>
                                <div class="text">
                                    {{ comment.text }}
                                </div>
                            </li>
                            {% endfor %}

                        </ul>
                    </div>

    </section>

    </main>
{% endblock %}

{% block toc %}
    <div class="widget widget-content">
                    <h3 class="widget-title">文章目录</h3>
                    {{ post.toc | safe }}
                </div>
{% endblock %}

templates/index.html:

{% extends 'base.html' %}

{% block title %}博客首页{% endblock %}

{% block main %}

    {% for post in posts %}
        <article class="post post-{{ post.id }}">
            <header class="entry-header">
                <h1 class="entry-title">
                    <a href="../single.html">{{ post.title }}</a>
                </h1>
                <div class="entry-meta">
                    <span class="post-category"><a href="#">{{ post.category }}</a></span>
                    <span class="post-date"><a href="#"><time class="entry-date"
                                                              datetime={{ post.create_time }}>{{ post.create_time }}</time></a></span>
                    <span class="post-author"><a href="#">{{ post.author }}</a></span>
                    <span class="comments-link"><a href="#">4 评论</a></span>
                    <span class="views-count"><a href="#">{{ post.views }} 阅读</a></span>
                </div>
            </header>
            <div class="entry-content clearfix">
                {% if post.summary %}
                    <p>{{ post.summary }}</p>
                {% else %}

                    {#   获取前面字符串的前200个字符;                           #}
                    <p>{{ post.body |  truncatechars:200 }}</p>
                {% endif %}
                <div class="read-more cl-effect-14">
                    <a href="{{ post.get_url }}" class="more-link">继续阅读 <span class="meta-nav">→</span></a>
                </div>
            </div>
        </article>
    {% endfor %}


    <br/>
    <br/>
    <br/>

    <div class="pager">


        {% if posts.has_previous %}
            <li><a class="active" href="?page={{ posts.previous_page_number }}">
                上一页</a></li>
        {% else %}
            <li><a class="disabled">上一页</a></li>
        {% endif %}
        <span class="current">第 {{ posts.number }} 页 / 共 {{ paginator.num_pages }} 页</span>

        {% if posts.has_next %}
            <li><a class="active" href="?page={{ posts.next_page_number }}">
                下一页</a></li>
        {% else %}
            <li><a class="disabled">下一页</a></li>

        {% endif %}


        <br/>

        {#        具体的分页#}

    </div>
{% endblock %}

base.html:

{% load blog_tags %}


<!DOCTYPE html>
<html>
<head>

    {% block title %}{% endblock %}

    <!-- meta -->
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- css -->
    <link rel="stylesheet" href="/static/css/bootstrap.min.css">
    <link rel="stylesheet" href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
    <link rel="stylesheet" href="/static/css/pace.css">
    <link rel="stylesheet" href="/static/css/highlights/github.css">

    <link rel="stylesheet" href="/static/css/custom.css">


</head>

<body id="single">
<div class="container">
    <header id="site-header">
        <div class="row">
            <div class="col-md-4 col-sm-5 col-xs-8">
                <div class="logo">
                    <h1><a href="{% url 'blog:index' %}"><b>Black</b> &amp; White</a></h1>
                </div>
            </div><!-- col-md-4 -->
            <div class="col-md-8 col-sm-7 col-xs-4">
                <nav class="main-nav" role="navigation">
                    <div class="navbar-header">
                        <button type="button" id="trigger-overlay" class="navbar-toggle">
                            <span class="ion-navicon"></span>
                        </button>
                    </div>

                    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                        <ul class="nav navbar-nav navbar-right">
                            <li class="cl-effect-11"><a href="{% url "blog:index" %}" data-hover="首页">首页</a></li>
                            <li class="cl-effect-11"><a href="full-width.html" data-hover="博客">博客</a></li>
                            <li class="cl-effect-11"><a href="about.html" data-hover="关于">关于</a></li>
                            <li class="cl-effect-11"><a href="contact.html" data-hover="联系">联系</a></li>
                        </ul>
                    </div><!-- /.navbar-collapse -->
                </nav>


                <div>
                        <form role="search" method="get" id="searchform" action="{% url "blog:search" %}">
                            <input name="query" type="search" placeholder="搜索"  width="20px" required>
                            <button type="submit"><span class="ion-ios-search-strong"></span></button>
                        </form>
                </div>




            </div><!-- col-md-8 -->
        </div>
    </header>
</div>

<div class="content-body">
    <div class="container">
        <div class="row">
            {% if message %}

                <p style="color: red">{{ message }}</p>
            {% endif %}


            <main class="col-md-8">
                {% block main %}

                {% endblock %}
            </main>
            <aside class="col-md-4">
                {% block toc %}


                {% endblock %}
                <div class="widget widget-recent-posts">
                    <h3 class="widget-title">最新文章</h3>
                    <ul>
                        {% get_recent_post as recent_posts %}
                        {% for post in  recent_posts %}
                            <li>
                                <a href="{{ post.get_url }}">{{ post.title }}</a>
                            </li>

                        {% endfor %}

                    </ul>
                </div>
                <div class="widget widget-archives">
                    <h3 class="widget-title">归档</h3>

                    {% archives as dates %}


                    <ul>
                        {% for date in dates %}
                            <li>

                                <a href="{% url 'blog:archive' date.year date.month %}">
                                    {{ date.year }} 年 {{ date.month }} 月</a>
                            </li>
                        {% endfor %}

                    </ul>
                </div>

                <div class="widget widget-category">
                    <h3 class="widget-title">分类</h3>
                    <ul>

                        {% get_category as  categories %}

                        {% for  category in categories %}
                            <li>
                                {#  /category/1/ #}
                                <a href="{% url "blog:category"  category.id %}">
                                    {{ category.name }} <span class="post-count">({{ category.num_posts }})</span></a>
                            </li>

                        {% endfor %}
                    </ul>
                </div>

                <div class="widget widget-tag-cloud">
                    <h3 class="widget-title">标签云</h3>
                    <ul>
                        {% get_tag as tags %}
                        {% for tag in tags %}
                            <li>
                                {# /tag/1/ #}
                                <a href="{% url "blog:tag" tag.id %}">{{ tag.name }}({{ tag.num_posts }})</a>
                            </li>

                        {% endfor %}

                    </ul>
                </div>
                <div class="rss">
                    <a href=""><span class="ion-social-rss-outline"></span> RSS 订阅</a>
                </div>
            </aside>
        </div>
    </div>
</div>
<footer id="site-footer">
    <div class="container">
        <div class="row">
            <div class="col-md-12">
                <p class="copyright">&copy 2018 - Collect from <a href="http://www.cssmoban.com/"
                                                                  target="_blank" title="模板之家">模板之家</a>
                    - Modified by <a href="#" title="网页模板" target="_blank">西部开源技术中心的博客</a>
                </p>
            </div>
        </div>
    </div>
</footer>

<!-- Mobile Menu -->
<div class="overlay overlay-hugeinc">
    <button type="button" class="overlay-close"><span class="ion-ios-close-empty"></span></button>
    <nav>
        <ul>
            <li><a href="blog/index.html">首页</a></li>
            <li><a href="full-width.html">博客</a></li>
            <li><a href="about.html">关于</a></li>
            <li><a href="contact.html">联系</a></li>
        </ul>
    </nav>
</div>

<script src="/static/js/script.js"></script>
<!-- js -->
<script src="/static/js/jquery-2.1.3.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
<script src="/static/js/pace.min.js"></script>
<script src="/static/js/modernizr.custom.js"></script>
</body>
</html>

效果展示:

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

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页