【Django】Django 静态资源和 HTML 文件管理

Posted by 西维蜀黍 on 2019-11-13, Last Modified on 2021-10-16

Background

django.contrib.staticfiles

django.contrib.staticfiles是django1.3新增的一个app来帮助开发者管理静态文件(js,css等)。

在我使用的 Django 1.6.11版本中,默认已安装并加载了 staticfiles App,

INSTALLED_APPS = [
    # 'django.contrib.admin',
    # 'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'core',
]

STATIC_URL

使用 STATIC_URL 管理静态文件( images, JavaScript, CSS)

在项目的 setting.py 中声明:

STATIC_URL = '/static/'

新建项目myproject:

$ django-admin.py startproject TestDjango

在项目中新建一个app名叫hello“:

$ cd TestDjango
$ python manage.py startapp hello
$ ls
hello  TestDjango  manage.py

在项目的settings.py文件中INSTALLED_APPS中配置hello app:

INSTALLED_APPS = (
    ...
    'hello',
)

在hello app下建一个static目录用来存放静态文件:

$ cd hello
$ mkdir static
$ ls
__init__.py  models.py  static  tests.py  views.py

然后在static目录新建一个test.js的静态文件:

$ cd static/
$ ls
test.js
$ cat test.js
alert("load test.js successfully");

当前项目结构:

tree
.
├── TestDjango
├── hello
│   ├── static
│   │   └── test.js
│   ├── ...
└── ...

运行项目:

$ python manage.py runserver 0.0.0.0:8001

通过url访问:

这里很奇怪的是,这个 static 文件夹是位于你的 Django App 文件夹根目录中的(而不是你的 Django Project 文件夹根目录中),而你指定的明明是 STATIC_URL = '/static/'

这其实是因为使用 STATIC_URL 定义的方式,静态资源文件夹只能被一个 Django App 使用,

在 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 文件夹文件夹:

$ mkdir templates

在 templates 文件夹中定义一个 a.html:

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>a</title>
	<link href="{% static 'test.js' %}" rel="stylesheet">
</head>
<body>
a's body
</body>
</html>

此时的文件结构:

$ tree
├── TestDjango
│   ├── ...
├── hello
│   ├── static
│   │   └── test.js
│   ├── ...
├── templates
    ├── a.html
└── ...

在 hello App 的 views.py 中定义一个 view:

def a(request):
	return render(request, 'a.html', locals())

在项目下的 urls.py 中定义路由:

from django.conf.urls import url
# from django.contrib import admin
from hello import views

urlpatterns = [
    #    url(r'^admin/', admin.site.urls),
	  url(r'^a/',views.a)
]

当访问 http://127.0.0.1:8001/a 时:

STATICFILES_DIRS

这其实是因为使用 STATIC_URL 定义的方式,静态资源文件夹只能被一个 Django App 使用。

如果当我们的 Django Project 中,有多个 Django App 时,使用 STATICFILES_DIRS 则可以更科学的管理静态资源。

Format example:

STATICFILES_DIRS = [
    "/home/special.polls.com/polls/static",
    "/home/polls.com/polls/static",
    "/opt/webfiles/common",
]

Note that these paths should use Unix-style forward slashes, even on Windows (e.g. "C:/Users/user/mysite/extra_static_content").

使用 STATICFILES_DIRS 管理静态资源

在 settings.py 中定义:

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
]

注意:

  • 不能不声明 STATIC_URL,而只声明 STATICFILES_DIRS,否则会有错误:django.core.exceptions.ImproperlyConfigured: You're using the staticfiles app without having set the required STATIC_URL setting.
  • 不能进行STATIC_URL = '/' 的声明,否则路由机制会出现问题。

在项目中新建一个app名叫hello2:

$ cd TestDjango
$ python manage.py startapp hello2
$ ls
TestDjango     hello      hello2     manage.py     templates

在hello2 的 views.py 中增加:

def b(request):
	# return HttpResponse("return this string")
	context = {}
	context['hello'] = 'Hello World!'
	return render(request, 'b.html', context)

在 urls.py 中修改整个 Django Project 的路由规则为:

from django.conf.urls import url

from hello.views import a
from hello2.views import b

urlpatterns = [
	url(r'^a/$', a),
	url(r'^b/$', b),
]

目录结构为:

$ tree /Users/wei.shi/Downloads/TestDjango
/Users/wei.shi/Downloads/TestDjango
├── TestDjango
   ├── ...
├── hello
   ├── views.py
   └── ...
├── hello2
   ├── views.py
   └── ...
├── static
   └── test.js
├── templates
    ├── a.html
    └── b.html
└── ...

最终,无论是访问 http://127.0.0.1:8001/a/ 还是 http://127.0.0.1:8001/b/,一切都正常:

存疑

然而发现一个神奇的地方,当在 settings.py 中设置为:

STATIC_URL = '/aabbccdd/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
]

在 HTML 页面中,仍然采用和上面一样的方法来加载静态资源,即:

{% load static %}
...
<link href="{% static 'test.js' %}" rel="stylesheet">

虽然请求 http://127.0.0.1:8001/a/ 时,仍能正常获取到静态资源,但是在Chrome DevTools 中,看到的请求 URL 竟然是 http://127.0.0.1:8001/aabbccdd/test.js:

Prefixes (optional)

In case you want to refer to files in one of the locations with an additional namespace, you can optionally provide a prefix as (prefix, path) tuples, e.g.:

STATICFILES_DIRS = [
    # ...
    ("downloads", "/opt/webfiles/stats"),
]

For example, assuming you have STATIC_URL set to '/static/', the collectstatic management command would collect the “stats” files in a 'downloads' subdirectory of STATIC_ROOT.

This would allow you to refer to the local file /opt/webfiles/stats/polls_20101022.tar.gz with /static/downloads/polls_20101022.tar.gz in your templates, e.g.:

<a href="{% static "downloads/polls_20101022.tar.gz" %}">

部署静态文件(Deploy Static Files)

django.contrib.staticfiles provides a convenience management command for gathering static files in a single directory so you can serve them easily.

Most larger Django sites use a separate Web server – i.e., one that’s not also running Django – for serving static files. This server often runs a different type of web server – faster but less full-featured. Some common choices are:

  • Nginx
  • A stripped-down version of Apache

在 Django Project 的 settings.py 中设置:

STATIC_ROOT = "/var/www/example.com/static/"

执行:

$ python manage.py collectstatic

此后,所有的静态资源都会被拷贝到 /var/www/example.com/static/下。

我们可以根据具体项目中的情况,将这些静态资源拷贝到 CDN 或者交由 Nginx 这样的 Web server 帮来我们处理静态资源的请求。


如果需要部署到 CDN,则可以在 Django Project 的 settings.py 中设置:

STATIC_URL = 'https://your.cdn./'

这样,所有的静态文件的 URL 前缀都会自动变成你这个的这个 CDN 的地址:

Reference