1.核心层源码一览
- django中的核心层通常是指core目录下的一些核心的功能和组件,负责处理Django框架的核心逻辑。以下是一些主要组件和功能:
django.core.exceptions
:包含了Django框架中的一些核心异常类。这些异常通常用于处理错误和异常情况。django.core.files
:提供了处理文件上传和存储的功能。这包括文件存储后端、文件上传处理等相关的组件。django.core.handlers
: 包含了处理HTTP请求的核心处理程序,如 BaseHandler。这是Django请求处理的入口点。django.core.mail
: 提供了用于处理电子邮件的模块。这包括邮件发送、邮件模板等相关的功能。django.core.management
:包含了Django命令行管理工具的核心实现。这个模块使得可以通过命令行执行一些常见的管理任务,如数据库迁移、创建超级用户等。django.core.serializers
:提供了用于序列化和反序列化数据的组件。这对于将数据从数据库中提取并以不同的格式(如JSON)进行呈现是很有用的。django.core.signals
:包含了Django框架中使用的信号(signals)系统的实现。信号用于允许某些部分的代码在应用中的其他部分发生变化时得到通知django.core.validators
:提供了用于验证数据的一些通用验证器。这些验证器用于确保数据符合预期的格式和要求
2.缓存模块调用源码解读
-
Django 的缓存模块位于
django/core/cache
目录下,主要涉及缓存的抽象和实现。 -
在 Django 中,
LocMemCache类
是一个本地内存缓存后端的实现,即将缓存数据存储在应用进程的内存中。from django.core.cache.backends.base import BaseCache class LocMemCache(BaseCache): pickle_protocol = pickle.HIGHEST_PROTOCOL def __init__(self, name, params): super().__init__(params) # _cache 用于存储缓存的键值对 self._cache = _caches.setdefault(name, OrderedDict()) # expire_info 用于存储每个缓存键的过期信息。 self._expire_info = _expire_info.setdefault(name, {}) # 锁 self._lock = _locks.setdefault(name, Lock()) # 尝试将一个键值对添加到缓存中 def add(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): key = self.make_key(key, version=version) self.validate_key(key) pickled = pickle.dumps(value, self.pickle_protocol) with self._lock: if self._has_expired(key): self._set(key, pickled, timeout) return True return False def get(self, key, default=None, version=None): key = self.make_key(key, version=version) self.validate_key(key) with self._lock: if self._has_expired(key): self._delete(key) return default pickled = self._cache[key] self._cache.move_to_end(key, last=False) return pickle.loads(pickled) # 将一个键值对添加到缓存中,同时处理缓存的大小和过期信息。 def _set(self, key, value, timeout=DEFAULT_TIMEOUT): if len(self._cache) >= self._max_entries: self._cull() self._cache[key] = value self._cache.move_to_end(key, last=False) # 将传入的超时时间转换为后端的超时表示形式。 self._expire_info[key] = self.get_backend_timeout(timeout) # 将键值对添加到缓存中 def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): key = self.make_key(key, version=version) self.validate_key(key) pickled = pickle.dumps(value, self.pickle_protocol) with self._lock: self._set(key, pickled, timeout) # 更新缓存中指定键的超时时间 def touch(self, key, timeout=DEFAULT_TIMEOUT, version=None): key = self.make_key(key, version=version) self.validate_key(key) with self._lock: if self._has_expired(key): return False self._expire_info[key] = self.get_backend_timeout(timeout) return True # 递增缓存中指定键的值。 def incr(self, key, delta=1, version=None): key = self.make_key(key, version=version) self.validate_key(key) with self._lock: if self._has_expired(key): self._delete(key) raise ValueError("Key '%s' not found" % key) pickled = self._cache[key] value = pickle.loads(pickled) new_value = value + delta pickled = pickle.dumps(new_value, self.pickle_protocol) self._cache[key] = pickled self._cache.move_to_end(key, last=False) return new_value def has_key(self, key, version=None): key = self.make_key(key, version=version) self.validate_key(key) with self._lock: if self._has_expired(key): self._delete(key) return False return True # 检查指定键是否已经过期。 def _has_expired(self, key): exp = self._expire_info.get(key, -1) return exp is not None and exp <= time.time() # 根据缓存的清理频率 _cull_frequency 进行清理,删除过期的键值对。 def _cull(self): if self._cull_frequency == 0: self._cache.clear() self._expire_info.clear() else: count = len(self._cache) // self._cull_frequency for i in range(count): key, _ = self._cache.popitem() del self._expire_info[key] # 删除缓存中指定键的键值对。 def _delete(self, key): try: del self._cache[key] del self._expire_info[key] except KeyError: return False return True # 删除缓存中指定键的键值对,通过调用 _delete 方法。 def delete(self, key, version=None): key = self.make_key(key, version=version) self.validate_key(key) with self._lock: return self._delete(key) # 清空缓存,删除所有键值对。 def clear(self): with self._lock: self._cache.clear() self._expire_info.clear()
-
在 Django 中,除了
LocMemCache类
等缓存模块外,还有第三方基于redis的缓存模块,其主要位于django/core/cache/backends/redis.py
文件中,主要实现了通过 Redis 存储缓存数据。RedisCache 类
继承自 BaseCache,是 Redis 缓存的具体实现,提供了与 Redis 通信的方法,包括连接池管理、键值对的存取等。class RedisCache(BaseCache): def __init__(self, server, params): super().__init__(params) # server 参数,用于指定 Redis 服务器的位置。 if isinstance(server, str): self._servers = re.split("[;,]", server) else: self._servers = server self._class = RedisCacheClient self._options = params.get("OPTIONS", {}) # 使用 cached_property 装饰器创建一个 _cache 属性,该属性是一个 RedisCacheClient 的实例,用于与 Redis 服务器进行通信。 @cached_property def _cache(self): return self._class(self._servers, **self._options) # 用于获取后端缓存的超时时间,将传入的超时时间转换为适用于后端的表示形式。 def get_backend_timeout(self, timeout=DEFAULT_TIMEOUT): if timeout == DEFAULT_TIMEOUT: timeout = self.default_timeout # The key will be made persistent if None used as a timeout. # Non-positive values will cause the key to be deleted. return None if timeout is None else max(0, int(timeout)) # 将键值对添加到缓存中,仅当键不存在时才添加。 def add(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): key = self.make_and_validate_key(key, version=version) return self._cache.add(key, value, self.get_backend_timeout(timeout)) # 获取缓存中指定键的值 def get(self, key, default=None, version=None): key = self.make_and_validate_key(key, version=version) return self._cache.get(key, default) # 设置缓存中的键值对,同时处理超时等参数。 def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): key = self.make_and_validate_key(key, version=version) self._cache.set(key, value, self.get_backend_timeout(timeout)) # 更新缓存中指定键的超时时间。 def touch(self, key, timeout=DEFAULT_TIMEOUT, version=None): key = self.make_and_validate_key(key, version=version) return self._cache.touch(key, self.get_backend_timeout(timeout)) # 删除缓存中指定键的键值对。 def delete(self, key, version=None): key = self.make_and_validate_key(key, version=version) return self._cache.delete(key) # 批量获取缓存中多个键对应的值。 def get_many(self, keys, version=None): key_map = { self.make_and_validate_key(key, version=version): key for key in keys } ret = self._cache.get_many(key_map.keys()) return {key_map[k]: v for k, v in ret.items()} # 检查缓存中是否存在指定的键。 def has_key(self, key, version=None): key = self.make_and_validate_key(key, version=version) return self._cache.has_key(key) # 将缓存中指定键的值增加指定的步长。 def incr(self, key, delta=1, version=None): key = self.make_and_validate_key(key, version=version) return self._cache.incr(key, delta) # 批量设置缓存中的键值对。 def set_many(self, data, timeout=DEFAULT_TIMEOUT, version=None): if not data: return [] safe_data = {} for key, value in data.items(): key = self.make_and_validate_key(key, version=version) safe_data[key] = value self._cache.set_many(safe_data, self.get_backend_timeout(timeout)) return [] # 批量删除缓存中的键值对。 def delete_many(self, keys, version=None): if not keys: return safe_keys = [self.make_and_validate_key(key, version=version) for key in keys] self._cache.delete_many(safe_keys) # 清空缓存 def clear(self): return self._cache.clear()
3.序列化器源码解读
-
Django的序列化器是用于将复杂数据类型(如
QuerySets
和模型实例)转换为Python数据类型,以便于渲染成JSON等格式。 常见的序列化器包括python序列化器
和json序列化器
,分别位于django/core/serializers/python.py
文件和django/core/serializers/json.py
文件中,而它们都是继承于抽象序列化器基类Serializer类
,位于django/core/serializers/base.py
,它提供了一个通用的序列化方法 serialize,用于将查询集(QuerySet)转换为序列化后的数据。 -
Python 序列化器
该模块提供了一个用于 Python 数据结构的序列化器。通常用于将 Django 的 QuerySets 或其他数据源序列化为Python 本地数据类型
。# 抽象序列化器基类 Serializer。提供了一个通用的序列化方法 serialize,用于将查询集(QuerySet)转换为序列化后的数据。 #这个基类提供了序列化的骨架,具体的序列化逻辑由其子类实现。子类需要实现 start_serialization、end_serialization、start_object、end_object、handle_field、handle_fk_field、handle_m2m_field 等方法。这种结构允许不同类型的序列化器在共享相似逻辑的同时,实现特定的定制化序列化行为 class Serializer: def serialize(self, queryset, *, stream=None, fields=None, use_natural_foreign_keys=False, use_natural_primary_keys=False, progress_output=None, object_count=0, **options): self.options = options # stream_class指数据流的类 self.stream = stream if stream is not None else self.stream_class() self.selected_fields = fields self.use_natural_foreign_keys = use_natural_foreign_keys self.use_natural_primary_keys = use_natural_primary_keys # progress_bar 分别指定进度条 progress_bar = self.progress_class(progress_output, object_count) # 调用 start_serialization 方法初始化序列化过程。 self.start_serialization() self.first = True for count, obj in enumerate(queryset, start=1): # 调用 start_object 方法开始一个对象的序列化。 self.start_object(obj) concrete_model = obj._meta.concrete_model # 针对每个对象,遍历其本地字段和本地多对多关系字段,根据序列化的条件调用 handle_field、handle_fk_field 和 handle_m2m_field 方法处理字段的序列化。 if self.use_natural_primary_keys: pk = concrete_model._meta.pk pk_parent = pk if pk.remote_field and pk.remote_field.parent_link else None else: pk_parent = None for field in concrete_model._meta.local_fields: if field.serialize or field is pk_parent: if field.remote_field is None: if self.selected_fields is None or field.attname in self.selected_fields: self.handle_field(obj, field) else: if self.selected_fields is None or field.attname[:-3] in self.selected_fields: self.handle_fk_field(obj, field) for field in concrete_model._meta.local_many_to_many: if field.serialize: if self.selected_fields is None or field.attname in self.selected_fields: self.handle_m2m_field(obj, field) self.end_object(obj) progress_bar.update(count) self.first = self.first and False self.end_serialization() return self.getvalue() # Python序列化器,用于将 QuerySet 序列化为基本的 Python 对象 class Serializer(base.Serializer): internal_use_only = True # 初始化序列化器,将当前对象 _current 设置为 None,并创建一个空列表 objects 用于存储序列化后的对象。 def start_serialization(self): self._current = None self.objects = [] def end_serialization(self): pass # start_object 方法初始化一个新的字典 _current,用于存储当前正在序列化的对象。 def start_object(self, obj): self._current = {} # end_object 方法将序列化后的对象添加到 objects 列表中,并将 _current 设置为 None。 def end_object(self, obj): self.objects.append(self.get_dump_object(obj)) self._current = None # 构建并返回一个表示序列化对象的字典。 def get_dump_object(self, obj): data = {'model': str(obj._meta)} if not self.use_natural_primary_keys or not hasattr(obj, 'natural_key'): data["pk"] = self._value_from_field(obj, obj._meta.pk) data['fields'] = self._current return data # 从模型字段中获取值,保护类型(如 None、数字、日期和 Decimal)保持不变。其他值首先转换为字符串。 def _value_from_field(self, obj, field): value = field.value_from_object(obj) return value if is_protected_type(value) else field.value_to_string(obj) # 处理常规模型字段,将其值添加到 _current 字典中。 def handle_field(self, obj, field): self._current[field.name] = self._value_from_field(obj, field) # 处理 ForeignKey 字段,根据设置的条件决定是否使用自然键。 def handle_fk_field(self, obj, field): if self.use_natural_foreign_keys and hasattr(field.remote_field.model, 'natural_key'): related = getattr(obj, field.name) if related: value = related.natural_key() else: value = None else: value = self._value_from_field(obj, field) self._current[field.name] = value # 处理 ManyToMany 字段,通过迭代相关对象并调用相应的方法构建 _current 字典。 def handle_m2m_field(self, obj, field): if field.remote_field.through._meta.auto_created: if self.use_natural_foreign_keys and hasattr(field.remote_field.model, 'natural_key'): def m2m_value(value): return value.natural_key() else: def m2m_value(value): return self._value_from_field(value, value._meta.pk) m2m_iter = getattr(obj, '_prefetched_objects_cache', {}).get( field.name, getattr(obj, field.name).iterator(), ) self._current[field.name] = [m2m_value(related) for related in m2m_iter] # 返回序列化后的对象列表 def getvalue(self): return self.objects
-
JSON 序列化器
该模块提供了一个序列化器,将 Django 的 QuerySets 或其他数据源转换为JSON 格式
。# Json序列化器,用于将 QuerySet 序列化为Json,这个序列化器继承了Python序列器。 class Serializer(PythonSerializer): internal_use_only = False def _init_options(self): # 初始化选项,设置一些 JSON 序列化所需的参数。 self._current = None self.json_kwargs = self.options.copy() self.json_kwargs.pop('stream', None) self.json_kwargs.pop('fields', None) if self.options.get('indent'): # Prevent trailing spaces self.json_kwargs['separators'] = (',', ': ') self.json_kwargs.setdefault('cls', DjangoJSONEncoder) self.json_kwargs.setdefault('ensure_ascii', False) def start_serialization(self): # 初始化序列化过程,在输出流中写入 "[",表示 JSON 数组的开始。 self._init_options() self.stream.write("[") def end_serialization(self): # 结束序列化过程。根据是否需要缩进,在输出流中写入 "\n" 和 "]" 表示 JSON 数组的结束。 if self.options.get("indent"): self.stream.write("\n") self.stream.write("]") if self.options.get("indent"): self.stream.write("\n") def end_object(self, obj): # 结束序列化单个对象。 indent = self.options.get("indent") if not self.first: self.stream.write(",") if not indent: self.stream.write(" ") if indent: self.stream.write("\n") json.dump(self.get_dump_object(obj), self.stream, **self.json_kwargs) self._current = None def getvalue(self): # 返回序列化后的结果。这里调用了父类 PythonSerializer 的 getvalue 方法。 return super(PythonSerializer, self).getvalue()
3.django发送邮件源码解读
- Django 的
django.core.mail
模块是用于处理电子邮件的模块,它提供了一些方便的函数来发送电子邮件。 EmailMessage 类
用于创建邮件消息对象。这个类提供了许多方法和属性,可以设置邮件的各种信息,如发件人、收件人、主题、内容等。通过 EmailMessage 对象的send()
方法来发送邮件。
class EmailMessage:
def __init__(self, subject='', body='', from_email=None, to=None, bcc=None,
connection=None, attachments=None, headers=None, cc=None,
reply_to=None):
# subject: 邮件主题。
# body: 邮件正文内容。
# from_email: 发件人邮箱地址。
# to: 收件人邮箱地址列表。
# cc: 抄送邮箱地址列表。
# bcc: 密送邮箱地址列表。
# 用于构建邮件消息的 email.message.EmailMessage 对象。
def message(self):
encoding = self.encoding or settings.DEFAULT_CHARSET
# 创建 SafeMIMEText 对象,其中包含邮件正文内容。
msg = SafeMIMEText(self.body, self.content_subtype, encoding)
# 设置邮件主题、发件人、收件人、抄送、回复地址等信息。
msg = self._create_message(msg)
msg['Subject'] = self.subject
msg['From'] = self.extra_headers.get('From', self.from_email)
self._set_list_header_if_not_empty(msg, 'To', self.to)
self._set_list_header_if_not_empty(msg, 'Cc', self.cc)
self._set_list_header_if_not_empty(msg, 'Reply-To', self.reply_to)
header_names = [key.lower() for key in self.extra_headers]
if 'date' not in header_names:
msg['Date'] = formatdate(localtime=settings.EMAIL_USE_LOCALTIME)
if 'message-id' not in header_names:
# Use cached DNS_NAME for performance
msg['Message-ID'] = make_msgid(domain=DNS_NAME)
for name, value in self.extra_headers.items():
if name.lower() != 'from': # From is already handled
msg[name] = value
return msg
# 用于发送邮件消息
def send(self, fail_silently=False):
"""Send the email message."""
if not self.recipients():
return 0
# 调用 get_connection 方法获取邮件连接对象,然后调用连接对象的 send_messages 方法发送邮件。
return self.get_connection(fail_silently).send_messages([self])
# 邮件连接对象EmailBackend类
class EmailBackend(BaseEmailBackend):
def send_messages(self, email_messages):
if not email_messages:
return 0
with self._lock:
new_conn_created = self.open()
if not self.connection or new_conn_created is None:
return 0
num_sent = 0
for message in email_messages:
# 调用 _send 方法进行实际的邮件发送。
sent = self._send(message)
if sent:
num_sent += 1
if new_conn_created:
self.close()
return num_sent
def _send(self, email_message):
if not email_message.recipients():
return False
encoding = email_message.encoding or settings.DEFAULT_CHARSET
from_email = sanitize_address(email_message.from_email, encoding)
recipients = [sanitize_address(addr, encoding) for addr in email_message.recipients()]
# 获取邮件消息对象,然后将其转换为字节形式。
message = email_message.message()
try:
# 发送邮件
self.connection.sendmail(from_email, recipients, message.as_bytes(linesep='\r\n'))
except smtplib.SMTPException:
if not self.fail_silently:
raise
return False
return True
5.django文件模块源码解读
-
django.core.files
模块是 Django 框架中用于处理文件上传和处理的核心模块之一。该模块定义了许多与文件相关的类和函数,包括文件存储、文件上传处理、文件字段等。 -
File
类是 Django 文件处理的基类,它实现了文件对象的基本操作,如读取、写入、关闭等。位于 django.core.files.base 模块中。class File(FileProxyMixin): DEFAULT_CHUNK_SIZE = 64 * 2 ** 10 # 初始化方法接受一个文件对象和一个可选的文件名。如果文件名未提供,会尝试从文件对象的属性中获取。 def __init__(self, file, name=None): self.file = file if name is None: name = getattr(file, 'name', None) self.name = name if hasattr(file, 'mode'): self.mode = file.mode # 返回文件的大小。使用 os.path.getsize 或文件对象的 size 属性(如果有)。 @cached_property def size(self): if hasattr(self.file, 'size'): return self.file.size if hasattr(self.file, 'name'): try: return os.path.getsize(self.file.name) except (OSError, TypeError): pass if hasattr(self.file, 'tell') and hasattr(self.file, 'seek'): pos = self.file.tell() self.file.seek(0, os.SEEK_END) size = self.file.tell() self.file.seek(pos) return size raise AttributeError("Unable to determine the file's size.") # 生成器方法,用于按块读取文件内容。接受一个可选的块大小参数。 def chunks(self, chunk_size=None): chunk_size = chunk_size or self.DEFAULT_CHUNK_SIZE try: self.seek(0) except (AttributeError, UnsupportedOperation): pass while True: data = self.read(chunk_size) if not data: break yield data # 返回是否文件大小超过指定块大小。 def multiple_chunks(self, chunk_size=None): return self.size > (chunk_size or self.DEFAULT_CHUNK_SIZE) # 打开文件,如果文件已关闭则重新打开。支持指定打开模式。 def open(self, mode=None): if not self.closed: self.seek(0) elif self.name and os.path.exists(self.name): self.file = open(self.name, mode or self.mode) else: raise ValueError("The file cannot be reopened.") return self def close(self): self.file.close()
-
UploadedFile
类继承自 File 类,表示上传的文件对象。位于django.core.files.uploadedfile
模块中。class UploadedFile(File): # 初始化方法接受文件对象(可选)、文件名、内容类型、文件大小、字符集和额外的内容类型信息 def __init__(self, file=None, name=None, content_type=None, size=None, charset=None, content_type_extra=None): super().__init__(file, name) self.size = size self.content_type = content_type self.charset = charset self.content_type_extra = content_type_extra # 返回一个字符串表示,包含类名、文件名和内容类型。 def __repr__(self): return "<%s: %s (%s)>" % (self.__class__.__name__, self.name, self.content_type) # 提供了对文件名属性的获取 def _get_name(self): return self._name # 提供了对文件名属性的设置 def _set_name(self, name): # Sanitize the file name so that it can't be dangerous. if name is not None: # Just use the basename of the file -- anything else is dangerous. name = os.path.basename(name) # File names longer than 255 characters can cause problems on older OSes. if len(name) > 255: name, ext = os.path.splitext(name) ext = ext[:255] name = name[:255 - len(ext)] + ext name = validate_file_name(name) self._name = name # 文件名属性的公共接口,通过 property 装饰器实现。 name = property(_get_name, _set_name)
-
Storage
类是文件存储的基类,定义了文件存储的接口。位于django.core.files.storage
模块中。class Storage: # 从存储中检索指定的文件,返回一个文件对象。 def open(self, name, mode='rb'): """Retrieve the specified file from storage.""" return self._open(name, mode) # 将新内容保存到指定名称的文件中。内容应为适当的文件对象或任何 Python 文件类似对象,准备好从头开始读取。 def save(self, name, content, max_length=None): # Get the proper name for the file, as it will actually be saved. if name is None: name = content.name if not hasattr(content, 'chunks'): content = File(content, name) name = self.get_available_name(name, max_length=max_length) name = self._save(name, content) validate_file_name(name, allow_relative_path=True) return name # 返回一个基于提供的文件名的文件名,适用于目标存储系统 def get_valid_name(self, name): return get_valid_filename(name) # 返回一个备用文件名,通过向文件名添加下划线和一个随机的 7 个字符的字母数字字符串 def get_alternative_name(self, file_root, file_ext): return '%s_%s%s' % (file_root, get_random_string(7), file_ext) # 返回在目标存储系统上可用且可用于写入新内容的文件名。 def get_available_name(self, name, max_length=None): name = str(name).replace('\\', '/') dir_name, file_name = os.path.split(name) if '..' in pathlib.PurePath(dir_name).parts: raise SuspiciousFileOperation("Detected path traversal attempt in '%s'" % dir_name) validate_file_name(file_name) file_root, file_ext = os.path.splitext(file_name) while self.exists(name) or (max_length and len(name) > max_length): name = os.path.join(dir_name, self.get_alternative_name(file_root, file_ext)) if max_length is None: continue truncation = len(name) - max_length if truncation > 0: file_root = file_root[:-truncation] if not file_root: raise SuspiciousFileOperation( 'Storage can not find an available filename for "%s". ' 'Please make sure that the corresponding file field ' 'allows sufficient "max_length".' % name ) name = os.path.join(dir_name, self.get_alternative_name(file_root, file_ext)) return name # 通过调用 get_valid_name() 验证文件名,并返回可传递给 save() 方法的文件名。 def generate_filename(self, filename): filename = str(filename).replace('\\', '/') # `filename` may include a path as returned by FileField.upload_to. dirname, filename = os.path.split(filename) if '..' in pathlib.PurePath(dirname).parts: raise SuspiciousFileOperation("Detected path traversal attempt in '%s'" % dirname) return os.path.normpath(os.path.join(dirname, self.get_valid_name(filename))) # 返回可以使用 Python 的内置 open() 函数检索文件的本地文件系统路径。不能使用 open() 访问的存储系统不应实现此方法。 def path(self, name): """ Return a local filesystem path where the file can be retrieved using Python's built-in open() function. Storage systems that can't be accessed using open() should *not* implement this method. """ raise NotImplementedError("This backend doesn't support absolute paths.") def delete(self, name): """ 从存储系统中删除指定的文件 """ raise NotImplementedError('subclasses of Storage must provide a delete() method') def exists(self, name): """ 如果存储系统中已存在由给定名称引用的文件,则返回 True,否则如果名称可用于新文件则返回 False。 """ raise NotImplementedError('subclasses of Storage must provide an exists() method') def listdir(self, path): """ 列出指定路径的内容。返回一个 2 元组的列表:第一个项目是目录,第二个项目是文件。 """ raise NotImplementedError('subclasses of Storage must provide a listdir() method') def size(self, name): """ 返回由名称指定的文件的总大小(以字节为单位)。 """ raise NotImplementedError('subclasses of Storage must provide a size() method') def url(self, name): """ 返回一个绝对 URL,可以通过 Web 浏览器直接访问文件的内容。 """ raise NotImplementedError('subclasses of Storage must provide a url() method') def get_accessed_time(self, name): """ 返回由名称指定的文件的上次访问时间(作为日期时间)。如果 USE_TZ=True,则日期时间将是时区感知的。 """ raise NotImplementedError('subclasses of Storage must provide a get_accessed_time() method') def get_created_time(self, name): """ 返回由名称指定的文件的创建时间(作为日期时间)。如果 USE_TZ=True,则日期时间将是时区感知的。 """ raise NotImplementedError('subclasses of Storage must provide a get_created_time() method') def get_modified_time(self, name): """ 返回由名称指定的文件的上次修改时间(作为日期时间)。如果 USE_TZ=True,则日期时间将是时区感知的。 """ raise NotImplementedError('subclasses of Storage must provide a get_modified_time() method')
-
FileSystemStorage
类是默认的文件系统存储后端,用于本地文件系统的存储。位于 django.core.files.storage 模块中。@deconstructible class FileSystemStorage(Storage): OS_OPEN_FLAGS = os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, 'O_BINARY', 0) # 构造函数,接受文件系统存储的位置、基础 URL、文件权限模式和目录权限模式等参数 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.""" if setting == 'MEDIA_ROOT': self.__dict__.pop('base_location', None) self.__dict__.pop('location', None) elif setting == 'MEDIA_URL': self.__dict__.pop('base_url', None) elif setting == 'FILE_UPLOAD_PERMISSIONS': self.__dict__.pop('file_permissions_mode', None) elif setting == 'FILE_UPLOAD_DIRECTORY_PERMISSIONS': self.__dict__.pop('directory_permissions_mode', None) # 辅助方法,返回给定值或设置的值(如果给定值为 None) def _value_or_setting(self, value, setting): return setting if value is None else value # 返回存储的基本位置,如果未提供则使用 MEDIA_ROOT 设置。 @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) # 返回存储的基本 URL,如果未提供则使用 MEDIA_URL 设置。 @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) # 返回文件权限模式,如果未提供则使用 FILE_UPLOAD_PERMISSIONS 设置。 @cached_property def file_permissions_mode(self): return self._value_or_setting(self._file_permissions_mode, settings.FILE_UPLOAD_PERMISSIONS) # 返回目录权限模式,如果未提供则使用 FILE_UPLOAD_DIRECTORY_PERMISSIONS 设置 @cached_property def directory_permissions_mode(self): return self._value_or_setting(self._directory_permissions_mode, settings.FILE_UPLOAD_DIRECTORY_PERMISSIONS) # 打开指定名称的文件并返回一个 File 对象。 def _open(self, name, mode='rb'): return File(open(self.path(name), mode)) # 将内容保存到指定名称的文件中,处理可能的并发问题,返回保存的文件名。 def _save(self, name, content): full_path = self.path(name) directory = os.path.dirname(full_path) try: if self.directory_permissions_mode is not None: old_umask = os.umask(0o777 & ~self.directory_permissions_mode) try: os.makedirs(directory, self.directory_permissions_mode, exist_ok=True) finally: os.umask(old_umask) else: os.makedirs(directory, exist_ok=True) except FileExistsError: raise FileExistsError('%s exists and is not a directory.' % directory) while True: try: 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) # Ensure the saved path is always relative to the storage root. name = os.path.relpath(full_path, self.location) # Store filenames with forward slashes, even on Windows. return str(name).replace('\\', '/') # 从存储系统中删除指定名称的文件。 def delete(self, name): assert name, "The name argument is not allowed to be empty." name = self.path(name) # If the file or directory exists, delete it from the filesystem. try: if os.path.isdir(name): os.rmdir(name) else: os.remove(name) except FileNotFoundError: # FileNotFoundError is raised if the file or directory was removed # concurrently. pass # 如果存储系统中已存在由给定名称引用的文件,则返回 True,否则如果名称可用于新文件则返回 False。 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)) # 返回文件的绝对 URL,可以通过 Web 浏览器直接访问。 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) # 如果启用了时区支持,则在 UTC 中创建一个明确的 datetime 对象;否则在本地时区创建一个纯粹的对象。 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. """ if settings.USE_TZ: # Safe to use .replace() because UTC doesn't have DST return datetime.utcfromtimestamp(ts).replace(tzinfo=timezone.utc) else: return datetime.fromtimestamp(ts) # 返回由名称指定的文件的上次访问时间(作为日期时间)。 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))) def get_storage_class(import_path=None): return import_string(import_path or settings.DEFAULT_FILE_STORAGE)
6.django中校验模块源码解读
-
django.core.checks
模块是 Django 中用于执行各种检查的关键模块。这些检查用于确保项目配置的一致性和正确性。 -
CheckMessage
类用于表示一个检查的结果信息。它包含了检查的级别(错误、警告等)、检查的详细说明和相关的对象等信息。源码位于django/core/checks/messages
中。class CheckMessage: def __init__(self, level, msg, hint=None, obj=None, id=None): assert isinstance(level, int), "The first argument should be level." self.level = level # 表示检查消息的严重级别的整数。 self.msg = msg # 包含检查主要消息的字符串 self.hint = hint # 提供提示或附加信息的可选字符串。 self.obj = obj # 与检查消息关联的可选对象。 self.id = id # 检查消息的可选标识符。 def __eq__(self, other): return ( isinstance(other, self.__class__) and all(getattr(self, attr) == getattr(other, attr) for attr in ['level', 'msg', 'hint', 'obj', 'id']) ) def __str__(self): from django.db import models if self.obj is None: obj = "?" elif isinstance(self.obj, models.base.ModelBase): # We need to hardcode ModelBase and Field cases because its __str__ # method doesn't return "applabel.modellabel" and cannot be changed. obj = self.obj._meta.label else: obj = str(self.obj) id = "(%s) " % self.id if self.id else "" hint = "\n\tHINT: %s" % self.hint if self.hint else '' return "%s: %s%s%s" % (obj, id, self.msg, hint) def __repr__(self): return "<%s: level=%r, msg=%r, hint=%r, obj=%r, id=%r>" % \ (self.__class__.__name__, self.level, self.msg, self.hint, self.obj, self.id) # 检查检查消息的严重级别是否等于或高于指定级别(默认为 ERROR)。 def is_serious(self, level=ERROR): return self.level >= level # 检查检查消息的标识符是否存在于 Django 设置中的已消音系统检查列表中。 def is_silenced(self): from django.conf import settings return self.id in settings.SILENCED_SYSTEM_CHECKS
-
CheckRegistry
类负责管理和执行所有注册的检查。它在 run_checks() 中被实例化,并提供了 run_checks() 方法来执行检查。源码位于django/core/checks/registry.py
中。class CheckRegistry: def __init__(self): self.registered_checks = set() # 用于存储普通检查函数 self.deployment_checks = set() # 用于存储部署检查函数 def register(self, check=None, *tags, **kwargs): """ 该方法可以用作函数或装饰器,用于注册检查函数 Example:: registry = CheckRegistry() @registry.register('mytag', 'anothertag') def my_check(app_configs, **kwargs): # ... perform checks and collect `errors` ... return errors # or registry.register(my_check, 'mytag', 'anothertag') """ def inner(check): if not func_accepts_kwargs(check): raise TypeError( 'Check functions must accept keyword arguments (**kwargs).' ) check.tags = tags checks = self.deployment_checks if kwargs.get('deploy') else self.registered_checks checks.add(check) return check if callable(check): return inner(check) else: if check: tags += (check,) return inner def run_checks(self, app_configs=None, tags=None, include_deployment_checks=False, databases=None): """ 该方法运行所有注册的检查函数并返回错误和警告的列表 """ errors = [] checks = self.get_checks(include_deployment_checks) if tags is not None: checks = [check for check in checks if not set(check.tags).isdisjoint(tags)] for check in checks: new_errors = check(app_configs=app_configs, databases=databases) assert is_iterable(new_errors), ( "The function %r did not return a list. All functions registered " "with the checks registry must return a list." % check) errors.extend(new_errors) return errors # 用于检查特定标签是否存在于注册的检查函数中。 def tag_exists(self, tag, include_deployment_checks=False): return tag in self.tags_available(include_deployment_checks) # 返回所有可用标签的集合,可以选择包括或排除部署检查。 def tags_available(self, deployment_checks=False): return set(chain.from_iterable( check.tags for check in self.get_checks(deployment_checks) )) # 返回所有要运行的检查函数的列表,可以选择包括或排除部署检查。 def get_checks(self, include_deployment_checks=False): checks = list(self.registered_checks) if include_deployment_checks: checks.extend(self.deployment_checks) return checks
-
使用示例
# 会创建一个 CheckRegistry 的实例 registry = CheckRegistry(tags=['my_check_tag']) # 使用 register 方法注册系统检查函数。这些检查函数是实际执行检查的函数,它们会返回一组 CheckMessage 对象表示检查结果。 @registry.register('my_check_tag') def my_check(app_configs, **kwargs): # 执行自定义检查逻辑 return [CheckMessage(level=2, msg='My check failed.', hint='Fix it!', obj=my_model)] # 通过 run_checks 方法执行所有注册的系统检查。该方法会遍历所有注册的检查函数,并执行它们,将检查结果收集到一个列表中。 results = registry.run_checks(app_configs=my_app_configs) # 系统检查执行后,返回一个包含 CheckMessage 对象的列表。应用程序可以根据这些结果采取相应的措施,例如打印消息、记录日志或引发异常。 for result in results: if result.is_serious(): print(f"Serious issue: {result}")