我们再迁移数据库的时候,创建了一个后台账户。然后我么还在models文件下创建了几个数据库表,也就是模型,但是我们需要在后台注册我们那几个模型之后,django admin 才会知道这些模型的存在:
blog/admin.py
from django.contrib import admin
from .models import Post, Category, Tag
admin.site.register(Post)
admin.site.register(Category)
adin.site. register(Tag)
激活虚拟运行环境,运行本地web服务器,访问http://127.0.0.1:8000/admin/,就进入到了,django admin 的后台登陆界面,输入创建的 管理员账户密码就能登陆到后台
登上去就会发现我们刚注册的三个模型,点击Posts后面 的增加按钮,将进入添加post的页面,也就是新增加文章。然后再相关的地方输入一些我们用来测试的内容,增加完后我们点击保存,这样一篇文章就添加完毕了,你也可以多添加几篇文章看看效果。注意每篇文章必须有一个分类,在添加文章的时候你可以选择分类,如果数据库中还没有这种分类,在选择分类的时候我们点击Category后面的+按钮新增加一个分类就好了。
然后我们再去访问http://127.0.0.1:8000/,你就可以看到我们新添加的文章了。就像这样的效果:
在后台文章列表页面,我们现在是看到了文章的标题,但我们希望能看到文章更多的信息,比如说:创建时间,最后一次的修改时间,分类,作者是谁。
这就需要我们来自己定制admin了:
blog/admin.py
from adango.contrib import admin
from .models import Post, Category, Tag
class PostAdmin(admin.ModelAdmin):
list_display = ['title','create_time','modified_time','category','author']
然后我们需要把新增加的PostAdmin也注册进来
admin.site.register(Post, PostAdmin)
admin.site.register(Category)
admin.site.register(Tag)
关于定制后台的方法还有很多,想进一步了解的就去查看文档。
还有很多有趣的方法
首页展示的是所有文章给的列表,当我们点击其中的一片文章时,我们应该实现跳转到文章的详情的页面,开发流程和前面一样:首先我们需要在urls里面配置url,就是 把URL和视图函数绑定到一起,然后实现视图函数,编写模板让视图函数渲染模板。
每篇post对应着不同的url。我们 大致可以这样设计,假设一个用户访问<网站域名>/post/1/的时候,就会进入显示第一篇文章的html页面,当用户访问 <网站域名>/post/2/ 时,显示的是第二篇文章的内容,这里数字代表了第几篇文章,也就是数据库中 Post 记录的 id 值。下面依照这个规则来绑定 URL 和视图:
blog/urls.py
from django.conf.urls import url
from .import views
app_name = 'blog'
urlpaaterns = [
url(r'^$', views.index, name = 'index')
url(r'^post/?[0-9]+)/$' ,views.detail,name ='detali')
]
django 使用正则表达式来匹配用户访问的网址,这里的r’^post/(?P[0-9]+)/$’正好匹配到我们上面需要访问文章详细页面的url规则,这条正则表达式的含义是:
以post开头,后面跟至少一位数字,并以 / 结 尾。比如当用户访问 post/255/ 时(注意 Django 并不关心域名,而只关心去掉域名后的相对 URL),被括起来的部分 (?P[0-9]+) 匹配 255,那么这个 255 会在调用视图函数 detail 时被传递进去,实际上视图函数的调用就是这个样子:detail(request, pk=255)。我们这里必须从 URL 里捕获文章的 id,因为只有这样我们才能知道用户访问的究竟是哪篇文章。
我们通过 app_name=’blog’ 告诉 Django 这个 urls.py 模块是属于 blog 应用的,这种技术叫做视图函数命名空间。我们看到 blog\urls.py 目前有两个视图函数,并且通过 name 属性给这些视图函数取了个别名,分别是 index、detail。但是一个复杂的 Django 项目可能不止这些视图函数,例如一些第三方应用中也可能有叫 index、detail 的视图函数,那么怎么把它们区分开来,防止冲突呢?方法就是通过 app_name 来指定命名空间,命名空间具体如何使用将在下面介绍。如果你忘了在 blog\urls.py 中添加这一句,接下来你可能会得到一个 NoMatchReversed 异常。
为了方便的生成上述的url,我们在post类里面定义了一个get_absolute_url方法,注意post本身是一个python类,在类中我们可以定义任何方法。
blog/models.py
from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse
from django.utils.six import python_2_unicode_compatible
@python_2_unicode_compatible
class Post(models.Model):
...
def __str__(self):
return self.title
#自定义 get_absolute_url方法
#记得从django.urls中导入reverse函数
def get_absolute_url(self):
return reverse('blog:detail',kwargs = {'pk':self.pk})
注意到 URL 配置中的 url(r’^post/(?P[0-9]+)/$’, views.detail, name=’detail’) ,,我们设定的 name=’detail’ 在这里派上了用场。看到这个 reverse 函数,它的第一个参数的值是 ‘blog:detail’,意思是 blog 应用下的 name=detail 的函数,由于我们在上面通过 app_name = ‘blog’ 告诉了 Django 这个 URL 模块是属于 blog 应用的,因此 Django 能够顺利地找到 blog 应用下 name 为 detail 的视图函数,于是 reverse 函数会去解析这个视图函数对应的 URL,我们这里 detail 对应的规则就是 post/(?P[0-9]+)/ 这个正则表达式,而正则表达式部分会被后面传入的参数 pk 替换,所以,如果 Post 的 id(或者 pk,这里 pk 和 id 是等价的) 是 255 的话,那么 get_absolute_url 函数返回的就是 /post/255/ ,这样 Post 自己就生成了自己的 URL。
然后我们就要自己编写视图函数了:
blog/views.py
from django.shortcuts import renser, get_object_or_404
form .models import Post
def index(request):
...
def detail(request):
post = get_object_or_404(Post,pk = pk)
return render(request, 'blog/detail.html', context = {'post':post})
视图函数很简单,他根据我们从url捕获的文章id也就是pk获取 数据库中文章id为该值得记录,然后传递给 模板。注意这里我们用到了从django.shortcuts模块中导入的get_object_or_404方法,作用就是当我们传入的pk对应的post在数据库中存在时,就返回对应的post,如果 不存在,就给用户返回一个404错误。表名请求的文章已经不存在。
接下来就是定制我们的模板文件,从下载 的博客模板中把single.html拷贝到templates/blog目录下面,然后改名为detail.html。此时的整体的项目目录结构应该是这样的
blogproject\
manage.py
blogproject\
__init__.py
settings.py
...
blog/
__init__.py
models.py
,,,
templates\
blog\
index.html
detail.html
在index页面博客文章列表的标题和继续阅读按钮上写上超链接,就是跳转到文章详情页对应的url,让用户点击后可以跳转到detail页面:
templates/blog/index.html
<article class="post post-1">
<header class="entry-header">
<h1 class="entry-title">
<a href="{{ post.get_absolute_url }}">{{ post.title }}a>
h1>
...
header>
<div class="entry-content clearfix">
...
<div class="read-more cl-effect-14">
<a href="{{ post.get_absolute_url }}" class="more-link">继续阅读 <span class="meta-nav">→span>a>
div>
div>
article>
{% empty %}
<div class="no-post">暂时还没有发布的文章!div>
{% endfor %}
这里我们修改两个地方,第一个是文章标题处:
<h1 class="entry-title">
<a href="{{ post.get_absolute_url }}">{{ post.title }}a>
h1>
们把 a 标签的 href 属性的值改成了 {{ post.get_absolute_url }}。回顾一下模板变量的用法,由于 get_absolute_url 这个方法(我们定义在 Post 类中的)返回的是 post 对应的 URL,因此这里 {{ post.get_absolute_url }} 最终会被替换成该 post 自身的 URL。
同样,第二处修改的是继续阅读按钮的链接:
<a href="{{ post.get_absolute_url }}" class="more-link">继续阅读 <span class="meta-nav">→span>
a>
这样当我们点击首页文章的标题或者继续阅读按钮后就会跳转到该篇文章对应的详情页面了。然而如果你尝试跳转到详情页后,你会发现样式是乱的。这在 真正的 Django 博客首页 时讲过,由于我们是直接复制的模板,还没有正确地处理静态文件。我们可以按照介绍过的方法修改静态文件的引入路径,但很快你会发现在任何页面都是需要引入这些静态文件,如果每个页面都要修改会很麻烦,而且代码都是重复的。下面就介绍 Django 模板继承的方法来帮我们消除这些重复操作
我们看到 index.html 文件和 detail.html 文件除了 main 标签包裹的部分不同外,其它地方都是相同的,我们可以把相同的部分抽取出来,放到 base.html 里。首先在 templates\ 目录下新建一个 base.html 文件,这时候你的项目目录应该变成了这个样子:
blogproject\
manage.py
blogproject\
__init__.py
settings.py
...
blog\
__init__.py
models.py
,,,
templates\
base.html
blog\
index.html
detail.html
把 index.html 的内容全部拷贝到 base.html 文件里,然后删掉 main 标签包裹的内容,替换成如下的内容。
templates/base.html
...
"col-md-8">
{% block main %}
{% endblock main %}
...
这里 block 也是一个模板标签,其作用是占位。比如这里的 {% block main %}{% endblock main %} 是一个占位框,main 是我们给这个 block 取的名字。下面我们会看到 block 标签的作用。同时我们也在 aside 标签下加了一个 {% block toc %}{% endblock toc %} 占位框,因为 detail.html 中 aside 标签下会多一个目录栏。当 {% block toc %}{% endblock toc %} 中没有任何内容时,{% block toc %}{% endblock toc %} 在模板中不会显示。但当其中有内容是,模板就会显示 block 中的内容。
在 index.html 里,我们在文件最顶部使用 {% extends ‘base.html’ %} 继承 base.html,这样就把 base.html 里的代码继承了过来,另外在 {% block main %}{% endblock main %} 包裹的地方填上 index 页面应该显示的内容:
templates/blog/index.html
{% extends 'base.html' %}
{% block main %}
{% for post in post_list %}
<article class="post post-1">
...
article>
{% empty %}
<div class="no-post">暂时还没有发布的文章!div>
{% endfor %}
<div class="pagination">
...
div>
{% endblock main %}
这样 base.html 里的代码加上 {% block main %}{% endblock main %} 里的代码就和最开始 index.html 里的代码一样了。这就是模板继承的作用,公共部分的代码放在 base.html 里,而其它页面不同的部分通过替换 {% block main %}{% endblock main %} 占位标签里的内容即可。
如果你对这种模板继承还是有点糊涂,可以把这种继承和 Python 中类的继承类比。base.html 就是父类,index.html 就是子类。index.html 继承了 base.html 中的全部内容,同时它自身还有一些内容,这些内容就通过 “覆写” {% block main %}{% endblock main %}(把 block 看做是父类的属性)的内容添加即可。
detail 页面处理起来就简单了,同样继承 base.html ,在 {% block main %}{% endblock main %} 里填充 detail.html 页面应该显示的内容,以及在 {% block toc %}{% endblock toc %} 中填写 base.html 中没有的目录部分的内容。不过目前的目录只是占位数据,我们在以后会实现如何从文章中自动摘取目录。
templates/blog/detail.html
{% extends 'base.html' %}
{% block main %}
<article class="post post-1">
...
article>
<section class="comment-area">
...
section>
{% endblock main %}
{% block toc %}
<div class="widget widget-content">
<h3 class="widget-title">文章目录h3>
<ul>
<li>
<a href="#">教程特点a>
li>
<li>
<a href="#">谁适合这个教程a>
li>
<li>
<a href="#">在线预览a>
li>
<li>
<a href="#">资源列表a>
li>
<li>
<a href="#">获取帮助a>
li>
ul>
div>
{% endblock toc %}
修改 article 标签下的一些内容,让其显示文章的实际数据:
<article class="post post-{{ post.pk }}">
<header class="entry-header">
<h1 class="entry-title">{{ post.title }}h1>
<div class="entry-meta">
<span class="post-category"><a href="#">{{ post.category.name }}a>span>
<span class="post-date"><a href="#"><time class="entry-date"
datetime="{{ post.created_time }}">{{ post.created_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="#">588 阅读a>span>
div>
header>
<div class="entry-content clearfix">
{{ post.body }}
div>
article>
再次从首页点击一篇文章的标题或者继续阅读按钮跳转到详情页面,可以看到预期效果了!