环境搭建
创建虚拟环境:
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:
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
from django import forms
# 将表单和数据库结合;
from comments.models import Comment
class CommentForm(forms.ModelForm):
class Meta:
# 表明这个表单对应的数据库模型
model = Comment
# 表明表单需要显示的字段
fields= ['user', 'email', 'text']
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())
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> & 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">© 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>