自强学堂
自强学堂:学习、分享、让你更强!
Django 教程HTMLCSSJAVASCRIPTJQUERYSQLPHPBOOTSTRAPANGULARXML
 

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

后台首页:


后台栏目列表:


后台文章列表:


文章编辑页面:

new_content.png

我们已经可以在后台进行更改和保存文章了,但是在编辑新闻的时候是不是觉得不爽,没有一个编辑器,我们下面来集成百度的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 同一级目录,如图所求:

QQ20150729-1@2x.png


在 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/')
    
    ...

再次打开,发现,已经可以使用了:

QQ20150729-2@2x.png

后台的功能就讲到这里,大家可以后期自己再进行完善。


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/  (注意端口,改成你自己的)会得到:

QQ20150729-3@2x.png

我们发现 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 %}

我们打开开发服务器(开发服务器可以一直开着,会自动刷新),查看首页:

QQ20150729-4@2x.png

首页已经可以显示所有栏目了,但是栏目页我们还没有完善,如果获取栏目相关的文章呢?

我们再次在终端测试,安装 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 %}

我们再次访问开发服务器,会发现,栏目已经可以显示相关的文章了:

QQ20150729-6@2x.png

但是由于我们前期写的时候的一个 bug,多篇文章可以共用一个网址,所以每个文章的 slug 都有三个与之对应的文章,【临时办法】如果想要看到文章的内容页,我们改一下视图函数,有多个时,显示第一篇(这样做有问题,后面完善)

def article_detail(request, article_slug):
    article = Article.objects.filter(slug=article_slug)[0]
    return render(request, 'news/article.html', {'article': article})

我们再点击文章的名称,就可以看到:

QQ20150729-7@2x.png

至此代码下载:zip下载   github 地址

今天就写到这里,我们明天再修复这个 bug,有能力的同学可以自己想想办法修复这样问题。

↑ 上方为自强学堂赞助商,非常荣幸有他们支持自强学堂的发展,感兴趣的了解一下他们的产品。