[关闭]
@hpp157 2016-08-24T04:43:20.000000Z 字数 20055 阅读 4410

Django学习 第三章 建立一个静态网站生成器

django python


Django是帮助完美主义者按时完成工作的Python网络开发框架,我们在第二章提到过,Django最初是在一个新闻中心开发的,用来让记者快速的编辑和发布新闻到网站上。自从2005年开放源代码以来,许多开发者用它来快速的开发网站和应用程序。那个时候就成为python领域web开发的首选框架,后来成长为web开发领域的领跑者。我们现在到处都能看到Django开发的身影,比如,Instagram和Pinterest就使用了Django开发。

选择一个好的框架对于任何项目的开始阶段来说都是非常重要的一件事。设定一个恰当的工作流程也是排在首位的事情。设计师和开发者能够高效率的并肩工作是一个项目能够顺利完成的至关重要的因素。当设计师和开发者同另一个人沟通时,Django的效率如何呢?
我们发现的一个方法是设计师和开发者并行的开始项目,互不干扰。设计和开发同时用Django框架做ta们各自的部分。接下来的教程里,我们会在Django项目中通过创建一个static site application 实现这一目标.

用Django创建静态网站

很多开发者认为Django并不是一个开发静态网站的web框架。在这一章里,我们会带你使用Django框架一步步的创建一个网站的快速原型。来帮助我们快速的生成网站和应用。

什么是快速原型

快速原型是在系统开发周期,凭借系统开发人员对用户需求的了解和系统主要功能的要求,在强有力的软件环境支持下,迅速构造出系统的初始原型,然后与用户一起不断对原型进行修改、完善,直到满足用户需求。

  1. 观察和分析。这部分的,要从你的最终目标用户来入手,包括你们团队的头脑风暴以及你想做什么(比如,谁是我们的用户,每个用户像从我们的产品中得到什么等)
  2. 创建。使用html,css,javascript快速的构造一个初始原型,仅仅使用灰色框示例和简单的布局。
  3. 运行。这是发现问题,消除误解,开发者和用户充分协调的过程,用户在开发者的指导下运行原型,使用过程中努力发现不合理的部分,各类人员在共同运行原型的过程中加深对系统的了解。

初始化项目布局

本章的开发环境 python3.4 django1.7 操作系统:win10
开发者头条客户端好像不支持外链图片,用手机浏览器打开可以看到图片

和前面两章一样,我们用单入口文件到方法来为我们的项目生成setting,以及其它组件。我们也需要创建一些用startproject命令可以自动生成的其它文件。让我们开始把。

文件/文件夹脚手架

我们的快速原型网站运行只需要这几个模块, views,URLs,以及template,让我们开始创建网站的结构。我们会创建一个叫做sitebuilder的文件夹,存放init.py, views.py, urls.py以及templates ,static的文件夹,static文件夹存放两个文件夹js ,css.图示如下

项目结构

基本设置

根据我们的文件布局来看,只需要很少的设置就能够让项目运行起来。首先,向prototypes.pyp中添加基本设置,同时,我们也会把我们sitebuilder程序添加到INSTALLED_APPS设置里。

  1. import sys
  2. from django.conf import settings
  3. settings.configure(
  4. DEBUG=True,
  5. SECRET_KEY='mykey',
  6. ROOT_URLCONF='sitebuilder.urls',
  7. MIDDLEWARE_CLASSES=(),
  8. INSTALLED_APPS=(
  9. 'django.contrib.staticfiles',
  10. 'django.contrib.webdesign',#经测试,这个只能用在django1.7中,后来的版本内置了这个标签
  11. 'sitebuilder',#注册我们的项目
  12. ),
  13. STATIC_URl='/static/',
  14. )
  15. if __name__ == '__main__':
  16. from django.core.management import execute_from_command_line
  17. execute_from_command_line(sys.argv)

说明:在django中,django.contrib里面的contrib意思是捐赠,附加的意思和单词contribute差不多,它是一个子框架,里边有很多附加的程序能够加速我们的web开发,详细的介绍可以参考链接:
django.contrib介绍

这里需要说明的是django.contrib.webdesign其中的webdesign:对设计者非常有用的Django扩展。它只包含一个模板标签{% lorem %}。这个标签可以向我们的应用自动添加占位描述文本。它在django1.8后被去除了,成为了一个内置标签。

为了初始化我们的项目,还需要在urls.py中添加一个URL设置,打开sitebuilder/urls.py添加下面的初始设置:

  1. urlpatterns=()

好了,运行命令,启动服务,打开浏览器看下结果

  1. python prototype.py runserver

开发者头条不支持外链,看图片的话用浏览器打开

随着基本设置的完成以及我们的应用已经运行起来,接下来可以开始创建让protype渲染的页面的模板结构了

页面渲染

大多数的django项目,都会为前端页面创建一个固定的基础模板,基础模板做好了,你就可以开始系统的安排剩下的模板了。

创建我们的基本模板

我们这个静态网站生成器的项目,它的整个site网站都是从一些最基本的模板上创建而来的。这些模板由基本的布局页面组成,为整个项目的每一个页面提供基本的结构设置。对于这个页面来说,我们要创建两个基本的模板,base.htmlpage.html
这两个页面放在 sitebuilde/templates下面

  1. <!doctype html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport"
  6. content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  7. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  8. <title>{% block title %}Document{% endblock %}</title>
  9. </head>
  10. <body>
  11. {% block content %}{% endblock %}
  12. </body>
  13. </html>

因为我们正在创建的是一个静态的网站,因此我们需要给我们的模板添加一个Django中的静态文件标签。接着,创建一个空的css文件site.css。放在sitebuilder/static/css下面

  1. $ touch sitebuilder/static/css/site.css

现在我们给base.html添加新的结构

  1. {% load staticfiles %}
  2. <!doctype html>
  3. <html lang="en">
  4. <head>
  5. <meta charset="UTF-8">
  6. <meta name="viewport"
  7. content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  8. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  9. <link rel="stylesheet" href="{% static 'css/site.css' %}">
  10. <title>{% block title %}Document{% endblock %}</title>
  11. </head>
  12. <body>
  13. {% block content %}{% endblock %}
  14. </body>
  15. </html>

由于我们还没有添加任何具体的url模式和视图views。所以在启动服务器打开浏览器查看时我们看到的页面还是之前的it worked!模板。
在下一个小节,我们开始添加urlviews以及创建一个page页面

静态页面生成器

渲染页面需要了解django的模板引擎工作原理,新手推荐一篇文章看下,了解一下什么是context,对这个比较熟悉的可以忽视。django模板高级进阶

就跟其他的静态页面生成器一样,能让我们更加快速的创建新的页面。我们需设置一个路径变量来引用文件路径,渲染页面的视图,以及指向page的url

  1. $ mkdir pages

这个pages目录是放置我们所有的静态文件的地方。现在让我们设置一个变量来指向这个位置。

  1. import os
  2. import sys
  3. from django.conf impot settings
  4. BASE_DIR=os.path.dirname(__file__)
  5. ....
  6. STATIC='/static/'
  7. STIE_PAGES_DIRECTORY=os.path.join(BASE_DIR,'pages'),
  8. ...

现在我们可以很容易的访问pages这个文件夹。page中的页面布局是由template文件夹中的文件决定的。这会有助于内容和页面布局之间的分离。
在我们输出静态页面之前,需要先创建views.py视图,动态的渲染pages页面。让我们给pages文件夹中的每个页面添加一个view来渲染它们。

  1. import os
  2. from django.conf import settings
  3. from django.http import Http404
  4. from django.shortcuts import render
  5. from django.template import Template
  6. from django.utils._os import safe_join
  7. def get_name_or_404(name):
  8. try:
  9. file_path=safe_join(settings.SITE_PAGES_DIRECTORY,name)
  10. except ValueError:
  11. raise Http404('page1 not found')
  12. else:
  13. if not os.path.exists(file_path):
  14. raise Http404('page not found2')
  15. with open(file_path,'r') as f:
  16. page = Template(f.read())
  17. return page
  18. def page(request,slug='index'):
  19. file_name = '{}.html'.format(slug)
  20. page=get_name_or_404(file_name)
  21. context={
  22. 'slug': slug,
  23. 'page': page,
  24. }
  25. return render(request,'page.html',context

-- 使用safe_join函数将page的文件和tmplate的filename合并起来会返回一个绝对的路径
--open(file_path,'r')每打开一个文件,就会用文件内容实例化一个新的django模板对象。
--解析模板是要传递一段context的,模板渲染就是从context中获取值来替换模板中的变量,并执行所有模板。这里传递了包含page和slug的context给page.html这个渲染模板

要完成视图,我们还要创建templates/page.html这个被渲染的页面,{% include page %}中的page变量要从传递过来的context中获取

  1. {% extends "base.html" %}
  2. {% block title %}{{ block.supper }}-{{ slug|capfirst }}{% endblock %}
  3. {% block content %}
  4. {% include page %}
  5. {% endblock %}

接下来就该处理url了,在sitebuilder/urls.py中编写对应的url

  1. from django.conf.urls import patterns, include, url
  2. from django.contrib import admin
  3. from .views import page
  4. urlpatterns = patterns('',
  5. # Examples:
  6. # url(r'^$', 'sitebuilder.views.home', name='home'),
  7. # url(r'^blog/', include('blog.urls')),
  8. url(r'^(?P<slug>[\w./-]+)/$', page , name='page'),
  9. url(r'^$', page ,name='homepage'),
  10. )

模板制作好了之后,我们开始添加想要的内容了,服务端的根目录会调用没有slug参数的Page视图,意思就是使用slug默认的参数值index,要渲染page首页,我们在pages文件夹中添加一个index.html文件

  1. <h1>welcome to site</h1>
  2. <p>hello word</p>

运行命令,重启服务器,然后打开浏览器http://localhost:8000查看效果(使用浏览器打开才可以看到图片)

基本样式

我们使用bootstrap3.2和jquery2.1.1来构建基本样式,请下载放置在文件夹中下载地址bootstrap以及jquery

prototypes.py/
pages/
sitebuilder/
    __init__.py
    static/
        js/
            bootstrap.min.js
            jquery.min.js
        css/
            bootstrap-theme.css.map
            bootstrap-theme.min.css
            bootstrap.css.map
            bootstrap.min.css
        fonts/
            glyphicons-halfings-ruglar.eot
            glyphicons-halfing-regular.svg
            glyhpicons-halfing-regular.ttf
            glyphicons-halfing-regular.woff
    templates/
    urls.py
    views.py

现在把这写文件引用到base.html中,(注释处)

  1. {% load staticfiles %}
  2. <!doctype html>
  3. <html lang="en">
  4. <head>
  5. <meta charset="UTF-8">
  6. <meta name="viewport"
  7. content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  8. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  9. #引入bootstrap的css文件
  10. <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
  11. <link rel="stylesheet" href="{% static 'css/site.css' %}">
  12. <title>{% block title %}Document{% endblock %}</title>
  13. </head>
  14. <body>
  15. {% block content %}{% endblock %}
  16. #js文件的添加
  17. <script src="{% static 'js/jquery.min.js' %}"
  18. <script src="{% static 'js/bootstrap.min.js %}"
  19. </body>
  20. </html>

布局layouts和导航条

我们首先创建一个首页的布局,然后创建一些带有导航条的其他的页面。
现在,让我们把index.html文件原来的内容删除,开始对首页布局。

  1. {% load webdesign %}
  2. <div class="jumbotron">
  3. <div class="container">
  4. <h1>Welcome to the Site</h1>
  5. <p>Insert Marketing copy here</p>
  6. </div>
  7. </div>
  8. <div class="container">
  9. <div class="row">
  10. <div class="col-md-6">
  11. <h2>About</h2>
  12. <p>{% lorem %}</p>
  13. </div>
  14. <div class="col-md-6">
  15. <h2>contact</h2>
  16. <p>{% lorem %}</p>
  17. <p>
  18. <a href="{% url 'page' 'contact' %}" class="btn btn-default">
  19. contact us >>
  20. </a>
  21. </p>
  22. </div>
  23. </div>
  24. </div>
  25. <hr>
  26. <footer>
  27. <div class="container">
  28. <p>&copy;You Company</p>
  29. </div>
  30. </footer>

这里面有几点我们要注意的事情,首先,我们根据page的url为应用的其它页面创造url。现在我们可以用指向其它页面的动态链接创建一个静态的网站。我们会在下一个小节把这个弄出来。现在尝试访问其它页面的话,会有个页面不存在的404错误。
还有一点要注意的是,我们使用{% lorem %}标签来给我们的首页产生一些文本占位符,用来代替段落内容或者文章的内容。
我们也需要给site.css添加一点样式来给content一点padding。

body{
    padding:50px 0 30px;
}

因为我们还有添加更多的页面,所以先给base.html添加一个基本的导航吧

  1. {% load staticfiles %}
  2. <!doctype html>
  3. <html lang="en">
  4. <head>
  5. <meta charset="UTF-8">
  6. <meta name="viewport"
  7. content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  8. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  9. <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
  10. <link rel="stylesheet" href="{% static 'css/site.css' %}">
  11. <title>{% block title %}Document{% endblock %}</title>
  12. </head>
  13. <body id="{% block body-id %}body{% endblock %}">
  14. <div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
  15. <div class="container">
  16. <div class="navbar-header">
  17. <button type="button" class="navbar-toggle"
  18. data-toggle="collapse" data-target=".navbar-collapse">
  19. <span class="sr-only">Toggle navgition</span>
  20. <span class="icon-bar"></span>
  21. <span class="icon-bar"></span>
  22. <span class="icon-bar"></span>
  23. </button>
  24. <a href="/" class="navbar-brand">Rapid Prototype</a>
  25. </div>
  26. <div class="collapse navbar-collapse">
  27. <ul class="nav navbar-nav">
  28. <li {% if slug == 'index' %}class="active"{% endif %}>
  29. <a href="/">Home</a>
  30. </li>
  31. <li {% if slug == 'contact' %}class="active"{% endif %}>
  32. <a href="{% url 'page' 'contact' %}">Contact</a>
  33. </li>
  34. <ul class="nav navbar-nav navbar-right">
  35. <li {% slug == 'login' %}class="active">{% endif %}>
  36. <a href="{% url 'page' 'login' %}">Login</a>
  37. </li>
  38. </ul>
  39. </ul>
  40. </div>
  41. </div>
  42. </div>
  43. {% block content %}{% endblock %}
  44. <script src="{% static 'js/jquery.min.js' %}"
  45. <script src="{% static 'js/bootstrap.min.js' %}"
  46. </body>
  47. </html>

等于==前后都要有空格

你可能注意到了,我们给<body>标签添加了{% block body-id %}body{% endblock %}模板标签,这个可以帮助给每一个页面定位一个css样式。我们也想给page.html中添加一个这样的标签,我们使用page页面的{{slug}}这个变量来为每一个页面的body动态的生成一个id。

  1. {% extends "base.html" %}
  2. {% block title %}{{ block.super }}-{{ slug|capfirst }}{% endblock %}
  3. {% block body-id %}{{ slug|slugify }} {% endblock %}
  4. {% block content %}
  5. {% include page %}
  6. {% endblock %}

这里我们使用了slugify这个过滤器,它可以把我们页面生成的任何slugs值转换成小写,来保证id的值的格式上的一致性。

现在来运行一下程序,打开浏览器
此处输入图片的描述

现在基本结构已经有了,现在我们给导航里的Contact和Login添加页面。首先添加login页面。pages/login.html

  1. <div class="container-fluid">
  2. <div class="row">
  3. <form class="form form-signup col-md-4 col-md-offset-4">
  4. <h2 class="form-signin-heading">
  5. Login to Your Account
  6. </h2>
  7. <div class="form-group">
  8. <input type="email" class="form-control"
  9. placeholder="Email address" required="" autofocus="" autocomplete="off">
  10. </div>
  11. <div class="form-group">
  12. <input type="password" class="form-control"
  13. placeholder="Password" required="" autocomplete="off">
  14. </div>
  15. <div class="form-group">
  16. <button class="btn btn-lg btn-primary btn-block"
  17. type="submit">Login</button>
  18. </div>
  19. </form>
  20. </div>
  21. </div>

tupian

已经有了一个漂亮的登陆页面了,但是还没有登陆的功能。这只是一个非常初级的例子,告诉设计师和开发人员怎么才能平行互不干扰的工作。现在登陆表单已经准备和后台进行同步(synced)了,当然它也已经有了一些上下文(context)和样式了。让我们同样的为Contact链接创建一个页面。

  1. <div class="container">
  2. <div class="row">
  3. <form action="" class="form-contact col-md-6" role="form">
  4. <h2>Contact Us</h2>
  5. <div class="form-group">
  6. <input type="email" class="form-control"
  7. placeholder="Email address..." required="">
  8. </div>
  9. <div class="form-group">
  10. <input type="text" class="form-control"
  11. placeholder="Title" required="">
  12. </div>
  13. <div class="form-group">
  14. <textarea class="form-control" required="" placeholder="Your message here ..."></textarea>
  15. </div>
  16. <div class="form-group">
  17. <button class="btn btn-lg btn-primary btn-block" type="submit">Submit</button>
  18. </div>
  19. </form>
  20. <div class="col-md-4 col-md-offset-2">
  21. <h2>Our Office</h2>
  22. <address>
  23. <strong>Company Name</strong>
  24. 123 Something St<br>
  25. New York, NY 0000<br>
  26. (212) 555 -1234
  27. </address>
  28. </div>
  29. </div>
  30. </div>

图片
再说下,这个表单目前还不能正常的工作,我们已经快速的创建了一些简单的原型页面。这是快速原型的核心观念之一,可以用来说明设计师和开发者可以无缝的平行工作。

在下一个小节里,我们通过一个简单的管理命令来生成动态内容(dynamic content)。

生成静态内容(Generating Static Content)

虽然我们已经有了一个可以工作的原型应用可以创建一个简单的静态网站。但是,我们的应用在技术上还不算一个可以生成内容的静态网站。让我们来编写一个自定义的管理命令来输出内容到静态页面

Setting设置

我们需要在sitebuilder中添加几个文件夹来存放我们自定义的管理命令。

prototypes.py
pages/
sitebuilder
    __init__.py
    management/
        __init__.py
        commands/
            __init__.py
            build.py
    static/
        js
        css/
        fonts/
    templates/
    urls.py
    views.py

在我们准备给项目增建一个命令之前,我们需要在settings里面添加一个静态页面的输出目录,

  1. INSTALLED_APPS=(
  2. 'django.contrib.admin',
  3. 'django.contrib.auth',
  4. 'django.contrib.contenttypes',
  5. 'django.contrib.sessions',
  6. 'django.contrib.messages',
  7. 'django.contrib.staticfiles',
  8. 'django.contrib.webdesign',
  9. 'sitebuilder'
  10. ),
  11. STATIC_URL = '/static/',
  12. SITE_PAGES_DIRECTORY=os.path.join(BASE_DIR,'pages'),
  13. SITE_OUTPUT_DIRECTORY=os.path.join(BASE_DIR,'_build'),
  14. STATIC_ROOT=os.path.join(BASE_DIR,'_build','static'),

SITE_OUTPUT_DIRECTORYSTATIC_ROOT会存储生成的内容并且不会被检入(checked into)版本控制里.

自定义管理命令

现在我们可以开始创建我们的自定义管理命令来生成和输出我们的静态网站。我们使用build.py文件开始编写

  1. import os
  2. import shutil
  3. from django.conf import settings
  4. from django.core.management import call_command
  5. from django.core.management.base import BaseCommand
  6. from django.core.urlresolvers import reverse
  7. from django.test.client import Client
  8. def get_pages():
  9. for name in os.listdir(settings.SITE_PAGES_DIRECTORY):
  10. if name.endswith('.html'):
  11. yield name[:-5]
  12. class Command(BaseCommand):
  13. help = 'Build static site output'
  14. def handle(self, *args, **options):
  15. if os.path.exists(settings.SITE_OUTPUT_DIRECTORY):
  16. shutil.rmtree(settings.SITE_OUTPUT_DIRECTORY)
  17. os.mkdir(settings.SITE_OUTPUT_DIRECTORY)
  18. os.makedirs(settings.STATIC_ROOT)
  19. call_command('collecstatic',interactive=False,clear=True,verbosity=0)
  20. client=Client()
  21. for page in get_pages():
  22. url =reverse('page',kwargs={'slug':page})
  23. response=client.get(url)
  24. if page == 'index':
  25. output_dir=settings.SITE_OUTPUT_DIRECTORY
  26. else:
  27. output_dir=os.path.join(settings.SITE_OUTPUT_DIRECTORY,
  28. page)
  29. os.makedirs(output_dir)
  30. with open(os.path.join(output_dir,'index.html'),'wb') as f:
  31. f.write(response.content)
  1. 首先检查输出目录是否存在,已经存在的话,删除创建一个新的干净目录
  2. 25行,使用call_command工具来运行collecstatic命令来复制网站所有的静态资源到STATIC_ROOT目录
  3. 28行,循环并收集pages文件夹中的所有html文件。
  4. 38行,我们使用django的test client模拟爬虫抓取site pages并且把渲染出来的内容写到SITE_OUTPUT_DIRECTORY

现在已经在命令行运行命令

  1. $ python prototype.py build

看下网站根目录是不是已经有了_build的文件夹了呢,里面有静态的html文件

创建单个页面

和任何项目一样,有时候我们(there are timee when) 只想要编辑少数的几个文件,但是我们现在的命令会重建整个_build中的所有静态文件。让我们设置下,允许一次只改动单个文件吧。

  1. import os
  2. import shutil
  3. from django.conf import settings
  4. from django.core.management import call_command
  5. from django.core.management.base import BaseCommand ,CommandError
  6. from django.core.urlresolvers import reverse
  7. from django.test.client import Client
  8. def get_pages():
  9. for name in os.listdir(settings.SITE_PAGES_DIRECTORY):
  10. if name.endswith('.html'):
  11. yield name[:-5]
  12. class Command(BaseCommand):
  13. help = 'Build static site output'
  14. def handle(self, *args, **options):
  15. if args:
  16. pages=args
  17. available=list(get_pages())
  18. invalid=[]
  19. for page in pages:
  20. if page not in available:
  21. invalid.append(page)
  22. if invalid:
  23. msg='Invalid pagesL:{}'.format(','.join(invalid))
  24. raise CommandError(msg)
  25. else:
  26. pages=get_pages()
  27. if os.path.exists(settings.SITE_OUTPUT_DIRECTORY):
  28. shutil.rmtree(settings.SITE_OUTPUT_DIRECTORY)
  29. os.mkdir(settings.SITE_OUTPUT_DIRECTORY)
  30. os.makedirs(settings.STATIC_ROOT)
  31. call_command('collectstatic', interactive=False, clear=True, verbosity=0)
  32. client = Client()
  33. for page in get_pages():
  34. url = reverse('page', kwargs={'slug': page})
  35. response = client.get(url)
  36. if page == 'index':
  37. output_dir = settings.SITE_OUTPUT_DIRECTORY
  38. else:
  39. output_dir = os.path.join(settings.SITE_OUTPUT_DIRECTORY,
  40. page)
  41. if not os.path.exists(output_dir):
  42. os.makedirs(output_dir)
  43. with open(os.path.join(output_dir, 'index.html'), 'wb') as f:
  44. f.write(response.content)

23行代码检查命令运行时是否传入参数,每次可以传不止一个。
30行代码,如果传入的页面不存在,抛出一个错误
51行,因为我们不是每次都毁掉整个目录重建,所以我们要处理一种新情况:目录是否已经存在

现在我们可以运行命令来编译一个单页面了。

  1. $ python prototypes.py build index

在下一个小节,我们会使用Django的static filesdjango-conpressor来更深入一步的处理静态文件。

服务和压缩静态文件

正如web上的任何请求一样,有非常多的针对每个页面的渲染处理,每个请求都返回一个响应,浪费我们的页面加载时间。为了节省这些时间,django内置了一个缓存框架给我们的web应用。
创建压缩后的静态文件是另一种加速页面加载的方法,在这一节中,我们会对hashing和使用django-compressor这两种方法都进行探讨来压缩这些文件。

Hashing我们的css和js文件

Django带来来很多有帮助的设置来让我们存储和为image,css和js文件服务。我们会使用其中的一种来创建我们文件名字的哈希,这是一个非常有用的技巧当文件改变时需要打破浏览器缓存时,通过这些基于文件内容的名字( or hashing),web服务器可以设置Expire给未来的这些文件。

  1. STATIC_URL = '/static/',
  2. SITE_PAGES_DIRECTORY=os.path.join(BASE_DIR,'pages'),
  3. SITE_OUTPUT_DIRECTORY=os.path.join(BASE_DIR,'_build'),
  4. STATIC_ROOT=os.path.join(BASE_DIR,'_build','static'),
  5. STATICFILES_STORAGE='django.contrib.staticfiles.storage.CachedStaticFilesStorage',

设置好STATICFILES_STORAGE之后,我们的文件会得到一个哈希,当DEBUG为False的时候,现在我们就给build命令添加上DEBUG的设置

  1. ...
  2. def handle(self, *args, **options):
  3. settings.EDBUG=False
  4. ...

现在让我们运行管理命令,看下_build文件夹中生成hash文件

  1. $python prototypes.py build

打开_build文件夹中的index.html文件,可以看到css文件的引用名称以经变成了哈希值

<link rel="stylesheet" href="/static/css/site.756ed2812363.css">

压缩我们的静态文件

另一种加快页面加载的方法是通过压缩文件减小css和js文件的体积。django-compressot这个常用模块就是用来帮助我们完成这样的工作的。它有不少的强大功能,诸如LESS/Sass的编译等.
要想开始使用这个模块,我们需要先安装它

  1. $sudo pip install django-compressor
  2. #windows用户不用加sudo

现在我们要去掉之前设置的哈希缓存,添加一些必要设置。

  1. INSTALLED_APPS=(
  2. 'django.contrib.admin',
  3. 'django.contrib.auth',
  4. 'django.contrib.contenttypes',
  5. 'django.contrib.sessions',
  6. 'django.contrib.messages',
  7. 'django.contrib.staticfiles',
  8. 'django.contrib.webdesign',
  9. 'sitebuilder',
  10. 'compressor'
  11. ),
  12. STATIC_URL = '/static/',
  13. SITE_PAGES_DIRECTORY=os.path.join(BASE_DIR,'pages'),
  14. SITE_OUTPUT_DIRECTORY=os.path.join(BASE_DIR,'_build'),
  15. STATIC_ROOT=os.path.join(BASE_DIR,'_build','static'),
  16. STATICFILES_FINDERS=(
  17. 'django.contrib.staticfiles.finders.FileSystemFinder',
  18. 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
  19. 'compressor.finders.CompressorFinder',
  20. )

如你在代码块中看到的,我们在INSTALLED_APPS列表中添加了compressor应用, 还添加了STATICFILES_FINDERS这个列表,中间使用了框架附加的contrib程序。
为了让我们运行命令时开始压缩,我们要对命令进行改造。添加setting.COMPTESSOR_ENABLED=True的选项,以及在收集了静态文件的命令运行后运行压缩命令call_command('compress',interacitve=False,force=True)

  1. def handle(self, *args, **options):
  2. settings.DEBUG=False
  3. settings.COMPRESS_ENABLED=True
  4. ...
  5. call_command('collectstatic', interactive=False, clear=True, verbosity=0)
  6. call_command('compress',interactive=False,force=True)
  7. client = Client()
  8. ...

现在我们可以添加{% compress %}块标签到我们静态资源上(base.html)

  1. {% compress css %}
  2. <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
  3. <link rel="stylesheet" href="{% static 'css/site.css' %}">
  4. {% endcompress %}
  5. ...
  6. {% compress js %}
  7. <script src="{% static 'js/jquery.min.js' %}"></script>
  8. <script src="{% static 'js/bootstrap.min.js' %}"></script>
  9. {% endcompress %}

现在运行build命令,结果如下:

生成动态内容

就像目前看到的一样,django中的快速原型模板已经可以创建一个简单的网站,唯一缺少的就是还不能生成动态的内容。一种实现其它静态站点的方法是使用一个YAML文件,如果我们不想给应用中添加不必要的文件的话,但是使用这个方案不是太理想。相反,我们会在views.py视图中添加少许的元素,来让动态内容使用。

更新我们的模板

目前我们的静态网站已经有一些基本的模板了,让我们添加一个pricing.html文件来生成动态内容

  1. {% load webdesign %}
  2. <div class="container">
  3. <div class="row">
  4. <div class="col-md-12">
  5. <h2>Available Pricing Plans</h2>
  6. </div>
  7. </div>
  8. <div class="rol">
  9. <div class="col-md-4">
  10. <div class="panel panel-success">
  11. <div class="panel-heading">
  12. <h4 class="text-center">Starter plan - free</h4>
  13. </div>
  14. <ul class="list-group list-group-flush text-center">
  15. <li class="list-group-item">Feature #1</li>
  16. <li class="list-group-item disabled">Feature #2</li>
  17. <li class="list-group-item disabled">Feature #3</li>
  18. </ul>
  19. <div class="panel-footer">
  20. <a href="" class="btn btn-lg btn-block btn-success">
  21. Buy Now!
  22. </a>
  23. </div>
  24. </div>
  25. </div>
  26. <div class="col-md-4">
  27. <div class="panel panel-danger">
  28. <div class="panel-heading">
  29. <h4 class="text-center">Basic Plan -$10</h4>
  30. </div>
  31. <ul class="list-group list-group-flush text-center">
  32. <li class="list-group-item">Feature #1</li>
  33. <li class="list-group-item disabled">Feature #2</li>
  34. <li class="list-group-item disabled">Feature #3</li>
  35. </ul>
  36. <div class="panel-footer">
  37. <a href="" class="btn btn-lg btn-block btn-info">
  38. Buy Now
  39. </a>
  40. </div>
  41. </div>
  42. </div>
  43. <div class="col-md-4">
  44. <div class="panel panel-info">
  45. <div class="panel-heading">
  46. <h4 class="text-center">
  47. Enterprise Plan -$20 /month
  48. </h4>
  49. </div>
  50. <ul class="list-group list-group-flush text-center">
  51. <li class="list-group-item">Feature #1</li>
  52. <li class="list-group-item disabled">Feature #2</li>
  53. <li class="list-group-item disabled">Feature #3</li>
  54. </ul>
  55. <div class="panel-footer">
  56. <a href="" class="btn btn-lg btn-block btn-danger">
  57. Buy Now
  58. </a>
  59. </div>
  60. </div>
  61. </div>
  62. <div class="row">
  63. <div class="col-md-12">
  64. <p>{% lorem %}</p>
  65. </div>
  66. </div>
  67. </div>

运行服务器,打开浏览器查看结果吧(开发者头条不支持外链,请用浏览器打开)
此处输入图片的描述

添加元数据

我们现在要对views.py视图进行重构,让views.py有读取{% block %}的功能

  1. import json
  2. import os
  3. from django.conf import settings
  4. from django.http import Http404
  5. from django.shortcuts import render
  6. from django.template import Template,Context
  7. from django.template.loader_tags import BlockNode
  8. from django.utils._os import safe_join
  9. def get_name_or_404(name):
  10. try:
  11. file_path=safe_join(settings.SITE_PAGES_DIRECTORY,name)
  12. except ValueError:
  13. raise Http404('page1 not found')
  14. else:
  15. if not os.path.exists(file_path):
  16. raise Http404('page not found2')
  17. with open(file_path,'r') as f:
  18. page = Template(f.read())
  19. meta=None
  20. for i, node in enumerate(list(page.nodelist)):
  21. if isinstance(node,BlockNode) and node.name == 'context':
  22. meta=page.nodelist.pop(i)
  23. break
  24. page._meta =meta
  25. return page
  26. def page(request,slug='index'):
  27. file_name = '{}.html'.format(slug)
  28. page=get_name_or_404(file_name)
  29. context={
  30. 'slug': slug,
  31. 'page': page,
  32. }
  33. if page._meta is not None:
  34. meta = page._meta.render(Context)
  35. extra_context=json.loads(meta)
  36. context.update(extra_context)
  37. return render(request,'page.html',context)

23行代码循环page的nodelist并且通过名字来检查BlockNode.BlockNode是个定义的类用来在模板中创建{% block%}元素,如果 context BlockNode被发现,就定义一个包含content的metavariable的变量。
38行,meta的内容使用python的json模块来渲染,{% block%}转换成平常的格式

我们现在在base.html文件中添加几个<meta>标签,用默认值。

  1. ...
  2. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  3. <meta name="description" content="{{ description|default:'Default prototype description' }}"
  4. <meta name="keywords" content="{{ keywords|default:'prototype' }}">
  5. <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
  6. ...

我们以pricing页面为例,用新的content block来更新这个页面

  1. {% load webdesign %}
  2. {% block content %}
  3. {
  4. "description":"product Pricing Plans",
  5. "keywords":"Products,pricing,buy me",
  6. }
  7. {% endblock %}
  8. <div class="container">

然后我们看下pricing页面的源代码,你会看到meta和keywords的变化

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注