导图社区 django框架搭建个人博客
使用Python+django完成一个个人博客的搭建
编辑于2019-07-03 11:09:14blogproject
强烈推荐在 Virtualenv 下进行 Django 的开发。安装 sudo apt-get install python-virtualenv使用方法 virtualenv [虚拟环境名称] (本项目中使用命令为: virtualenv blogproject_env)启动虚拟环境 cd blogproject_env source ./bin/activate退出虚拟环境 deactivate在虚拟环境安装Python套件(by pip安装工具) : pip install [套件名称]
建立django工程-blogpeoject
__init__
settings
修改时区加语言
把 LANGUAGE_CODE 的值改为 zh-hans,TIME_ZONE 的值改为 Asia/Shanghai
urls
配置不同应用下的子路由include
wsgi
创建应用-------blog
migrations
生成迁移文件
__init__
static
从网上下载博客模板放在blog下
css
js
新建文件夹templatetags
自定义模板标签blog_tags.py
最新文章模板标签
from django import templatefrom ..models import Postregister = template.Library()@register.simple_tagdef get_recent_posts(num=5): return Post.objects.all().order_by('-created_time')[:num]
这个函数的功能是获取数据库中前 num 篇文章,在这里默认为5为了能够通过 {% get_recent_posts %} 的语法在模板中调用这个函数,必须按照 Django 的规定注册这个函数为模板标签from django import templateregister = template.Library()@register.simple_tag这里我们首先导入 template 这个模块,然后实例化了一个 template.Library 类,并将函数 get_recent_posts 装饰为 register.simple_tag。这样就可以在模板中使用语法 {% get_recent_posts %} 调用这个函数了。
归档模板标签
和最新文章模板标签一样,先写好函数,然后将函数注册为模板标签即可。
blog/templatetags/blog_tags.py@register.simple_tagdef archives(): return Post.objects.dates('created_time', 'month', order='DESC')
这里 dates 方法会返回一个列表,列表中的元素为每一篇文章(Post)的创建时间,且是 Python 的 date 对象,精确到月份,降序排列。接受的三个参数值表明了这些含义,一个是 created_time ,即 Post 的创建时间,month 是精度,order='DESC' 表明降序排列(即离当前越近的时间越排在前面)。
分类模板标签
先写好函数,然后将函数注册为模板标签。
from ..models import Post, Category@register.simple_tagdef get_categories(): return Category.objects.all()
子主题
新建py文件__init__
__init__.py
admin
注册admin后台显示的类
添加数据
定制admin显示方式
from django.contrib import adminfrom .models import Post, Category, Tagclass PostAdmin(admin.ModelAdmin): list_display = ['title', 'created_time', 'modified_time', 'category', 'author']# 把新增的 PostAdmin 也注册进来admin.site.register(Post, PostAdmin)admin.site.register(Category)admin.site.register(Tag)
apps
models
创建 Django 博客的数据库模型
创建完成后迁移数据库从admin超级管理员后台测试添加文章以及各种一对对,多对多关系是否正常绑定python3 manage.py createsuperuser
class Category(models.Model)
name
class Tag(models.Model)
name
class Post(models.Model)
文章标题title
文章正文body = models.TextField()
文章摘要excerpt = models.CharField(max_length=200, blank=True)
文章创建时间created_time = models.DateTimeField()文章修改时间modified_time = models.DateTimeField()
文章分类category = models.ForeignKey(Category)
# 我们规定一篇文章只能对应一个分类,但是一个分类下可以有多篇文章,所以我们使用的是 ForeignKey,即一对多的关联关系。
文章标签tags = models.ManyToManyField(Tag, blank=True)
而对于标签来说,一篇文章可以有多个标签,同一个标签下也可能有多篇文章,所以我们使用 ManyToManyField,表明这是多对多的关联关系。同时我们规定文章可以没有标签,因此为标签 tags 指定了 blank=True。
文章作者author = models.ForeignKey(User)
文章作者,这里 User 是从 django.contrib.auth.models 导入的规定一篇文章只能有一个作者,而一个作者可能会写多篇文章,因此这是一对多的关联关系,和 Category 类似。
定义方法反向获取网址
def get_absolute_url(self): return reverse('blog:detail', kwargs={'pk': self.pk})
对应的路由为: url(r'^post/(?P<pk>[0-9]+)/$', views.detail, name='detail'),
定义一个 Meta 类,并指定排序属性:
class Meta: ordering = ['-created_time']
为三个模型添加显示方法
def __str__(self): return self.name
记得迁移
tests
urls
首页
url(r'^$', views.index, name='index'),
文章详情页
url(r'^post/(?P<pk>[0-9]+)/$', views.detail, name='detail'),
归档页
url(r'^archives/(?P<year>[0-9]{4})/(?P<month>[0-9]{1,2})/$', views.archives, name='archives'),
分类页
url(r'^category/(?P<pk>[0-9]+)/$', views.category, name='category'),
views
index视图函数
def index(request): post_list = Post.objects.all().order_by('-created_time') return render(request, 'blog/index.html', context={'post_list': post_list})
detail详情页面函数
基础框架
def detail(request, pk): post = get_object_or_404(Post, pk=pk) return render(request, 'blog/detail.html', context={'post': post})
在 detail 视图中渲染 Markdown
import markdown from django.shortcuts import render, get_object_or_404 from .models import Post def detail(request, pk): post = get_object_or_404(Post, pk=pk) # 记得在顶部引入 markdown 模块 post.body = markdown.markdown(post.body, extensions=[ 'markdown.extensions.extra', 'markdown.extensions.codehilite', 'markdown.extensions.toc', ]) return render(request, 'blog/detail.html', context={'post': post})
在detail视图中添加评论部分
import markdown from django.shortcuts import render, get_object_or_404 + from comments.forms import CommentForm from .models import Post, Category def detail(request, pk): post = get_object_or_404(Post, pk=pk) post.body = markdown.markdown(post.body, extensions=[ 'markdown.extensions.extra', 'markdown.extensions.codehilite', 'markdown.extensions.toc', ]) # 记得在顶部导入 CommentForm form = CommentForm() # 获取这篇 post 下的全部评论 comment_list = post.comment_set.all() # 将文章、表单、以及文章下的评论列表作为模板变量传给 detail.html 模板,以便渲染相应数据。 context = {'post': post, 'form': form, 'comment_list': comment_list } return render(request, 'blog/detail.html', context=context)
archives归档部分函数
def archives(request, year, month): post_list = Post.objects.filter(created_time__year=year, created_time__month=month ).order_by('-created_time') return render(request, 'blog/index.html', context={'post_list': post_list})
category分类页面视图函数
blog/views.py import markdown from django.shortcuts import render, get_object_or_404 # 引入 Category 类 from .models import Post, Category def category(request, pk): # 记得在开始部分导入 Category 类 cate = get_object_or_404(Category, pk=pk) post_list = Post.objects.filter(category=cate).order_by('-created_time') return render(request, 'blog/index.html', context={'post_list': post_list})这里我们首先根据传入的 pk 值(也就是被访问的分类的 id 值)从数据库中获取到这个分类。get_object_or_404 函数和 detail 视图中一样,其作用是如果用户访问的分类不存在,则返回一个 404 错误页面以提示用户访问的资源不存在。然后我们通过 filter 函数过滤出了该分类下的全部文章。同样也和首页视图中一样对返回的文章列表进行了排序。
创建应用-comments
migrations
生成迁移文件
__init__
__init__
migrations
生成迁移文件
__init__
admin
apps
forms
处理用户在评论表单处所提交的内容
comments/forms.py from django import forms from .models import Comment class CommentForm(forms.ModelForm): class Meta: model = Comment fields = ['name', 'email', 'url', 'text']要使用 Django 的表单功能,我们首先导入 forms 模块。Django 的表单类必须继承自 forms.Form 类或者 forms.ModelForm 类。如果表单对应有一个数据库模型(例如这里的评论表单对应着评论模型),那么使用 ModelForm 类会简单很多,这是 Django 为我们提供的方便。之后我们在表单的内部类 Meta 里指定一些和表单相关的东西。model = Comment 表明这个表单对应的数据库模型是 Comment 类。fields = ['name', 'email', 'url', 'text'] 指定了表单需要显示的字段,这里我们指定了 name、email、url、text 需要显示。
models
设计评论的数据库模型
class Comment(models.Model):
comments/models.py from django.db import models from django.utils.six import python_2_unicode_compatible # python_2_unicode_compatible 装饰器用于兼容 Python2 @python_2_unicode_compatible class Comment(models.Model): name = models.CharField(max_length=100) email = models.EmailField(max_length=255) url = models.URLField(blank=True) text = models.TextField() created_time = models.DateTimeField(auto_now_add=True) post = models.ForeignKey('blog.Post') def __str__(self): return self.text[:20]
name = models.CharField(max_length=100)
email = models.EmailField(max_length=255)
url = models.URLField(blank=True)
text = models.TextField()
created_time = models.DateTimeField(auto_now_add=True)
post = models.ForeignKey('blog.Post')
为模型添加显示方法
def __str__(self): return self.text[:20]
记得迁移
tests
urls
别忘了给这个评论的 URL 模式规定命名空间,即 app_name = 'comments'。
comments/urls.py from django.conf.urls import url from . import views app_name = 'comments' urlpatterns = [ url(r'^comment/post/(?P<post_pk>[0-9]+)/$', views.post_comment, name='post_comment'), ]
在项目的 blogprokect\ 目录的 urls.py 里包含 comments\urls.py 这个文件
url(r'', include('comments.urls')),
views
def post_comment(request, post_pk):
comments/views.py from django.shortcuts import render, get_object_or_404, redirect from blog.models import Post from .models import Comment from .forms import CommentForm def post_comment(request, post_pk): # 先获取被评论的文章,因为后面需要把评论和被评论的文章关联起来。 # 这里我们使用了 Django 提供的一个快捷函数 get_object_or_404, # 这个函数的作用是当获取的文章(Post)存在时,则获取;否则返回 404 页面给用户。 post = get_object_or_404(Post, pk=post_pk) # HTTP 请求有 get 和 post 两种,一般用户通过表单提交数据都是通过 post 请求, # 因此只有当用户的请求为 post 时才需要处理表单数据。 if request.method == 'POST': # 用户提交的数据存在 request.POST 中,这是一个类字典对象。 # 我们利用这些数据构造了 CommentForm 的实例,这样 Django 的表单就生成了。 form = CommentForm(request.POST) # 当调用 form.is_valid() 方法时,Django 自动帮我们检查表单的数据是否符合格式要求。 if form.is_valid(): # 检查到数据是合法的,调用表单的 save 方法保存数据到数据库, # commit=False 的作用是仅仅利用表单的数据生成 Comment 模型类的实例,但还不保存评论数据到数据库。 comment = form.save(commit=False) # 将评论和被评论的文章关联起来。 comment.post = post # 最终将评论数据保存进数据库,调用模型实例的 save 方法 comment.save() # 重定向到 post 的详情页,实际上当 redirect 函数接收一个模型的实例时,它会调用这个模型实例的 get_absolute_url 方法, # 然后重定向到 get_absolute_url 方法返回的 URL。 return redirect(post) else: # 检查到数据不合法,重新渲染详情页,并且渲染表单的错误。 # 因此我们传了三个模板变量给 detail.html, # 一个是文章(Post),一个是评论列表,一个是表单 form # 注意这里我们用到了 post.comment_set.all() 方法, # 这个用法有点类似于 Post.objects.all() # 其作用是获取这篇 post 下的的全部评论, # 因为 Post 和 Comment 是 ForeignKey 关联的, # 因此使用 post.comment_set.all() 反向查询全部评论。 # 具体请看下面的讲解。 comment_list = post.comment_set.all() context = {'post': post, 'form': form, 'comment_list': comment_list } return render(request, 'blog/detail.html', context=context) # 不是 post 请求,说明用户没有提交数据,重定向到文章详情页。 return redirect(post)redirect 既可以接收一个 URL 作为参数,也可以接收一个模型的实例作为参数(例如这里的 post)。如果接收一个模型的实例,那么这个实例必须实现了 get_absolute_url 方法,这样 redirect 会根据 get_absolute_url 方法返回的 URL 值进行重定向。
模板-templates
blog
index.html
引入css,js模板
引入css文档:使用标签{% static "相对路径" %}<link rel="stylesheet" href="{% static 'blog/css/bootstrap.min.css' %}">
detail.html
引入css,js模板
引入js文档:使用标签{% static "相对路径" %}<script src="{% static 'blog/js/modernizr.custom.js' %}"></script>
base.html
manage.py
在建立主项目之后,测试是否能运行
在setting中注册应用,配置数据库
在 settings.py 文件里设置一下模板文件所在的路径
别忘了{% load staticfiles %}