Django 开发内容管理系统(第二天)
内容管理系统开发(第二天)
2.8 完善后台的功能,在后台添加,编辑,删除数据
更改 news/admin.py
from django.contrib import admin from .models import Column, Article class ColumnAdmin(admin.ModelAdmin): list_display = ('name', 'slug', 'intro',) class ArticleAdmin(admin.ModelAdmin): list_display = ('title', 'slug', 'author', 'pub_date', 'update_time') admin.site.register(Column, ColumnAdmin) admin.site.register(Article, ArticleAdmin)
创建一个超级管理员(如果你没有后台帐户和密码的话)
python manage.py createsuperuser
这里我们打开开发服务器,访问后台网址,就可以看到:
python manage.py runserver 还可以指定端口 python manage.py runserver 8002 也可以监视所有本机IP, python manage.py runserver 0.0.0.0:8001
后台首页:
后台栏目列表:
后台文章列表:
文章编辑页面:
我们已经可以在后台进行更改和保存文章了,但是在编辑新闻的时候是不是觉得不爽,没有一个编辑器,我们下面来集成百度的Ueditor 到我们的系统:
2.8.1 集成 DjangoUeditor 编辑器
安装 DjangoUeditor 包
由于这个包 1.8 有一个bug,已经被我修复了,但是原作者还没有上传到 pypi, 我们直接下载 zip,或者 git clone 下来,
原作者github 地址:https://github.com/zhangfisher/DjangoUeditor 直接下载zip (Python 2)
Python 3 开发者:https://github.com/twz915/DjangoUeditor3 直接下载zip(Python 2/3)
把里面的 DjangoUeditor-master 中的 DjangoUeditor 文件夹放到 news 同一级目录,如图所求:
在 minicms/settings.py 中加入 DjangoUeditor 这个应用
INSTALLED_APPS = ( ... 'news', 'DjangoUeditor', )
在 minicms/urls.py 中添加一行:
url(r'^ueditor/', include('DjangoUeditor.urls' )),
Django 1.8.3 urls.py 中写了,建议先引入,再使用,即:
from django.conf.urls import include, url from django.contrib import admin from DjangoUeditor import urls as DjangoUeditor_urls urlpatterns = [ url(r'^admin/', include(admin.site.urls)), url(r'^ueditor/', include(DjangoUeditor_urls)), ]
但是原来的写法依旧可以使用。
这一段是后来加的,github上没有
为了让上传的图片,文件可以在本地调试的时候可以正常显示,下载,
在 minicms/settings.py 设置 static 和 media
# Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.8/howto/static-files/ STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, 'static') # 公共的 static 文件,比如 jquery.js 可以放这里,这里面的文件夹不能包含 STATIC_ROOT STATICFILES_DIRS = ( os.path.join(BASE_DIR, "common_static"), ) # upload folder MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
在 minicms/urls.py 最后加入以下代码:
# use Django server /media/ files from django.conf import settings if settings.DEBUG: from django.conf.urls.static import static urlpatterns += static( settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
安装配置好后,修改 models.py
# 引入这个 Field,其它的不要变 from DjangoUeditor.models import UEditorField class Article(models.Model): ...省略 #仅修改 content 字段 content = UEditorField('内容', height=300, width=1000, default=u'', blank=True, imagePath="uploads/images/", toolbars='besttome', filePath='uploads/files/') ...
再次打开,发现,已经可以使用了:
后台的功能就讲到这里,大家可以后期自己再进行完善。
2.9 前台内容显示
我们在后台可以看到栏目,新闻,但是让用户来看这些内容不能每人给一个后台帐号吧。
我们假设这么设计,首页显示一些栏目,每个栏目显示五篇相关的文章,栏目可以点击进去,文章也可以点击进去。
2.9.1 网址与内容如何关联
我们复习一下前面的 models.py
class Column(models.Model): ... slug = models.CharField('栏目网址', max_length=256, db_index=True) ... class Article(models.Model): ... slug = models.CharField('网址', max_length=256, db_index=True) ...
前面我们都自定义了文章的网址,假设我们想访问 /column/column_slug/ 访问栏目的内容,访问 /news/article_slug/ 查看文章详情
提示:其实这里有个 bug, 不同栏目,不同文章网址可以是一样的,先不管这个,我们稍后会修复它!
我们修改 minicms/urls.py 添加上面的规则:
url(r'^$', 'news.views.index', name='index'), url(r'^column/(?P<column_slug>[^/]+)/$', 'news.views.column_detail', name='column'), url(r'^news/(?P<article_slug>[^/]+)/$', 'news.views.article_detail', name='article'), url(r'^admin/', include(admin.site.urls)),
下面我们来完善这三个视图函数(这里为了告诉大家如何做,我们不用通用视图,因为通用视图都帮大家做好了,学不到什么东西)
首页 index 栏目 column 文章详情 article
# -*- coding: utf-8 -*- from django.shortcuts import render from django.http import HttpResponse def index(request): return HttpResponse(u'欢迎来自强学堂学习Django') def column_detail(request, column_slug): return HttpResponse('column slug: ' + column_slug) def article_detail(request, article_slug): return HttpResponse('article slug: ' + article_slug)
上面是一个大致框架,我们马上完善它,大家看一下参数是如何传递的,可以打开开发服务器 python manage.py runserver
访问比如:http://127.0.0.1:8002/column/tech/ (注意端口,改成你自己的)会得到:
我们发现 slug 已经被正确传递到了 views.py 中的视图函数,我们在 views.py 中可以用 slug 检索出相应的栏目或文章。
至此,我们已经知道栏目网址和文章网址的规则,我们现在用代码来生成相关的网址,我们运行 python manage.py shell 进入有项目环境的终端
python manage.py shell 相当于下面两句: python from minicms.wsgi import *
我们知道 slug 后,就能得到相应的网址:
>>> from django.core.urlresolvers import reverse >>> >>> # column >>> reverse('column', args=('tech',)) u'/column/tech/' >>> reverse('column', args=('sports',)) u'/column/sports/' >>> >>> # article >>> reverse('article', args=('article_slug',)) u'/news/article_slug/' >>> reverse('article', args=('windows_10',)) u'/news/windows_10/' >>>
我们修改 models.py ,将获取网址的功能写成一个函数 get_absolute_url ,然后在模板或其它脚本中调用。
后台默认也会调用这个函数,可以理解成一个约定俗成的名称。
from django.core.urlresolvers import reverse ... class Column(models.Model): ... slug = models.CharField('栏目网址', max_length=256, db_index=True) ... def get_absolute_url(self): return reverse('column', args=(self.slug,)) ... class Article(models.Model): ... slug = models.CharField('网址', max_length=256, db_index=True) ... def get_absolute_url(self): return reverse('article', args=(self.slug,)) ...
注意 args 参数为元组,写 args=(self.slug) 这样是错的,注意后面有一个逗号 args=(self.slug,)
我们再次进入 终端 python manage.py shell
>>> from news.models import Column, Article >>> c = Column.objects.all()[0] >>> c.get_absolute_url() u'/column/sports/' >>> a = Article.objects.all()[0] >>> a.get_absolute_url() u'/news/article_1/'
我们可以看到这样已经可以获取到文章或者栏目的网址了,下面我们在模板中显示它们。
2.9.2 相关模板文件
我们先写一个 base.html 所有的其它模板都继承它,由于 base.html 不属于某一个 app,它是整个项目共用的,我们建立一个专门的 模块文件夹来放它样的文件。
比如还有其它的 baidutongji.html ,把百度统计的代码放进去来统计访问情况。
我们修改 settings.py,添加一个模板目录 项目下 templates 文件夹。
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', ], }, }, ]
项目目录下 建一个 templates 文件夹,里面写一个 base.html
templates/base.html 模板文件
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>{% block title %}欢迎光临{% endblock title %} - 自强学堂</title> {% block css %} {% endblock css %} {% block js %} {% endblock js %} </head> <body> {% block content %} <h1>自强 新闻网</h1> {% endblock content %} {% include "baidutongji.html" %} </body> </html>
上面这样写是为了足够简单,喜欢 BootStrap 的同学可以用这个 base.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,Chrome=1"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>{% block title %}欢迎光临{% endblock title %} - 自强学堂</title> <!-- Bootstrap --> <link href="http://apps.bdimg.com/libs/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet"> <!-- 引入下面两个库让 IE8 支持 HTML5 元素 --> <!-- 警告: Respond.js 通过 file:// 浏览的时候不能正常工作!--> <!--[if lt IE 9]> <script src="http://apps.bdimg.com/libs/html5shiv/3.7/html5shiv.min.js"></script> <script src="http://apps.bdimg.com/libs/respond.js/1.4.2/respond.min.js"></script> <![endif]--> {% block css %} {% endblock css %} {% block js %} {% endblock js %} </head> <body> <div> <div> {% block content %} <h1>自强 新闻网</h1> {% endblock content %} </div> </div> <script src="http://apps.bdimg.com/libs/jquery/1.11.1/jquery.min.js"></script> <script src="http://apps.bdimg.com/libs/bootstrap/3.3.0/js/bootstrap.min.js"></script> {% include "baidutongji.html" %} </body> </html>
templates/baidutongji.html
<!-- 这里放统计的代码 --> <!-- 百度统计 begin--> <div style="display:none" hidden> <script type="text/javascript"> var _bdhmProtocol = (("https:" == document.location.protocol) ? " https://" : " http://"); document.write(unescape("%3Cscript src='" + _bdhmProtocol + "hm.baidu.com/h.js%3Fab8b04b0bf640ac196520db1cd029664' type='text/javascript'%3E%3C/script%3E")); </script> </div> <!-- 百度统计 end-->
2.9.3 视图中检索结果,渲染到模板中显示出来
我们改写视图函数,查询数据库,得到相关的内容:
def column_detail(request, column_slug): column = Column.objects.get(slug=column_slug) return render(request, 'news/column.html', {'column': column}) def article_detail(request, article_slug): article = Article.objects.get(slug=article_slug) return render(request, 'news/article.html', {'article': article})
在 news 下新建 templates 文件夹,再在 templates 下新建 news 文件夹,我们来写两个模板文件:
栏目模板,视图传递过来了一个 column object
news/templates/news/column.html (或者 项目下的 templates/news/column.html 路径)Django 都能找到它们
{% extends "base.html" %} {% block title %} {{ column.title }} {% endblock title %} {% block content %} 栏目简介:{{ column.intro }} 栏目文章列表: 还需要完善 {% endblock content %}
文章模板,视图传递过来了一个 article object
news/templates/news/article.html (或者 项目下的 templates/news/article.html)
{% extends "base.html" %} {% block title %} {{ article.title }} {% endblock title %} {% block content %} <h1>文章标题: {{ article.title }}</h1> <div id="main"> {{ article.content }} </div> {% endblock content %}
我们先在首页显示所有栏目及链接:
news/views.py
def index(request): columns = Column.objects.all() return render(request, 'index.html', {'columns': columns})
项目下 templates/index.html
{% extends "base.html" %} {% block title %} 首页 {% endblock title %} {% block content %} <ul> {% for column in columns %} <li> <a href="{{ column.get_absolute_url }}"> {{ column.name }} </a> </li> {% endfor %} </ul> {% endblock content %}
我们打开开发服务器(开发服务器可以一直开着,会自动刷新),查看首页:
首页已经可以显示所有栏目了,但是栏目页我们还没有完善,如果获取栏目相关的文章呢?
我们再次在终端测试,安装 bpython,会提示相关的属性和方法
python manage.py shell
下面是测试结果:
>>> from news.models import Column >>> c = Column.objects.all()[0] >>> c <Column: 体育新闻> >>> c.article_set.all() [<Article: 体育新闻_1>, <Article: 体育新闻_2>, <Article: 体育新闻_3>, <Article: 体育 新闻_4>, <Article: 体育新闻_5>, <Article: 体育新闻_6>, <Article: 体育新闻_7>, <Artic le: 体育新闻_8>, <Article: 体育新闻_9>, <Article: 体育新闻_10>]
我们发现用 column.article_set.all() 方法可以获取栏目相关的文章。
我们改一下 news/templates/news/column.html
{% extends "base.html" %} {% block title %} {{ column.title }} {% endblock title %} {% block content %} <p>栏目名称:{{ column.name }}</p> 栏目简介:{{ column.intro }} 栏目文章列表: <ul> {% for article in column.article_set.all %} <li> <a href="{{ article.get_absolute_url }}">{{ article.title }}</a> </li> {% endfor %} </ul> {% endblock content %}
我们再次访问开发服务器,会发现,栏目已经可以显示相关的文章了:
但是由于我们前期写的时候的一个 bug,多篇文章可以共用一个网址,所以每个文章的 slug 都有三个与之对应的文章,【临时办法】如果想要看到文章的内容页,我们改一下视图函数,有多个时,显示第一篇(这样做有问题,后面完善)
def article_detail(request, article_slug): article = Article.objects.filter(slug=article_slug)[0] return render(request, 'news/article.html', {'article': article})
我们再点击文章的名称,就可以看到:
今天就写到这里,我们明天再修复这个 bug,有能力的同学可以自己想想办法修复这样问题。