Django 中对静态文件的支持


处理静态文件,尤其是在开发时,是一件很头疼的事情。在这篇文章中,我们将会讨论一些设置,目录结构和他们之间的相互影响。设置好 DEBUG = True 然后我们开始开发吧。

我们将会创建一个 Django 项目,这样可以让我们更好的了解我们讨论的这些这些文件在什么目录中。我们将会使用 Django1.4,这些都也能在 Django1.3 中工作,因为没有在 Django1.2 下进行测试,所以对1.2版本下是否有问题不是很清楚。

创建项目

如果你不需要这部分,可以直接跳到处理静态文件这一节。只要保证你看过了这节底部的目录结构并对其有了解,这样你阅读后面的内容会更舒服。

我们将在命名为 staticvirt 的虚拟环境中做所有事情,所以我们需要命令

~$ virtualenv staticvirt  

接下来我们需要在这个虚拟环境中创建一个 Django 项目。确保你进入了虚拟环境的目录,并且激活了该环境。同时也要保证在这个虚拟环境中安装了 Django,因为我们不想污染系统的包环境。

~$ cd staticvirt/ 
 ~/staticvirt$ source bin/activate  
(staticvirt)~/staticvirt$ pip install django==1.4  

创建一个 Django 项目。

django-admin.py startproject test_project  

进入项目所在系统。

cd test_project/  

让我们看看现在的目录结构。

(staticvirt)~/staticvirt/test_project$ tree  
.
 |-- manage.py  
*-- test_project  |-- __init__.py
|-- settings.py  
|-- urls.py  
*-- wsgi.py    1 directory, 5 files  

现在查看下 test_project/settings.py 的内容。搜索所有包括 static 的行,下面我列出所有包括 static 的行。

STATIC_ROOT = ''   

STATIC_URL = '/static/'  

STATICFILES_DIRS = ()  

STATICFILES_FINDERS = (  'django.contrib.staticfiles.finders.FileSystemFinder',     
'django.contrib.staticfiles.finders.AppDirectoriesFinder',   
'django.contrib.staticfiles.finders.DefaultStorageFinder', 
)   


INSTALLED_APPS = (    
....   
....   
'django.contrib.staticfiles',   
....   
)  

然而,这里所看到的都是 Django 提供的默认设置,我们没有做任何的设置。

我们创建一个 app,我们将会在里面创建一个 template,然后会写一些静态文件,比如样式文件,然后在模板中使用这个样式文件。

python manage.py startapp some_app  

将 some_app 添加进 test_project/settings.py 中的INSTALLED_APPS。

我们需要一个 urls.py 文件来为 some_app 定制路由。项目的 urls.py应该包括 some_app 中的 urls.py。所以,我们在 test_project/urls.py 中添加以下一行。

url(r'^some_app/', include('some_app.urls'))  

在 some_app 的 urls.py 文件中添加以下内容。

url(r'^home$', direct_to_template, {"template": "some_app/home.html"})  

创建一个名为 templates 的目录,然后将其添加进TEMPLATE_DIRS。我在 manage.py 同级目录下创建 templates。

将 templates 添加进 TEMPLATE_DIRS 我需要做以下设定,如果你也使用跟我一样的目录结构,你也需要同样的设定。

PROJECT_DIR = os.path.dirname(__file__)  

TEMPLATE_DIRS = (
os.path.join(PROJECT_DIR, '../templates'),  
)  

我们需要为 some_app 创建 home.html 文件,然后你需要进入templates 目录。所以创建 templates/come_app/home.html,在文件中写入以下内容。

<html>  
<body>  
<h1>This is home for some_app</h1>   
</body>  
</html>  

现在查看一下项目的目录结构,便于消除一些不清楚的地方。

~/staticvirt/test_project$ tree -I *.pyc 
. 
|-- manage.py 
|-- some_app 
| |-- __init__.py   
| |-- models.py   
| |-- tests.py   
| |-- urls.py   
| *-- views.py     
|-- templates   
| *-- some_app   
| *-- home.html   
*-- test_project   
|-- __init__.py   
|-- settings.py   
|-- urls.py   
*-- wsgi.py    

4 directories, 11 files  

我们不想想是 .pyc 文件,所以将他们做了过滤。

启动服务。请确保你做好了你的数据库设定。

(staticvirt)~/staticvirt/test_project$ python manage.py runserver  

在浏览器中打开 http://127.0.0.1:8000/some_app/home。从现在开始,我们称这个页面为 some_app 的 home,你应该能够看到你刚写下的 html 的内容。

处理静态文件

让我们编辑下 some_app 中的 home.html 文件,并且在其中添加样式,现在还不存在任何样式文件,我们将在编辑好 home.html 中的代码后添加。

<html>   
<head>   
<link href="{{STATIC_URL}}styles.css" rel="stylesheet" type="text/css">   
</head>   
<body>   
<h1>This is home for some_app</h1>   
</body>  
</html>    

刷新 some_app 的 home 页面。你将不会看到任何变化,因为我们还没有创建样式文件。

同样,访问 http://127.0.0.1/static/style.css,你将会看到一个404页面。

现在开始创建样式文件。因为我们想要在 some_app 的 template 中使用这个样式,所以我们将在 some_app 的 static/ 的子目录中创建。所以创建 some_app/static/style.css,添加以下内容。

body  
{  background-color: red;  } 

再刷新 some_app 的 home 页面,你将会看到页面背景变成了红色。同样,访问 http://127.0.0.1/static/style.css,你看到的不再是404页面,而是样式文件的内容。如果你看到这些变化,请确认你将
some_app 添加进了 INSTALLED_APPS,并且重启了服务。

需要注意的地方
  • 我们没有对 Django 的默认静态文件设置做任何改变。我们完全保留了 Django 的 settings.py 中关于静态文件的设置。

  • 在开发中,你不需要在 urls.py 中关于静态文件做任何改变,不需要添加 staticfiles_urlpatterns(),我经常对此感到疑惑。

  • 在开发中,你不需要执行 python manage.py collectstatic。

内部是怎么工作的
  • 首先,检索 settings.py 中所有关于静态文件的设置。

  • 他们是 STATIC_URL, STATIC_ROOT, STATICFILES_FINDERS, STATICFILES_DIRS。

  • 同样我们已经将 'django.contrib.staticfiles' 添加进了INSTALLED_APPS。

  • 现在先不管 STATIC_ROOT 和 STATICFILES_DIRS。即使你将他们注释或者删除,你的项目依然能够像现在一样工作。

  • 我们需要将 'django.contrib.staticfiles' 添加进 INSTALLED_APPS,如果我们想要使用 Django 默认的静态文件处理服务。

  • 所谓的 Django 默认的静态文件处理服务就相当于需要使用 Django提供的 python manage.py runserver。

  • Django 默认会在 STATIC_URL 下处理静态文件。注意STATIC_URL 已经设置为 '/static/' 。这就是为什么我们获取到了我们的静态文件,举个例子,样式文件在这个 url 下http://127.0.0.1:8000/static/styles.css。如果你访问http://127.0.0.1:8000/static_changed/styles.css,你将会得到一个404页面。如果你想要在http://127.0.0.1:8000/static_changed/styles.css提供,需要设置STATIC_URL = '/static_changed/'。现在动手试试吧。这只是为了举例说明 STATIC_URL 的用处,现在都改回默认设置,即STATIC_URL = '/static/'。

  • 下一个问题是,Django 是怎么知道从哪里去读取静态文件的,或者说怎么知道去哪里找到静态文件呢?这就
    STATICFILES_FINDERS 的作用了。在 STATICFILES_FINDERS 中我们有两条记录:

'django.contrib.staticfiles.finders.FileSystemFinder',  'django.contrib.staticfiles.finders.AppDirectoriesFinder'  

现在你可以先不管 FileSystemFinder,如果你愿意,你可以先注释掉这一行。

AppDirectoriesFinder 告诉 Django 从 INSTALLED_APPS
中每一个 app下的 static/ 子目录下去寻找静态文件。记住,我们是将style.css 放在了 some_app
中 static/ 子目录下,这就是为什么 Django 能够找到它,并且进行正确的处理。如果你将 'static/' 子目录修改为其他名字,你的静态文件就不能被正确处理了。动手试一试吧。注释掉 AppDirectoriesFinder 这一行,然后访问 http://127.0.0.1:8000/static/styles.css,现在样式文件不能被正确地处理了。好,尝试过后去掉注释。

现在,我们知道了 STATIC_URL 和 STATICFILES_FINDERS
的作用。我们现在仍然不需要用到 STATIC__ROOT 和STATICFILES_DIRS。

为了了解一些其他的事情关于静态文件的处理,我们需要另一个app。

创建一个。

python manage.py startapp other_app  

修改项目的 urls.py,将 other_app 包括进去。现在项目的 urls.py 包括两行。

url(r'^some_app/', include('some_app.urls')),   
url(r'^other_app/', include('other_app.urls')),   

我们需要在 other_app 的 urls.py 中添加几行,比如,在other_app/urls.py中:

url(r'^home$', direct_to_template, {"template": "other_app/home.html"})  

现在在 templates 目录下创建 other_app/home.html。

<html>   
<body>   
<h1>This is home for other_app</h1>    
</body>   
</html>   

查看一下现在的目录结构。

~/staticvirt/test_project$ tree -I *.pyc 
. 
|-- manage.py  
|-- other_app  
| |-- __init__.py  
| |-- models.py  
| |-- tests.py  
| |-- urls.py  
| *-- views.py  
|-- some_app  
| |-- __init__.py  
| |-- models.py  
| |-- static  
| | *-- styles.css  
| |-- tests.py  
| |-- urls.py  
| *-- views.py  
|-- templates  
| |-- other_app  
| | *-- home.html 
| *-- some_app   
| *-- home.html  
*-- test_project  
|-- __init__.py   
|-- settings.py  
|-- urls.py  
*-- wsgi.py    

将 other_app 添加进 INSTALLED_APPS。

现在访问 url:http://127.0.0.1:8000/other_app/home

为 other_app 的 home 页面添加样式。假设我们想让它的背景颜色为蓝色,我们创建 other_app/static/other_style.css

body{  background-color: blue;  }  

将样式文件添加进 other_app 的 home 页面的模板中,将templates/other_app/home.html
改为:

<html>  
<head>  
<link href="{{STATIC_URL}}other_style.css" rel="stylesheet" type="text/css">  
</head>  
<body>  
<h1>This is home for other_app</h1>  
</body>  
</html>  

刷新 http://127.0.0.1:8000/other_app/home,你将会看到蓝色背景。你也许需要重启服务才能看到变化。同样,我们能够在http://127.0.0.1:8000/static/other_style.css 中看到样式文件的内容。

同时,访问 http://127.0.0.1:8000/some_app/home,验证下some_app 的 home 页面依然是红色背景。

这里发生了什么

当我们发起一个 /static/other_style.css 的请求,Django 知道STATIC_URL 设置为 '/static/' ,这跟 url 提供的第一个部分相匹配,因此它推断我们想要将其作为静态文件处理,所以它进入所有 app 的 static/ 子目录中进行查找,因为 STATICFILES_FINDERS 包含了 'django.contrib.staticfiles.finders.AppDirectoriesFinder'。当它在other_app 中的 static/ 目录下找到一个名为 other_style.css 的文件,就对它进行处理。

然而,这带来了另一个问题,你一定注意到了我们将 other_app
中的样式文件命名为 other_style.css。如果我们想要它的名称也为style.css 会发生什么呢?试试看。

mv other_app/static/other_style.css other_app/static/styles.css    

同时,我们需要修改 other_app 的 home 文件来引入这个样式文件。我们必须做这个,因为我们将 other_style.css
改名为了 style.css。other_app 的 home 文件修改如下:

<html>  
<head>  
<link href="{{STATIC_URL}}styles.css" rel="stylesheet" type="text/css">  
</head>  
<body>  
<h1>This is home for other_app</h1>  
</body>  
</html>   

现在查看两个我们创建的页面。

http://127.0.0.1:8000/some_app/home  
http://127.0.0.1:8000/other_app/home  

你会发现现在两个页面的背景都变成了红色。这依赖于INSTALLED_APPS 中 app 的排列顺序。如果 some_app 在other_app 的前面,两个页面都会是红色背景。如果 other_app
在 some_app 的前面,那么两个页面背景都是蓝色。在我的设置中,some_app 在 other_app 之前,所以背景都是红色的。

为什么这会发生

两个页面都想引用一个名为 style.css 的静态文件。Django 尝试在INSTALLED_APPS 中列出的所有 app 中的 static/ 子目录下寻找这个文件。一旦它在 some_app 的 static/ 子目录中找到了,就会进行处理并且不再继续在 other_app 中进行寻找。因此,some_app
中 static/ 子目录下将背景设置为红色,那么两个页面都被设置为红色背景了。

怎么避免

那么,如果我们想在两个 app 中样式文件都叫做 style.css 怎么做?这时候,我们需要在没一个 app 下的 static/ 目录下增加一层目录,将其命名为各自 app 的名称。像下面这么做:

mkdir some_app/static/some_app   
mv some_app/static/styles.css some_app/static/some_app    
mkdir other_app/static/other_app   
mv other_app/static/styles.css other_app/static/other_app/   

我们在每一个 app 下的 static/ 子目录下创建一个与各自 app 相同的目录。然后将样式文件移到这个目录下。

同理,也需要修改各自的模板文件。

修改 templates/some_app/home.html 中的 stylesheet 路径,新的内容如下:

<html>  
<head>   
<link href="{{STATIC_URL}}some_app/styles.css" rel="stylesheet" type="text/css">     
</head>   
<body>  
<h1>This is home for some_app</h1>   
</body>   
</html>   

对 templates/other_app/home.html 做相似的改动。

<html>   
<head>   
<link href="{{STATIC_URL}}other_app/styles.css" rel="stylesheet" type="text/css">   
</head>   
<body>   
<h1>This is home for other_app</h1>   
</body>   
</html>   

现在再次查看两个页面。

http://127.0.0.1:8000/some_app/home  
http://127.0.0.1:8000/other_app/home 

你将会发现一个背景是红色,另一个是蓝色。

这里发生了什么

  • some_app 的模板需要引用http://127.0.0.1:8000/static/some_app/styles.css

  • Django 发现这个 url 以 '/static/' 开头,这跟 STATIC_URL
    匹配,推测这需要处理静态文件 some_app/style.css。

  • 它开始在所有 app 的 static/ 子目录中寻找文件 some_app/style.css。

  • 它最终在 some_app 的 static/ 子目录中找到了它,并进行处理。

  • other_app 的模板需要引用http://127.0.0.1:8000/static/other_app/styles.css

  • Django 开始在所有 app 的 static/ 子目录中寻找文件other_app/style.css。

  • 它最终在 other_app 的 static/ 子目录中找到了它,并进行处理。

希望你现在对于 STATIC_URL, STATICFILES_FINDERS 和静态文件是怎么处理的更加清楚了。

关于 STATICFILES_DIRS

到现在我们假定我们在 some_app 和 other_app是需要各自独立的静态文件,所以我们为他们写了不同样式文件。

假定我们项目中一些样式需要保持一致,没一个 app 都没有特殊。这也的话,我们不需要将这些样式文件放进任何一个 app 的 static/ 子目录中。我们在 manage.py 的同级目录中创建一个目录,然后将项目共同的静态资源放在这个目录中。

然我们看看是怎么做的。

在 manage.py 的同一级下创建一个名为 project_static 的目录。

mkdir project_static   

创建一个名为 base.css 的文件,放进去。

touch project_static/base.css  

编辑这个页面,包含以下内容:

h1  
{  
font-style: italic;  
}  

我们想让项目中所有h1标签中的内容斜体显示。

Django 现在还不知道这个文件,也不知道怎么进行处理。要让Django 知道它,需要将包含这个文件的目录添加进STATICFILES_DIRS。所以编辑 test_project/settings.py
,将需要的目录添加进 STATICFILES_DIRS。

STATICFILES_DIRS = (  
os.path.join(PROJECT_DIR, '../project_static'),  
)  

试着访问http://127.0.0.1:8000/static/base.css
,你应该能看到刚才写的样式。请确保在STATICFILES_FINDERS
中你设置了:

'django.contrib.staticfiles.finders.FileSystemFinder'   

否则你将得到一个404页面。

这里发生了什么

  • Django 服务器收到一个关于静态文件的请求,因为是一以 '/static/' 开头的 url。

  • 它开始在 STATICFILES_DIRS 设定的所有目录中寻找这个静态文件,比如 base.css。

  • 由于我们在 STATICFILES_DIRS 中指定了一个目录,即
    project_static,Django 服务器在这个目录中尝试寻找这个文件。它在这个目录中进行搜索时找到了这个文件,然后进行处理。

  • 如果没有在 STATICFILES_DIRS 指定的目录中找到这个文件,它将会在 INSTALLED_APPS 下所有 app 的 static/ 子目录尝试寻找。

  • 注意,这时候依然没有不需要添加 staticfiles_urlpatterns()。

为了在模板中使用这个文件,我们需要引用这个样式。在所有模板中添加进下面这行。

<link href="{{STATIC_URL}}base.css" rel="stylesheet" type="text/css"> 

刷新两个页面的url,你将会看到这些页面中h1标签中的字体都为斜体。

让我们查看最终的目录结构,如果你有什么问题可以有帮助。

(staticvirt)~/staticvirt/test_project$ tree -I *.pyc   
.      
|-- manage.py     
|-- other_app     
| |-- __init__.py     
| |-- models.py     
| |-- static     
| | *-- other_app     
| | *-- styles.css     
| |-- tests.py     
| |-- urls.py     
| *-- views.py     
|-- project_static     
| *-- base.css     
|-- some_app     
| |-- __init__.py    
| |-- models.py     
| |-- static     
| | *-- some_app    
| | *-- styles.css     
| |-- tests.py     
| |-- urls.py       
| *-- views.py     
|-- templates     
| |-- other_app     
| | *-- home.html     
| *-- some_app     
| *-- home.html     
*-- test_project     
|-- __init__.py     
|-- settings.py     
|-- urls.py     
*-- wsgi.py     

11 directories, 20 files  

关于 STATIC_ROOT

  • 如果在开发阶段你使用 Django 的 runserver,你将永远不会需要STATIC_ROOT。

  • 一旦你需要进入生产,你能在服务器中使用它。Django 提供了一个静态文件管理的命令叫做 collectstatic,它将收集所有的静态资源,(如在 STATICFILES_DI
    ![Uploading 0_005091.jpg . . .]
    RS 中找到的和在所有 app 下的 static/子目录中找到的静态资源),将它们放进一个 STATIC_ROOT
    定义的位置。

  • STATIC_ROOT 只有在你使用 collectstatic 命令的时候才会有用处。

让我们验证一下,创建一个名为 static_resources 的目录.

mkdir static_resources 

修改settings.py,添加以下几行.

STATIC_ROOT = os.path.join(PROJECT_DIR, '../static_resources') 

现在运行命令:

python manage.py collectstatic  

它会请求你确认,输入 'yes',然后你将会看见所有的静态资源被收集进一个你在 STATIC_ROOT 定义的目录中。

然后在生产服务器中你可以设置所有的静态文件请求都进入STATIC_ROOT 定义的目录中进行查找。

再说一次,关于 STATIC_ROOT 的部分只是附带着说说。在开发阶段你都不需要用到它。

原文:http://agiliq.com/blog/2013/03/serving-static-files-in-django/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容