django 静态文件
1.静态文件开发环境配置
项目中的CSS、图片、js都是静态文件。一般会将静态文件放到一个单独的目录中,以方便管理。在html页面中调用时,也需要指定静态文件的路径,Django中提供了一种解析的方式配置静态文件路径。静态文件可以放在项目根目录下,也可以放在应用的目录下,由于有些静态文件在项目中是通用的,所以推荐放在项目的根目录下,方便管理。
为了提供静态文件,需要配置两个参数:
-
先创建目录: static_files
-
在setting.py文件中配置:
- STATICFILES_DIRS 存放查找静态文件的目录
- STATIC_URL 访问静态文件的URL前缀
-
示例:
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static_files'),
]
- 说明: 此时在static_files添加的任何静态文件都可以使用网址 /static/文件在static_files中的路径 来访问了
- 注意:
- Django 仅在调试模式下(DEBUG=True)能对外提供静态文件。
- 当DEBUG=False工作在生产模式时,Django不再对外提供静态文件,需要是用collectstatic命令来收集静态文件并交由其他静态文件服务器来提供。
2.静态文件生产环境部署
静态文件除了我们业务之外,django本身还有自己的静态文件,如果rest_framework、admin等。我们需要收集这些静态文件,集中一起放到静态文件服务器中。
- 先创建目录 static
- 在setting.py文件中配置:
STATIC_ROOT = os.path.join(os.path.dirname(os.path.dirname(BASE_DIR)), 'front_end/static')
- 执行收集命令:
python manage.py collectstatic
- 生产环境静态文件服务器配置(这里采用nginx)
- 1.打开配置文件,一般是
sudo vim /usr/local/nginx/conf/nginx.conf
- 2.```
- server {
-
// 余下省略(文档原因,自己对齐)listen 80; server_name www.meiduo.site; location / { root /home/python/Desktop/front_end_pc; index index.html index.htm; }
}``` - 3.重启 nginx:
sudo /usr/local/nginx/sbin/nginx -s reload
- 1.打开配置文件,一般是
django 自带文件管理系统
1.一些特点:
1.默认情况下,Django会将上传的图片保存在本地服务器上,需要配置保存的路径。
2.一般项目本身和业务结合较多的文件, 会配置一个 MEDIA_ROOT
参数结合 BASE_DIR 来形成一个完整的路径,单独放置在 static 文件目录下;
3.django 会在global_settins中, 默认: MEDIA_ROOT = ‘’
4.常见在 ImageField 字段中 upload_to
相关联
2.自定义django的文件存储系统(类)
- 1.继承: django.core.files.storage.Storage
- 2.Storage 继承 object类, 而且没有
__init__.py
, 所以实例化的时候不需要参数,也就是说任何配置都应该从django.conf.settings中获取
from django.conf import settings
from django.core.files.storage import Storage
class MyStorage(Storage):
def __init__(self, base_url=None, client_conf=None):
if base_url is None:
base_url = settings.File_SERVER_URL
self.base_url = base_url
if client_conf is None:
client_conf = settings.File_CLIENT_CONF
self.client_conf = client_conf
-
3.存储类中必须实现_open()和_save()方法,以及任何后续使用中可能用到的其他方法。
-
_open(name, mode=‘rb’)
被Storage.open()调用,在打开文件时被使用。 -
_save(name, content)
被Storage.save()调用,name是传入的文件名,content是Django接收到的文件内容,该方法需要将content文件内容保存。
Django会将该方法的返回值保存到数据库中对应的文件字段,也就是说该方法应该返回要保存在数据库中的文件名称信息。 -
exists(name)
如果名为name的文件在文件系统中存在,则返回True,否则返回False。 -
url(name)
返回文件的完整访问URL -
delete(name)
删除name的文件 -
listdir(path)
列出指定路径的内容 -
size(name)
返回name文件的总大小
注意,并不是这些方法全部都要实现,可以省略用不到的方法。 -
4.需要为存储类添加django.utils.deconstruct.deconstructible装饰器
3.配置 自定义的文件存储类
- 1.django 默认在global_settings中配置了:
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
- 2.我们这里需要 自己配置 DEFAULT_FILE_STORAGE
- 3.如果是保存到服务器,我们可能还需要配置一些 url port path 等文件存储服务器信息.
4.其他静态文件服务器:
- FastDFS
- OSS
5.自定义可以参考源码 FileSystemStorage 标准实现方法
@deconstructible
class FileSystemStorage(Storage):
"""
Standard filesystem storage
"""
# The combination of O_CREAT and O_EXCL makes os.open() raise OSError if
# the file already exists before it's opened.
OS_OPEN_FLAGS = os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, 'O_BINARY', 0)
def __init__(self, location=None, base_url=None, file_permissions_mode=None,
directory_permissions_mode=None):
self._location = location
self._base_url = base_url
self._file_permissions_mode = file_permissions_mode
self._directory_permissions_mode = directory_permissions_mode
setting_changed.connect(self._clear_cached_properties)
def _clear_cached_properties(self, setting, **kwargs):
"""Reset setting based property values."""
略
def _value_or_setting(self, value, setting):
return setting if value is None else value
@cached_property
def base_location(self):
return self._value_or_setting(self._location, settings.MEDIA_ROOT)
@cached_property
def location(self):
return os.path.abspath(self.base_location)
@cached_property
def base_url(self):
if self._base_url is not None and not self._base_url.endswith('/'):
self._base_url += '/'
return self._value_or_setting(self._base_url, settings.MEDIA_URL)
@cached_property
def file_permissions_mode(self):
return self._value_or_setting(self._file_permissions_mode, settings.FILE_UPLOAD_PERMISSIONS)
@cached_property
def directory_permissions_mode(self):
return self._value_or_setting(self._directory_permissions_mode, settings.FILE_UPLOAD_DIRECTORY_PERMISSIONS)
def _open(self, name, mode='rb'):
return File(open(self.path(name), mode))
def _save(self, name, content):
full_path = self.path(name)
# Create any intermediate directories that do not exist.
directory = os.path.dirname(full_path)
if not os.path.exists(directory):
try:
if self.directory_permissions_mode is not None:
# os.makedirs applies the global umask, so we reset it,
# for consistency with file_permissions_mode behavior.
old_umask = os.umask(0)
try:
os.makedirs(directory, self.directory_permissions_mode)
finally:
os.umask(old_umask)
else:
os.makedirs(directory)
except FileExistsError:
# There's a race between os.path.exists() and os.makedirs().
# If os.makedirs() fails with FileExistsError, the directory
# was created concurrently.
pass
if not os.path.isdir(directory):
raise IOError("%s exists and is not a directory." % directory)
# There's a potential race condition between get_available_name and
# saving the file; it's possible that two threads might return the
# same name, at which point all sorts of fun happens. So we need to
# try to create the file, but if it already exists we have to go back
# to get_available_name() and try again.
while True:
try:
# This file has a file path that we can move.
if hasattr(content, 'temporary_file_path'):
file_move_safe(content.temporary_file_path(), full_path)
# This is a normal uploadedfile that we can stream.
else:
# The current umask value is masked out by os.open!
fd = os.open(full_path, self.OS_OPEN_FLAGS, 0o666)
_file = None
try:
locks.lock(fd, locks.LOCK_EX)
for chunk in content.chunks():
if _file is None:
mode = 'wb' if isinstance(chunk, bytes) else 'wt'
_file = os.fdopen(fd, mode)
_file.write(chunk)
finally:
locks.unlock(fd)
if _file is not None:
_file.close()
else:
os.close(fd)
except FileExistsError:
# A new name is needed if the file exists.
name = self.get_available_name(name)
full_path = self.path(name)
else:
# OK, the file save worked. Break out of the loop.
break
if self.file_permissions_mode is not None:
os.chmod(full_path, self.file_permissions_mode)
# Store filenames with forward slashes, even on Windows.
return name.replace('\\', '/')
def delete(self, name):
略
pass
def exists(self, name):
return os.path.exists(self.path(name))
def listdir(self, path):
path = self.path(path)
directories, files = [], []
for entry in os.scandir(path):
if entry.is_dir():
directories.append(entry.name)
else:
files.append(entry.name)
return directories, files
def path(self, name):
return safe_join(self.location, name)
def size(self, name):
return os.path.getsize(self.path(name))
def url(self, name):
if self.base_url is None:
raise ValueError("This file is not accessible via a URL.")
url = filepath_to_uri(name)
if url is not None:
url = url.lstrip('/')
return urljoin(self.base_url, url)
def _datetime_from_timestamp(self, ts):
"""
If timezone support is enabled, make an aware datetime object in UTC;
otherwise make a naive one in the local timezone.
"""
略
def get_accessed_time(self, name):
return self._datetime_from_timestamp(os.path.getatime(self.path(name)))
def get_created_time(self, name):
return self._datetime_from_timestamp(os.path.getctime(self.path(name)))
def get_modified_time(self, name):
return self._datetime_from_timestamp(os.path.getmtime(self.path(name)))
6.还可以修改 upload_to = “” , 来制定文件保存的位置. 更复杂的还可以通过 lambda函数 , 自定义函数 等方式来实现 将文件文件保存在指定的位置, 甚至是动态的位置.
7.django 在 FileField 类中通过 self.storage = storage or default_storage
实现 default_storage 指定默认文件存储系统的功能的.
参考下面的源码…
class FileField(Field):
# The class to wrap instance attributes in. Accessing the file object off
# the instance will always return an instance of attr_class.
attr_class = FieldFile
# The descriptor to use for accessing the attribute off of the class.
descriptor_class = FileDescriptor
description = _("File")
def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs):
self._primary_key_set_explicitly = 'primary_key' in kwargs
self.storage = storage or default_storage # 调用默认的 文件存储系统
self.upload_to = upload_to
kwargs.setdefault('max_length', 100)
super().__init__(verbose_name, name, **kwargs)
# 略