docker setup
This commit is contained in:
		| @ -0,0 +1,3 @@ | ||||
| from django.views.generic.base import View | ||||
|  | ||||
| __all__ = ["View"] | ||||
							
								
								
									
										160
									
								
								srcs/.venv/lib/python3.11/site-packages/django/views/csrf.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								srcs/.venv/lib/python3.11/site-packages/django/views/csrf.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,160 @@ | ||||
| from django.conf import settings | ||||
| from django.http import HttpResponseForbidden | ||||
| from django.template import Context, Engine, TemplateDoesNotExist, loader | ||||
| from django.utils.translation import gettext as _ | ||||
| from django.utils.version import get_docs_version | ||||
|  | ||||
| # We include the template inline since we need to be able to reliably display | ||||
| # this error message, especially for the sake of developers, and there isn't any | ||||
| # other way of making it available independent of what is in the settings file. | ||||
|  | ||||
| # Only the text appearing with DEBUG=False is translated. Normal translation | ||||
| # tags cannot be used with this inline templates as makemessages would not be | ||||
| # able to discover the strings. | ||||
|  | ||||
| CSRF_FAILURE_TEMPLATE = """ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|   <meta http-equiv="content-type" content="text/html; charset=utf-8"> | ||||
|   <meta name="robots" content="NONE,NOARCHIVE"> | ||||
|   <title>403 Forbidden</title> | ||||
|   <style type="text/css"> | ||||
|     html * { padding:0; margin:0; } | ||||
|     body * { padding:10px 20px; } | ||||
|     body * * { padding:0; } | ||||
|     body { font:small sans-serif; background:#eee; color:#000; } | ||||
|     body>div { border-bottom:1px solid #ddd; } | ||||
|     h1 { font-weight:normal; margin-bottom:.4em; } | ||||
|     h1 span { font-size:60%; color:#666; font-weight:normal; } | ||||
|     #info { background:#f6f6f6; } | ||||
|     #info ul { margin: 0.5em 4em; } | ||||
|     #info p, #summary p { padding-top:10px; } | ||||
|     #summary { background: #ffc; } | ||||
|     #explanation { background:#eee; border-bottom: 0px none; } | ||||
|   </style> | ||||
| </head> | ||||
| <body> | ||||
| <div id="summary"> | ||||
|   <h1>{{ title }} <span>(403)</span></h1> | ||||
|   <p>{{ main }}</p> | ||||
| {% if no_referer %} | ||||
|   <p>{{ no_referer1 }}</p> | ||||
|   <p>{{ no_referer2 }}</p> | ||||
|   <p>{{ no_referer3 }}</p> | ||||
| {% endif %} | ||||
| {% if no_cookie %} | ||||
|   <p>{{ no_cookie1 }}</p> | ||||
|   <p>{{ no_cookie2 }}</p> | ||||
| {% endif %} | ||||
| </div> | ||||
| {% if DEBUG %} | ||||
| <div id="info"> | ||||
|   <h2>Help</h2> | ||||
|     {% if reason %} | ||||
|     <p>Reason given for failure:</p> | ||||
|     <pre> | ||||
|     {{ reason }} | ||||
|     </pre> | ||||
|     {% endif %} | ||||
|  | ||||
|   <p>In general, this can occur when there is a genuine Cross Site Request Forgery, or when | ||||
|   <a | ||||
|   href="https://docs.djangoproject.com/en/{{ docs_version }}/ref/csrf/">Django’s | ||||
|   CSRF mechanism</a> has not been used correctly.  For POST forms, you need to | ||||
|   ensure:</p> | ||||
|  | ||||
|   <ul> | ||||
|     <li>Your browser is accepting cookies.</li> | ||||
|  | ||||
|     <li>The view function passes a <code>request</code> to the template’s <a | ||||
|     href="https://docs.djangoproject.com/en/dev/topics/templates/#django.template.backends.base.Template.render"><code>render</code></a> | ||||
|     method.</li> | ||||
|  | ||||
|     <li>In the template, there is a <code>{% templatetag openblock %} csrf_token | ||||
|     {% templatetag closeblock %}</code> template tag inside each POST form that | ||||
|     targets an internal URL.</li> | ||||
|  | ||||
|     <li>If you are not using <code>CsrfViewMiddleware</code>, then you must use | ||||
|     <code>csrf_protect</code> on any views that use the <code>csrf_token</code> | ||||
|     template tag, as well as those that accept the POST data.</li> | ||||
|  | ||||
|     <li>The form has a valid CSRF token. After logging in in another browser | ||||
|     tab or hitting the back button after a login, you may need to reload the | ||||
|     page with the form, because the token is rotated after a login.</li> | ||||
|   </ul> | ||||
|  | ||||
|   <p>You’re seeing the help section of this page because you have <code>DEBUG = | ||||
|   True</code> in your Django settings file. Change that to <code>False</code>, | ||||
|   and only the initial error message will be displayed.  </p> | ||||
|  | ||||
|   <p>You can customize this page using the CSRF_FAILURE_VIEW setting.</p> | ||||
| </div> | ||||
| {% else %} | ||||
| <div id="explanation"> | ||||
|   <p><small>{{ more }}</small></p> | ||||
| </div> | ||||
| {% endif %} | ||||
| </body> | ||||
| </html> | ||||
| """  # NOQA | ||||
| CSRF_FAILURE_TEMPLATE_NAME = "403_csrf.html" | ||||
|  | ||||
|  | ||||
| def csrf_failure(request, reason="", template_name=CSRF_FAILURE_TEMPLATE_NAME): | ||||
|     """ | ||||
|     Default view used when request fails CSRF protection | ||||
|     """ | ||||
|     from django.middleware.csrf import REASON_NO_CSRF_COOKIE, REASON_NO_REFERER | ||||
|  | ||||
|     c = { | ||||
|         "title": _("Forbidden"), | ||||
|         "main": _("CSRF verification failed. Request aborted."), | ||||
|         "reason": reason, | ||||
|         "no_referer": reason == REASON_NO_REFERER, | ||||
|         "no_referer1": _( | ||||
|             "You are seeing this message because this HTTPS site requires a " | ||||
|             "“Referer header” to be sent by your web browser, but none was " | ||||
|             "sent. This header is required for security reasons, to ensure " | ||||
|             "that your browser is not being hijacked by third parties." | ||||
|         ), | ||||
|         "no_referer2": _( | ||||
|             "If you have configured your browser to disable “Referer” headers, " | ||||
|             "please re-enable them, at least for this site, or for HTTPS " | ||||
|             "connections, or for “same-origin” requests." | ||||
|         ), | ||||
|         "no_referer3": _( | ||||
|             'If you are using the <meta name="referrer" ' | ||||
|             'content="no-referrer"> tag or including the “Referrer-Policy: ' | ||||
|             "no-referrer” header, please remove them. The CSRF protection " | ||||
|             "requires the “Referer” header to do strict referer checking. If " | ||||
|             "you’re concerned about privacy, use alternatives like " | ||||
|             '<a rel="noreferrer" …> for links to third-party sites.' | ||||
|         ), | ||||
|         "no_cookie": reason == REASON_NO_CSRF_COOKIE, | ||||
|         "no_cookie1": _( | ||||
|             "You are seeing this message because this site requires a CSRF " | ||||
|             "cookie when submitting forms. This cookie is required for " | ||||
|             "security reasons, to ensure that your browser is not being " | ||||
|             "hijacked by third parties." | ||||
|         ), | ||||
|         "no_cookie2": _( | ||||
|             "If you have configured your browser to disable cookies, please " | ||||
|             "re-enable them, at least for this site, or for “same-origin” " | ||||
|             "requests." | ||||
|         ), | ||||
|         "DEBUG": settings.DEBUG, | ||||
|         "docs_version": get_docs_version(), | ||||
|         "more": _("More information is available with DEBUG=True."), | ||||
|     } | ||||
|     try: | ||||
|         t = loader.get_template(template_name) | ||||
|     except TemplateDoesNotExist: | ||||
|         if template_name == CSRF_FAILURE_TEMPLATE_NAME: | ||||
|             # If the default template doesn't exist, use the string template. | ||||
|             t = Engine().from_string(CSRF_FAILURE_TEMPLATE) | ||||
|             c = Context(c) | ||||
|         else: | ||||
|             # Raise if a developer-specified template doesn't exist. | ||||
|             raise | ||||
|     return HttpResponseForbidden(t.render(c)) | ||||
							
								
								
									
										647
									
								
								srcs/.venv/lib/python3.11/site-packages/django/views/debug.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										647
									
								
								srcs/.venv/lib/python3.11/site-packages/django/views/debug.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,647 @@ | ||||
| import functools | ||||
| import itertools | ||||
| import re | ||||
| import sys | ||||
| import types | ||||
| import warnings | ||||
| from pathlib import Path | ||||
|  | ||||
| from django.conf import settings | ||||
| from django.http import Http404, HttpResponse, HttpResponseNotFound | ||||
| from django.template import Context, Engine, TemplateDoesNotExist | ||||
| from django.template.defaultfilters import pprint | ||||
| from django.urls import resolve | ||||
| from django.utils import timezone | ||||
| from django.utils.datastructures import MultiValueDict | ||||
| from django.utils.encoding import force_str | ||||
| from django.utils.module_loading import import_string | ||||
| from django.utils.regex_helper import _lazy_re_compile | ||||
| from django.utils.version import PY311, get_docs_version | ||||
|  | ||||
| # Minimal Django templates engine to render the error templates | ||||
| # regardless of the project's TEMPLATES setting. Templates are | ||||
| # read directly from the filesystem so that the error handler | ||||
| # works even if the template loader is broken. | ||||
| DEBUG_ENGINE = Engine( | ||||
|     debug=True, | ||||
|     libraries={"i18n": "django.templatetags.i18n"}, | ||||
| ) | ||||
|  | ||||
|  | ||||
| def builtin_template_path(name): | ||||
|     """ | ||||
|     Return a path to a builtin template. | ||||
|  | ||||
|     Avoid calling this function at the module level or in a class-definition | ||||
|     because __file__ may not exist, e.g. in frozen environments. | ||||
|     """ | ||||
|     return Path(__file__).parent / "templates" / name | ||||
|  | ||||
|  | ||||
| class ExceptionCycleWarning(UserWarning): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class CallableSettingWrapper: | ||||
|     """ | ||||
|     Object to wrap callable appearing in settings. | ||||
|     * Not to call in the debug page (#21345). | ||||
|     * Not to break the debug page if the callable forbidding to set attributes | ||||
|       (#23070). | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, callable_setting): | ||||
|         self._wrapped = callable_setting | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return repr(self._wrapped) | ||||
|  | ||||
|  | ||||
| def technical_500_response(request, exc_type, exc_value, tb, status_code=500): | ||||
|     """ | ||||
|     Create a technical server error response. The last three arguments are | ||||
|     the values returned from sys.exc_info() and friends. | ||||
|     """ | ||||
|     reporter = get_exception_reporter_class(request)(request, exc_type, exc_value, tb) | ||||
|     if request.accepts("text/html"): | ||||
|         html = reporter.get_traceback_html() | ||||
|         return HttpResponse(html, status=status_code) | ||||
|     else: | ||||
|         text = reporter.get_traceback_text() | ||||
|         return HttpResponse( | ||||
|             text, status=status_code, content_type="text/plain; charset=utf-8" | ||||
|         ) | ||||
|  | ||||
|  | ||||
| @functools.lru_cache | ||||
| def get_default_exception_reporter_filter(): | ||||
|     # Instantiate the default filter for the first time and cache it. | ||||
|     return import_string(settings.DEFAULT_EXCEPTION_REPORTER_FILTER)() | ||||
|  | ||||
|  | ||||
| def get_exception_reporter_filter(request): | ||||
|     default_filter = get_default_exception_reporter_filter() | ||||
|     return getattr(request, "exception_reporter_filter", default_filter) | ||||
|  | ||||
|  | ||||
| def get_exception_reporter_class(request): | ||||
|     default_exception_reporter_class = import_string( | ||||
|         settings.DEFAULT_EXCEPTION_REPORTER | ||||
|     ) | ||||
|     return getattr( | ||||
|         request, "exception_reporter_class", default_exception_reporter_class | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def get_caller(request): | ||||
|     resolver_match = request.resolver_match | ||||
|     if resolver_match is None: | ||||
|         try: | ||||
|             resolver_match = resolve(request.path) | ||||
|         except Http404: | ||||
|             pass | ||||
|     return "" if resolver_match is None else resolver_match._func_path | ||||
|  | ||||
|  | ||||
| class SafeExceptionReporterFilter: | ||||
|     """ | ||||
|     Use annotations made by the sensitive_post_parameters and | ||||
|     sensitive_variables decorators to filter out sensitive information. | ||||
|     """ | ||||
|  | ||||
|     cleansed_substitute = "********************" | ||||
|     hidden_settings = _lazy_re_compile( | ||||
|         "API|TOKEN|KEY|SECRET|PASS|SIGNATURE|HTTP_COOKIE", flags=re.I | ||||
|     ) | ||||
|  | ||||
|     def cleanse_setting(self, key, value): | ||||
|         """ | ||||
|         Cleanse an individual setting key/value of sensitive content. If the | ||||
|         value is a dictionary, recursively cleanse the keys in that dictionary. | ||||
|         """ | ||||
|         if key == settings.SESSION_COOKIE_NAME: | ||||
|             is_sensitive = True | ||||
|         else: | ||||
|             try: | ||||
|                 is_sensitive = self.hidden_settings.search(key) | ||||
|             except TypeError: | ||||
|                 is_sensitive = False | ||||
|  | ||||
|         if is_sensitive: | ||||
|             cleansed = self.cleansed_substitute | ||||
|         elif isinstance(value, dict): | ||||
|             cleansed = {k: self.cleanse_setting(k, v) for k, v in value.items()} | ||||
|         elif isinstance(value, list): | ||||
|             cleansed = [self.cleanse_setting("", v) for v in value] | ||||
|         elif isinstance(value, tuple): | ||||
|             cleansed = tuple([self.cleanse_setting("", v) for v in value]) | ||||
|         else: | ||||
|             cleansed = value | ||||
|  | ||||
|         if callable(cleansed): | ||||
|             cleansed = CallableSettingWrapper(cleansed) | ||||
|  | ||||
|         return cleansed | ||||
|  | ||||
|     def get_safe_settings(self): | ||||
|         """ | ||||
|         Return a dictionary of the settings module with values of sensitive | ||||
|         settings replaced with stars (*********). | ||||
|         """ | ||||
|         settings_dict = {} | ||||
|         for k in dir(settings): | ||||
|             if k.isupper(): | ||||
|                 settings_dict[k] = self.cleanse_setting(k, getattr(settings, k)) | ||||
|         return settings_dict | ||||
|  | ||||
|     def get_safe_request_meta(self, request): | ||||
|         """ | ||||
|         Return a dictionary of request.META with sensitive values redacted. | ||||
|         """ | ||||
|         if not hasattr(request, "META"): | ||||
|             return {} | ||||
|         return {k: self.cleanse_setting(k, v) for k, v in request.META.items()} | ||||
|  | ||||
|     def get_safe_cookies(self, request): | ||||
|         """ | ||||
|         Return a dictionary of request.COOKIES with sensitive values redacted. | ||||
|         """ | ||||
|         if not hasattr(request, "COOKIES"): | ||||
|             return {} | ||||
|         return {k: self.cleanse_setting(k, v) for k, v in request.COOKIES.items()} | ||||
|  | ||||
|     def is_active(self, request): | ||||
|         """ | ||||
|         This filter is to add safety in production environments (i.e. DEBUG | ||||
|         is False). If DEBUG is True then your site is not safe anyway. | ||||
|         This hook is provided as a convenience to easily activate or | ||||
|         deactivate the filter on a per request basis. | ||||
|         """ | ||||
|         return settings.DEBUG is False | ||||
|  | ||||
|     def get_cleansed_multivaluedict(self, request, multivaluedict): | ||||
|         """ | ||||
|         Replace the keys in a MultiValueDict marked as sensitive with stars. | ||||
|         This mitigates leaking sensitive POST parameters if something like | ||||
|         request.POST['nonexistent_key'] throws an exception (#21098). | ||||
|         """ | ||||
|         sensitive_post_parameters = getattr(request, "sensitive_post_parameters", []) | ||||
|         if self.is_active(request) and sensitive_post_parameters: | ||||
|             multivaluedict = multivaluedict.copy() | ||||
|             for param in sensitive_post_parameters: | ||||
|                 if param in multivaluedict: | ||||
|                     multivaluedict[param] = self.cleansed_substitute | ||||
|         return multivaluedict | ||||
|  | ||||
|     def get_post_parameters(self, request): | ||||
|         """ | ||||
|         Replace the values of POST parameters marked as sensitive with | ||||
|         stars (*********). | ||||
|         """ | ||||
|         if request is None: | ||||
|             return {} | ||||
|         else: | ||||
|             sensitive_post_parameters = getattr( | ||||
|                 request, "sensitive_post_parameters", [] | ||||
|             ) | ||||
|             if self.is_active(request) and sensitive_post_parameters: | ||||
|                 cleansed = request.POST.copy() | ||||
|                 if sensitive_post_parameters == "__ALL__": | ||||
|                     # Cleanse all parameters. | ||||
|                     for k in cleansed: | ||||
|                         cleansed[k] = self.cleansed_substitute | ||||
|                     return cleansed | ||||
|                 else: | ||||
|                     # Cleanse only the specified parameters. | ||||
|                     for param in sensitive_post_parameters: | ||||
|                         if param in cleansed: | ||||
|                             cleansed[param] = self.cleansed_substitute | ||||
|                     return cleansed | ||||
|             else: | ||||
|                 return request.POST | ||||
|  | ||||
|     def cleanse_special_types(self, request, value): | ||||
|         try: | ||||
|             # If value is lazy or a complex object of another kind, this check | ||||
|             # might raise an exception. isinstance checks that lazy | ||||
|             # MultiValueDicts will have a return value. | ||||
|             is_multivalue_dict = isinstance(value, MultiValueDict) | ||||
|         except Exception as e: | ||||
|             return "{!r} while evaluating {!r}".format(e, value) | ||||
|  | ||||
|         if is_multivalue_dict: | ||||
|             # Cleanse MultiValueDicts (request.POST is the one we usually care about) | ||||
|             value = self.get_cleansed_multivaluedict(request, value) | ||||
|         return value | ||||
|  | ||||
|     def get_traceback_frame_variables(self, request, tb_frame): | ||||
|         """ | ||||
|         Replace the values of variables marked as sensitive with | ||||
|         stars (*********). | ||||
|         """ | ||||
|         # Loop through the frame's callers to see if the sensitive_variables | ||||
|         # decorator was used. | ||||
|         current_frame = tb_frame.f_back | ||||
|         sensitive_variables = None | ||||
|         while current_frame is not None: | ||||
|             if ( | ||||
|                 current_frame.f_code.co_name == "sensitive_variables_wrapper" | ||||
|                 and "sensitive_variables_wrapper" in current_frame.f_locals | ||||
|             ): | ||||
|                 # The sensitive_variables decorator was used, so we take note | ||||
|                 # of the sensitive variables' names. | ||||
|                 wrapper = current_frame.f_locals["sensitive_variables_wrapper"] | ||||
|                 sensitive_variables = getattr(wrapper, "sensitive_variables", None) | ||||
|                 break | ||||
|             current_frame = current_frame.f_back | ||||
|  | ||||
|         cleansed = {} | ||||
|         if self.is_active(request) and sensitive_variables: | ||||
|             if sensitive_variables == "__ALL__": | ||||
|                 # Cleanse all variables | ||||
|                 for name in tb_frame.f_locals: | ||||
|                     cleansed[name] = self.cleansed_substitute | ||||
|             else: | ||||
|                 # Cleanse specified variables | ||||
|                 for name, value in tb_frame.f_locals.items(): | ||||
|                     if name in sensitive_variables: | ||||
|                         value = self.cleansed_substitute | ||||
|                     else: | ||||
|                         value = self.cleanse_special_types(request, value) | ||||
|                     cleansed[name] = value | ||||
|         else: | ||||
|             # Potentially cleanse the request and any MultiValueDicts if they | ||||
|             # are one of the frame variables. | ||||
|             for name, value in tb_frame.f_locals.items(): | ||||
|                 cleansed[name] = self.cleanse_special_types(request, value) | ||||
|  | ||||
|         if ( | ||||
|             tb_frame.f_code.co_name == "sensitive_variables_wrapper" | ||||
|             and "sensitive_variables_wrapper" in tb_frame.f_locals | ||||
|         ): | ||||
|             # For good measure, obfuscate the decorated function's arguments in | ||||
|             # the sensitive_variables decorator's frame, in case the variables | ||||
|             # associated with those arguments were meant to be obfuscated from | ||||
|             # the decorated function's frame. | ||||
|             cleansed["func_args"] = self.cleansed_substitute | ||||
|             cleansed["func_kwargs"] = self.cleansed_substitute | ||||
|  | ||||
|         return cleansed.items() | ||||
|  | ||||
|  | ||||
| class ExceptionReporter: | ||||
|     """Organize and coordinate reporting on exceptions.""" | ||||
|  | ||||
|     @property | ||||
|     def html_template_path(self): | ||||
|         return builtin_template_path("technical_500.html") | ||||
|  | ||||
|     @property | ||||
|     def text_template_path(self): | ||||
|         return builtin_template_path("technical_500.txt") | ||||
|  | ||||
|     def __init__(self, request, exc_type, exc_value, tb, is_email=False): | ||||
|         self.request = request | ||||
|         self.filter = get_exception_reporter_filter(self.request) | ||||
|         self.exc_type = exc_type | ||||
|         self.exc_value = exc_value | ||||
|         self.tb = tb | ||||
|         self.is_email = is_email | ||||
|  | ||||
|         self.template_info = getattr(self.exc_value, "template_debug", None) | ||||
|         self.template_does_not_exist = False | ||||
|         self.postmortem = None | ||||
|  | ||||
|     def _get_raw_insecure_uri(self): | ||||
|         """ | ||||
|         Return an absolute URI from variables available in this request. Skip | ||||
|         allowed hosts protection, so may return insecure URI. | ||||
|         """ | ||||
|         return "{scheme}://{host}{path}".format( | ||||
|             scheme=self.request.scheme, | ||||
|             host=self.request._get_raw_host(), | ||||
|             path=self.request.get_full_path(), | ||||
|         ) | ||||
|  | ||||
|     def get_traceback_data(self): | ||||
|         """Return a dictionary containing traceback information.""" | ||||
|         if self.exc_type and issubclass(self.exc_type, TemplateDoesNotExist): | ||||
|             self.template_does_not_exist = True | ||||
|             self.postmortem = self.exc_value.chain or [self.exc_value] | ||||
|  | ||||
|         frames = self.get_traceback_frames() | ||||
|         for i, frame in enumerate(frames): | ||||
|             if "vars" in frame: | ||||
|                 frame_vars = [] | ||||
|                 for k, v in frame["vars"]: | ||||
|                     v = pprint(v) | ||||
|                     # Trim large blobs of data | ||||
|                     if len(v) > 4096: | ||||
|                         v = "%s… <trimmed %d bytes string>" % (v[0:4096], len(v)) | ||||
|                     frame_vars.append((k, v)) | ||||
|                 frame["vars"] = frame_vars | ||||
|             frames[i] = frame | ||||
|  | ||||
|         unicode_hint = "" | ||||
|         if self.exc_type and issubclass(self.exc_type, UnicodeError): | ||||
|             start = getattr(self.exc_value, "start", None) | ||||
|             end = getattr(self.exc_value, "end", None) | ||||
|             if start is not None and end is not None: | ||||
|                 unicode_str = self.exc_value.args[1] | ||||
|                 unicode_hint = force_str( | ||||
|                     unicode_str[max(start - 5, 0) : min(end + 5, len(unicode_str))], | ||||
|                     "ascii", | ||||
|                     errors="replace", | ||||
|                 ) | ||||
|         from django import get_version | ||||
|  | ||||
|         if self.request is None: | ||||
|             user_str = None | ||||
|         else: | ||||
|             try: | ||||
|                 user_str = str(self.request.user) | ||||
|             except Exception: | ||||
|                 # request.user may raise OperationalError if the database is | ||||
|                 # unavailable, for example. | ||||
|                 user_str = "[unable to retrieve the current user]" | ||||
|  | ||||
|         c = { | ||||
|             "is_email": self.is_email, | ||||
|             "unicode_hint": unicode_hint, | ||||
|             "frames": frames, | ||||
|             "request": self.request, | ||||
|             "request_meta": self.filter.get_safe_request_meta(self.request), | ||||
|             "request_COOKIES_items": self.filter.get_safe_cookies(self.request).items(), | ||||
|             "user_str": user_str, | ||||
|             "filtered_POST_items": list( | ||||
|                 self.filter.get_post_parameters(self.request).items() | ||||
|             ), | ||||
|             "settings": self.filter.get_safe_settings(), | ||||
|             "sys_executable": sys.executable, | ||||
|             "sys_version_info": "%d.%d.%d" % sys.version_info[0:3], | ||||
|             "server_time": timezone.now(), | ||||
|             "django_version_info": get_version(), | ||||
|             "sys_path": sys.path, | ||||
|             "template_info": self.template_info, | ||||
|             "template_does_not_exist": self.template_does_not_exist, | ||||
|             "postmortem": self.postmortem, | ||||
|         } | ||||
|         if self.request is not None: | ||||
|             c["request_GET_items"] = self.request.GET.items() | ||||
|             c["request_FILES_items"] = self.request.FILES.items() | ||||
|             c["request_insecure_uri"] = self._get_raw_insecure_uri() | ||||
|             c["raising_view_name"] = get_caller(self.request) | ||||
|  | ||||
|         # Check whether exception info is available | ||||
|         if self.exc_type: | ||||
|             c["exception_type"] = self.exc_type.__name__ | ||||
|         if self.exc_value: | ||||
|             c["exception_value"] = str(self.exc_value) | ||||
|             if exc_notes := getattr(self.exc_value, "__notes__", None): | ||||
|                 c["exception_notes"] = "\n" + "\n".join(exc_notes) | ||||
|         if frames: | ||||
|             c["lastframe"] = frames[-1] | ||||
|         return c | ||||
|  | ||||
|     def get_traceback_html(self): | ||||
|         """Return HTML version of debug 500 HTTP error page.""" | ||||
|         with self.html_template_path.open(encoding="utf-8") as fh: | ||||
|             t = DEBUG_ENGINE.from_string(fh.read()) | ||||
|         c = Context(self.get_traceback_data(), use_l10n=False) | ||||
|         return t.render(c) | ||||
|  | ||||
|     def get_traceback_text(self): | ||||
|         """Return plain text version of debug 500 HTTP error page.""" | ||||
|         with self.text_template_path.open(encoding="utf-8") as fh: | ||||
|             t = DEBUG_ENGINE.from_string(fh.read()) | ||||
|         c = Context(self.get_traceback_data(), autoescape=False, use_l10n=False) | ||||
|         return t.render(c) | ||||
|  | ||||
|     def _get_source(self, filename, loader, module_name): | ||||
|         source = None | ||||
|         if hasattr(loader, "get_source"): | ||||
|             try: | ||||
|                 source = loader.get_source(module_name) | ||||
|             except ImportError: | ||||
|                 pass | ||||
|             if source is not None: | ||||
|                 source = source.splitlines() | ||||
|         if source is None: | ||||
|             try: | ||||
|                 with open(filename, "rb") as fp: | ||||
|                     source = fp.read().splitlines() | ||||
|             except OSError: | ||||
|                 pass | ||||
|         return source | ||||
|  | ||||
|     def _get_lines_from_file( | ||||
|         self, filename, lineno, context_lines, loader=None, module_name=None | ||||
|     ): | ||||
|         """ | ||||
|         Return context_lines before and after lineno from file. | ||||
|         Return (pre_context_lineno, pre_context, context_line, post_context). | ||||
|         """ | ||||
|         source = self._get_source(filename, loader, module_name) | ||||
|         if source is None: | ||||
|             return None, [], None, [] | ||||
|  | ||||
|         # If we just read the source from a file, or if the loader did not | ||||
|         # apply tokenize.detect_encoding to decode the source into a | ||||
|         # string, then we should do that ourselves. | ||||
|         if isinstance(source[0], bytes): | ||||
|             encoding = "ascii" | ||||
|             for line in source[:2]: | ||||
|                 # File coding may be specified. Match pattern from PEP-263 | ||||
|                 # (https://www.python.org/dev/peps/pep-0263/) | ||||
|                 match = re.search(rb"coding[:=]\s*([-\w.]+)", line) | ||||
|                 if match: | ||||
|                     encoding = match[1].decode("ascii") | ||||
|                     break | ||||
|             source = [str(sline, encoding, "replace") for sline in source] | ||||
|  | ||||
|         lower_bound = max(0, lineno - context_lines) | ||||
|         upper_bound = lineno + context_lines | ||||
|  | ||||
|         try: | ||||
|             pre_context = source[lower_bound:lineno] | ||||
|             context_line = source[lineno] | ||||
|             post_context = source[lineno + 1 : upper_bound] | ||||
|         except IndexError: | ||||
|             return None, [], None, [] | ||||
|         return lower_bound, pre_context, context_line, post_context | ||||
|  | ||||
|     def _get_explicit_or_implicit_cause(self, exc_value): | ||||
|         explicit = getattr(exc_value, "__cause__", None) | ||||
|         suppress_context = getattr(exc_value, "__suppress_context__", None) | ||||
|         implicit = getattr(exc_value, "__context__", None) | ||||
|         return explicit or (None if suppress_context else implicit) | ||||
|  | ||||
|     def get_traceback_frames(self): | ||||
|         # Get the exception and all its causes | ||||
|         exceptions = [] | ||||
|         exc_value = self.exc_value | ||||
|         while exc_value: | ||||
|             exceptions.append(exc_value) | ||||
|             exc_value = self._get_explicit_or_implicit_cause(exc_value) | ||||
|             if exc_value in exceptions: | ||||
|                 warnings.warn( | ||||
|                     "Cycle in the exception chain detected: exception '%s' " | ||||
|                     "encountered again." % exc_value, | ||||
|                     ExceptionCycleWarning, | ||||
|                 ) | ||||
|                 # Avoid infinite loop if there's a cyclic reference (#29393). | ||||
|                 break | ||||
|  | ||||
|         frames = [] | ||||
|         # No exceptions were supplied to ExceptionReporter | ||||
|         if not exceptions: | ||||
|             return frames | ||||
|  | ||||
|         # In case there's just one exception, take the traceback from self.tb | ||||
|         exc_value = exceptions.pop() | ||||
|         tb = self.tb if not exceptions else exc_value.__traceback__ | ||||
|         while True: | ||||
|             frames.extend(self.get_exception_traceback_frames(exc_value, tb)) | ||||
|             try: | ||||
|                 exc_value = exceptions.pop() | ||||
|             except IndexError: | ||||
|                 break | ||||
|             tb = exc_value.__traceback__ | ||||
|         return frames | ||||
|  | ||||
|     def get_exception_traceback_frames(self, exc_value, tb): | ||||
|         exc_cause = self._get_explicit_or_implicit_cause(exc_value) | ||||
|         exc_cause_explicit = getattr(exc_value, "__cause__", True) | ||||
|         if tb is None: | ||||
|             yield { | ||||
|                 "exc_cause": exc_cause, | ||||
|                 "exc_cause_explicit": exc_cause_explicit, | ||||
|                 "tb": None, | ||||
|                 "type": "user", | ||||
|             } | ||||
|         while tb is not None: | ||||
|             # Support for __traceback_hide__ which is used by a few libraries | ||||
|             # to hide internal frames. | ||||
|             if tb.tb_frame.f_locals.get("__traceback_hide__"): | ||||
|                 tb = tb.tb_next | ||||
|                 continue | ||||
|             filename = tb.tb_frame.f_code.co_filename | ||||
|             function = tb.tb_frame.f_code.co_name | ||||
|             lineno = tb.tb_lineno - 1 | ||||
|             loader = tb.tb_frame.f_globals.get("__loader__") | ||||
|             module_name = tb.tb_frame.f_globals.get("__name__") or "" | ||||
|             ( | ||||
|                 pre_context_lineno, | ||||
|                 pre_context, | ||||
|                 context_line, | ||||
|                 post_context, | ||||
|             ) = self._get_lines_from_file( | ||||
|                 filename, | ||||
|                 lineno, | ||||
|                 7, | ||||
|                 loader, | ||||
|                 module_name, | ||||
|             ) | ||||
|             if pre_context_lineno is None: | ||||
|                 pre_context_lineno = lineno | ||||
|                 pre_context = [] | ||||
|                 context_line = "<source code not available>" | ||||
|                 post_context = [] | ||||
|  | ||||
|             colno = tb_area_colno = "" | ||||
|             if PY311: | ||||
|                 _, _, start_column, end_column = next( | ||||
|                     itertools.islice( | ||||
|                         tb.tb_frame.f_code.co_positions(), tb.tb_lasti // 2, None | ||||
|                     ) | ||||
|                 ) | ||||
|                 if start_column and end_column: | ||||
|                     underline = "^" * (end_column - start_column) | ||||
|                     spaces = " " * (start_column + len(str(lineno + 1)) + 2) | ||||
|                     colno = f"\n{spaces}{underline}" | ||||
|                     tb_area_spaces = " " * ( | ||||
|                         4 | ||||
|                         + start_column | ||||
|                         - (len(context_line) - len(context_line.lstrip())) | ||||
|                     ) | ||||
|                     tb_area_colno = f"\n{tb_area_spaces}{underline}" | ||||
|             yield { | ||||
|                 "exc_cause": exc_cause, | ||||
|                 "exc_cause_explicit": exc_cause_explicit, | ||||
|                 "tb": tb, | ||||
|                 "type": "django" if module_name.startswith("django.") else "user", | ||||
|                 "filename": filename, | ||||
|                 "function": function, | ||||
|                 "lineno": lineno + 1, | ||||
|                 "vars": self.filter.get_traceback_frame_variables( | ||||
|                     self.request, tb.tb_frame | ||||
|                 ), | ||||
|                 "id": id(tb), | ||||
|                 "pre_context": pre_context, | ||||
|                 "context_line": context_line, | ||||
|                 "post_context": post_context, | ||||
|                 "pre_context_lineno": pre_context_lineno + 1, | ||||
|                 "colno": colno, | ||||
|                 "tb_area_colno": tb_area_colno, | ||||
|             } | ||||
|             tb = tb.tb_next | ||||
|  | ||||
|  | ||||
| def technical_404_response(request, exception): | ||||
|     """Create a technical 404 error response. `exception` is the Http404.""" | ||||
|     try: | ||||
|         error_url = exception.args[0]["path"] | ||||
|     except (IndexError, TypeError, KeyError): | ||||
|         error_url = request.path_info[1:]  # Trim leading slash | ||||
|  | ||||
|     try: | ||||
|         tried = exception.args[0]["tried"] | ||||
|     except (IndexError, TypeError, KeyError): | ||||
|         resolved = True | ||||
|         tried = request.resolver_match.tried if request.resolver_match else None | ||||
|     else: | ||||
|         resolved = False | ||||
|         if not tried or (  # empty URLconf | ||||
|             request.path == "/" | ||||
|             and len(tried) == 1 | ||||
|             and len(tried[0]) == 1  # default URLconf | ||||
|             and getattr(tried[0][0], "app_name", "") | ||||
|             == getattr(tried[0][0], "namespace", "") | ||||
|             == "admin" | ||||
|         ): | ||||
|             return default_urlconf(request) | ||||
|  | ||||
|     urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF) | ||||
|     if isinstance(urlconf, types.ModuleType): | ||||
|         urlconf = urlconf.__name__ | ||||
|  | ||||
|     with builtin_template_path("technical_404.html").open(encoding="utf-8") as fh: | ||||
|         t = DEBUG_ENGINE.from_string(fh.read()) | ||||
|     reporter_filter = get_default_exception_reporter_filter() | ||||
|     c = Context( | ||||
|         { | ||||
|             "urlconf": urlconf, | ||||
|             "root_urlconf": settings.ROOT_URLCONF, | ||||
|             "request_path": error_url, | ||||
|             "urlpatterns": tried, | ||||
|             "resolved": resolved, | ||||
|             "reason": str(exception), | ||||
|             "request": request, | ||||
|             "settings": reporter_filter.get_safe_settings(), | ||||
|             "raising_view_name": get_caller(request), | ||||
|         } | ||||
|     ) | ||||
|     return HttpResponseNotFound(t.render(c)) | ||||
|  | ||||
|  | ||||
| def default_urlconf(request): | ||||
|     """Create an empty URLconf 404 error response.""" | ||||
|     with builtin_template_path("default_urlconf.html").open(encoding="utf-8") as fh: | ||||
|         t = DEBUG_ENGINE.from_string(fh.read()) | ||||
|     c = Context( | ||||
|         { | ||||
|             "version": get_docs_version(), | ||||
|         } | ||||
|     ) | ||||
|  | ||||
|     return HttpResponse(t.render(c)) | ||||
| @ -0,0 +1,66 @@ | ||||
| from functools import wraps | ||||
|  | ||||
| from django.middleware.cache import CacheMiddleware | ||||
| from django.utils.cache import add_never_cache_headers, patch_cache_control | ||||
| from django.utils.decorators import decorator_from_middleware_with_args | ||||
|  | ||||
|  | ||||
| def cache_page(timeout, *, cache=None, key_prefix=None): | ||||
|     """ | ||||
|     Decorator for views that tries getting the page from the cache and | ||||
|     populates the cache if the page isn't in the cache yet. | ||||
|  | ||||
|     The cache is keyed by the URL and some data from the headers. | ||||
|     Additionally there is the key prefix that is used to distinguish different | ||||
|     cache areas in a multi-site setup. You could use the | ||||
|     get_current_site().domain, for example, as that is unique across a Django | ||||
|     project. | ||||
|  | ||||
|     Additionally, all headers from the response's Vary header will be taken | ||||
|     into account on caching -- just like the middleware does. | ||||
|     """ | ||||
|     return decorator_from_middleware_with_args(CacheMiddleware)( | ||||
|         page_timeout=timeout, | ||||
|         cache_alias=cache, | ||||
|         key_prefix=key_prefix, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def cache_control(**kwargs): | ||||
|     def _cache_controller(viewfunc): | ||||
|         @wraps(viewfunc) | ||||
|         def _cache_controlled(request, *args, **kw): | ||||
|             # Ensure argument looks like a request. | ||||
|             if not hasattr(request, "META"): | ||||
|                 raise TypeError( | ||||
|                     "cache_control didn't receive an HttpRequest. If you are " | ||||
|                     "decorating a classmethod, be sure to use " | ||||
|                     "@method_decorator." | ||||
|                 ) | ||||
|             response = viewfunc(request, *args, **kw) | ||||
|             patch_cache_control(response, **kwargs) | ||||
|             return response | ||||
|  | ||||
|         return _cache_controlled | ||||
|  | ||||
|     return _cache_controller | ||||
|  | ||||
|  | ||||
| def never_cache(view_func): | ||||
|     """ | ||||
|     Decorator that adds headers to a response so that it will never be cached. | ||||
|     """ | ||||
|  | ||||
|     @wraps(view_func) | ||||
|     def _wrapper_view_func(request, *args, **kwargs): | ||||
|         # Ensure argument looks like a request. | ||||
|         if not hasattr(request, "META"): | ||||
|             raise TypeError( | ||||
|                 "never_cache didn't receive an HttpRequest. If you are " | ||||
|                 "decorating a classmethod, be sure to use @method_decorator." | ||||
|             ) | ||||
|         response = view_func(request, *args, **kwargs) | ||||
|         add_never_cache_headers(response) | ||||
|         return response | ||||
|  | ||||
|     return _wrapper_view_func | ||||
| @ -0,0 +1,62 @@ | ||||
| from functools import wraps | ||||
|  | ||||
|  | ||||
| def xframe_options_deny(view_func): | ||||
|     """ | ||||
|     Modify a view function so its response has the X-Frame-Options HTTP | ||||
|     header set to 'DENY' as long as the response doesn't already have that | ||||
|     header set. Usage: | ||||
|  | ||||
|     @xframe_options_deny | ||||
|     def some_view(request): | ||||
|         ... | ||||
|     """ | ||||
|  | ||||
|     @wraps(view_func) | ||||
|     def wrapper_view(*args, **kwargs): | ||||
|         resp = view_func(*args, **kwargs) | ||||
|         if resp.get("X-Frame-Options") is None: | ||||
|             resp["X-Frame-Options"] = "DENY" | ||||
|         return resp | ||||
|  | ||||
|     return wrapper_view | ||||
|  | ||||
|  | ||||
| def xframe_options_sameorigin(view_func): | ||||
|     """ | ||||
|     Modify a view function so its response has the X-Frame-Options HTTP | ||||
|     header set to 'SAMEORIGIN' as long as the response doesn't already have | ||||
|     that header set. Usage: | ||||
|  | ||||
|     @xframe_options_sameorigin | ||||
|     def some_view(request): | ||||
|         ... | ||||
|     """ | ||||
|  | ||||
|     @wraps(view_func) | ||||
|     def wrapper_view(*args, **kwargs): | ||||
|         resp = view_func(*args, **kwargs) | ||||
|         if resp.get("X-Frame-Options") is None: | ||||
|             resp["X-Frame-Options"] = "SAMEORIGIN" | ||||
|         return resp | ||||
|  | ||||
|     return wrapper_view | ||||
|  | ||||
|  | ||||
| def xframe_options_exempt(view_func): | ||||
|     """ | ||||
|     Modify a view function by setting a response variable that instructs | ||||
|     XFrameOptionsMiddleware to NOT set the X-Frame-Options HTTP header. Usage: | ||||
|  | ||||
|     @xframe_options_exempt | ||||
|     def some_view(request): | ||||
|         ... | ||||
|     """ | ||||
|  | ||||
|     @wraps(view_func) | ||||
|     def wrapper_view(*args, **kwargs): | ||||
|         resp = view_func(*args, **kwargs) | ||||
|         resp.xframe_options_exempt = True | ||||
|         return resp | ||||
|  | ||||
|     return wrapper_view | ||||
| @ -0,0 +1,17 @@ | ||||
| from functools import wraps | ||||
|  | ||||
|  | ||||
| def no_append_slash(view_func): | ||||
|     """ | ||||
|     Mark a view function as excluded from CommonMiddleware's APPEND_SLASH | ||||
|     redirection. | ||||
|     """ | ||||
|  | ||||
|     # view_func.should_append_slash = False would also work, but decorators are | ||||
|     # nicer if they don't have side effects, so return a new function. | ||||
|     @wraps(view_func) | ||||
|     def wrapper_view(*args, **kwargs): | ||||
|         return view_func(*args, **kwargs) | ||||
|  | ||||
|     wrapper_view.should_append_slash = False | ||||
|     return wrapper_view | ||||
| @ -0,0 +1,59 @@ | ||||
| from functools import wraps | ||||
|  | ||||
| from django.middleware.csrf import CsrfViewMiddleware, get_token | ||||
| from django.utils.decorators import decorator_from_middleware | ||||
|  | ||||
| csrf_protect = decorator_from_middleware(CsrfViewMiddleware) | ||||
| csrf_protect.__name__ = "csrf_protect" | ||||
| csrf_protect.__doc__ = """ | ||||
| This decorator adds CSRF protection in exactly the same way as | ||||
| CsrfViewMiddleware, but it can be used on a per view basis.  Using both, or | ||||
| using the decorator multiple times, is harmless and efficient. | ||||
| """ | ||||
|  | ||||
|  | ||||
| class _EnsureCsrfToken(CsrfViewMiddleware): | ||||
|     # Behave like CsrfViewMiddleware but don't reject requests or log warnings. | ||||
|     def _reject(self, request, reason): | ||||
|         return None | ||||
|  | ||||
|  | ||||
| requires_csrf_token = decorator_from_middleware(_EnsureCsrfToken) | ||||
| requires_csrf_token.__name__ = "requires_csrf_token" | ||||
| requires_csrf_token.__doc__ = """ | ||||
| Use this decorator on views that need a correct csrf_token available to | ||||
| RequestContext, but without the CSRF protection that csrf_protect | ||||
| enforces. | ||||
| """ | ||||
|  | ||||
|  | ||||
| class _EnsureCsrfCookie(CsrfViewMiddleware): | ||||
|     def _reject(self, request, reason): | ||||
|         return None | ||||
|  | ||||
|     def process_view(self, request, callback, callback_args, callback_kwargs): | ||||
|         retval = super().process_view(request, callback, callback_args, callback_kwargs) | ||||
|         # Force process_response to send the cookie | ||||
|         get_token(request) | ||||
|         return retval | ||||
|  | ||||
|  | ||||
| ensure_csrf_cookie = decorator_from_middleware(_EnsureCsrfCookie) | ||||
| ensure_csrf_cookie.__name__ = "ensure_csrf_cookie" | ||||
| ensure_csrf_cookie.__doc__ = """ | ||||
| Use this decorator to ensure that a view sets a CSRF cookie, whether or not it | ||||
| uses the csrf_token template tag, or the CsrfViewMiddleware is used. | ||||
| """ | ||||
|  | ||||
|  | ||||
| def csrf_exempt(view_func): | ||||
|     """Mark a view function as being exempt from the CSRF view protection.""" | ||||
|  | ||||
|     # view_func.csrf_exempt = True would also work, but decorators are nicer | ||||
|     # if they don't have side effects, so return a new function. | ||||
|     @wraps(view_func) | ||||
|     def wrapper_view(*args, **kwargs): | ||||
|         return view_func(*args, **kwargs) | ||||
|  | ||||
|     wrapper_view.csrf_exempt = True | ||||
|     return wrapper_view | ||||
| @ -0,0 +1,96 @@ | ||||
| from functools import wraps | ||||
|  | ||||
| from django.http import HttpRequest | ||||
|  | ||||
|  | ||||
| def sensitive_variables(*variables): | ||||
|     """ | ||||
|     Indicate which variables used in the decorated function are sensitive so | ||||
|     that those variables can later be treated in a special way, for example | ||||
|     by hiding them when logging unhandled exceptions. | ||||
|  | ||||
|     Accept two forms: | ||||
|  | ||||
|     * with specified variable names: | ||||
|  | ||||
|         @sensitive_variables('user', 'password', 'credit_card') | ||||
|         def my_function(user): | ||||
|             password = user.pass_word | ||||
|             credit_card = user.credit_card_number | ||||
|             ... | ||||
|  | ||||
|     * without any specified variable names, in which case consider all | ||||
|       variables are sensitive: | ||||
|  | ||||
|         @sensitive_variables() | ||||
|         def my_function() | ||||
|             ... | ||||
|     """ | ||||
|     if len(variables) == 1 and callable(variables[0]): | ||||
|         raise TypeError( | ||||
|             "sensitive_variables() must be called to use it as a decorator, " | ||||
|             "e.g., use @sensitive_variables(), not @sensitive_variables." | ||||
|         ) | ||||
|  | ||||
|     def decorator(func): | ||||
|         @wraps(func) | ||||
|         def sensitive_variables_wrapper(*func_args, **func_kwargs): | ||||
|             if variables: | ||||
|                 sensitive_variables_wrapper.sensitive_variables = variables | ||||
|             else: | ||||
|                 sensitive_variables_wrapper.sensitive_variables = "__ALL__" | ||||
|             return func(*func_args, **func_kwargs) | ||||
|  | ||||
|         return sensitive_variables_wrapper | ||||
|  | ||||
|     return decorator | ||||
|  | ||||
|  | ||||
| def sensitive_post_parameters(*parameters): | ||||
|     """ | ||||
|     Indicate which POST parameters used in the decorated view are sensitive, | ||||
|     so that those parameters can later be treated in a special way, for example | ||||
|     by hiding them when logging unhandled exceptions. | ||||
|  | ||||
|     Accept two forms: | ||||
|  | ||||
|     * with specified parameters: | ||||
|  | ||||
|         @sensitive_post_parameters('password', 'credit_card') | ||||
|         def my_view(request): | ||||
|             pw = request.POST['password'] | ||||
|             cc = request.POST['credit_card'] | ||||
|             ... | ||||
|  | ||||
|     * without any specified parameters, in which case consider all | ||||
|       variables are sensitive: | ||||
|  | ||||
|         @sensitive_post_parameters() | ||||
|         def my_view(request) | ||||
|             ... | ||||
|     """ | ||||
|     if len(parameters) == 1 and callable(parameters[0]): | ||||
|         raise TypeError( | ||||
|             "sensitive_post_parameters() must be called to use it as a " | ||||
|             "decorator, e.g., use @sensitive_post_parameters(), not " | ||||
|             "@sensitive_post_parameters." | ||||
|         ) | ||||
|  | ||||
|     def decorator(view): | ||||
|         @wraps(view) | ||||
|         def sensitive_post_parameters_wrapper(request, *args, **kwargs): | ||||
|             if not isinstance(request, HttpRequest): | ||||
|                 raise TypeError( | ||||
|                     "sensitive_post_parameters didn't receive an HttpRequest " | ||||
|                     "object. If you are decorating a classmethod, make sure " | ||||
|                     "to use @method_decorator." | ||||
|                 ) | ||||
|             if parameters: | ||||
|                 request.sensitive_post_parameters = parameters | ||||
|             else: | ||||
|                 request.sensitive_post_parameters = "__ALL__" | ||||
|             return view(request, *args, **kwargs) | ||||
|  | ||||
|         return sensitive_post_parameters_wrapper | ||||
|  | ||||
|     return decorator | ||||
| @ -0,0 +1,5 @@ | ||||
| from django.middleware.gzip import GZipMiddleware | ||||
| from django.utils.decorators import decorator_from_middleware | ||||
|  | ||||
| gzip_page = decorator_from_middleware(GZipMiddleware) | ||||
| gzip_page.__doc__ = "Decorator for views that gzips pages if the client supports it." | ||||
| @ -0,0 +1,132 @@ | ||||
| """ | ||||
| Decorators for views based on HTTP headers. | ||||
| """ | ||||
| import datetime | ||||
| from functools import wraps | ||||
|  | ||||
| from django.http import HttpResponseNotAllowed | ||||
| from django.middleware.http import ConditionalGetMiddleware | ||||
| from django.utils import timezone | ||||
| from django.utils.cache import get_conditional_response | ||||
| from django.utils.decorators import decorator_from_middleware | ||||
| from django.utils.http import http_date, quote_etag | ||||
| from django.utils.log import log_response | ||||
|  | ||||
| conditional_page = decorator_from_middleware(ConditionalGetMiddleware) | ||||
|  | ||||
|  | ||||
| def require_http_methods(request_method_list): | ||||
|     """ | ||||
|     Decorator to make a view only accept particular request methods.  Usage:: | ||||
|  | ||||
|         @require_http_methods(["GET", "POST"]) | ||||
|         def my_view(request): | ||||
|             # I can assume now that only GET or POST requests make it this far | ||||
|             # ... | ||||
|  | ||||
|     Note that request methods should be in uppercase. | ||||
|     """ | ||||
|  | ||||
|     def decorator(func): | ||||
|         @wraps(func) | ||||
|         def inner(request, *args, **kwargs): | ||||
|             if request.method not in request_method_list: | ||||
|                 response = HttpResponseNotAllowed(request_method_list) | ||||
|                 log_response( | ||||
|                     "Method Not Allowed (%s): %s", | ||||
|                     request.method, | ||||
|                     request.path, | ||||
|                     response=response, | ||||
|                     request=request, | ||||
|                 ) | ||||
|                 return response | ||||
|             return func(request, *args, **kwargs) | ||||
|  | ||||
|         return inner | ||||
|  | ||||
|     return decorator | ||||
|  | ||||
|  | ||||
| require_GET = require_http_methods(["GET"]) | ||||
| require_GET.__doc__ = "Decorator to require that a view only accepts the GET method." | ||||
|  | ||||
| require_POST = require_http_methods(["POST"]) | ||||
| require_POST.__doc__ = "Decorator to require that a view only accepts the POST method." | ||||
|  | ||||
| require_safe = require_http_methods(["GET", "HEAD"]) | ||||
| require_safe.__doc__ = ( | ||||
|     "Decorator to require that a view only accepts safe methods: GET and HEAD." | ||||
| ) | ||||
|  | ||||
|  | ||||
| def condition(etag_func=None, last_modified_func=None): | ||||
|     """ | ||||
|     Decorator to support conditional retrieval (or change) for a view | ||||
|     function. | ||||
|  | ||||
|     The parameters are callables to compute the ETag and last modified time for | ||||
|     the requested resource, respectively. The callables are passed the same | ||||
|     parameters as the view itself. The ETag function should return a string (or | ||||
|     None if the resource doesn't exist), while the last_modified function | ||||
|     should return a datetime object (or None if the resource doesn't exist). | ||||
|  | ||||
|     The ETag function should return a complete ETag, including quotes (e.g. | ||||
|     '"etag"'), since that's the only way to distinguish between weak and strong | ||||
|     ETags. If an unquoted ETag is returned (e.g. 'etag'), it will be converted | ||||
|     to a strong ETag by adding quotes. | ||||
|  | ||||
|     This decorator will either pass control to the wrapped view function or | ||||
|     return an HTTP 304 response (unmodified) or 412 response (precondition | ||||
|     failed), depending upon the request method. In either case, the decorator | ||||
|     will add the generated ETag and Last-Modified headers to the response if | ||||
|     the headers aren't already set and if the request's method is safe. | ||||
|     """ | ||||
|  | ||||
|     def decorator(func): | ||||
|         @wraps(func) | ||||
|         def inner(request, *args, **kwargs): | ||||
|             # Compute values (if any) for the requested resource. | ||||
|             def get_last_modified(): | ||||
|                 if last_modified_func: | ||||
|                     dt = last_modified_func(request, *args, **kwargs) | ||||
|                     if dt: | ||||
|                         if not timezone.is_aware(dt): | ||||
|                             dt = timezone.make_aware(dt, datetime.timezone.utc) | ||||
|                         return int(dt.timestamp()) | ||||
|  | ||||
|             # The value from etag_func() could be quoted or unquoted. | ||||
|             res_etag = etag_func(request, *args, **kwargs) if etag_func else None | ||||
|             res_etag = quote_etag(res_etag) if res_etag is not None else None | ||||
|             res_last_modified = get_last_modified() | ||||
|  | ||||
|             response = get_conditional_response( | ||||
|                 request, | ||||
|                 etag=res_etag, | ||||
|                 last_modified=res_last_modified, | ||||
|             ) | ||||
|  | ||||
|             if response is None: | ||||
|                 response = func(request, *args, **kwargs) | ||||
|  | ||||
|             # Set relevant headers on the response if they don't already exist | ||||
|             # and if the request method is safe. | ||||
|             if request.method in ("GET", "HEAD"): | ||||
|                 if res_last_modified and not response.has_header("Last-Modified"): | ||||
|                     response.headers["Last-Modified"] = http_date(res_last_modified) | ||||
|                 if res_etag: | ||||
|                     response.headers.setdefault("ETag", res_etag) | ||||
|  | ||||
|             return response | ||||
|  | ||||
|         return inner | ||||
|  | ||||
|     return decorator | ||||
|  | ||||
|  | ||||
| # Shortcut decorators for common cases based on ETag or Last-Modified only | ||||
| def etag(etag_func): | ||||
|     return condition(etag_func=etag_func) | ||||
|  | ||||
|  | ||||
| def last_modified(last_modified_func): | ||||
|     return condition(last_modified_func=last_modified_func) | ||||
| @ -0,0 +1,46 @@ | ||||
| from functools import wraps | ||||
|  | ||||
| from django.utils.cache import patch_vary_headers | ||||
|  | ||||
|  | ||||
| def vary_on_headers(*headers): | ||||
|     """ | ||||
|     A view decorator that adds the specified headers to the Vary header of the | ||||
|     response. Usage: | ||||
|  | ||||
|        @vary_on_headers('Cookie', 'Accept-language') | ||||
|        def index(request): | ||||
|            ... | ||||
|  | ||||
|     Note that the header names are not case-sensitive. | ||||
|     """ | ||||
|  | ||||
|     def decorator(func): | ||||
|         @wraps(func) | ||||
|         def inner_func(*args, **kwargs): | ||||
|             response = func(*args, **kwargs) | ||||
|             patch_vary_headers(response, headers) | ||||
|             return response | ||||
|  | ||||
|         return inner_func | ||||
|  | ||||
|     return decorator | ||||
|  | ||||
|  | ||||
| def vary_on_cookie(func): | ||||
|     """ | ||||
|     A view decorator that adds "Cookie" to the Vary header of a response. This | ||||
|     indicates that a page's contents depends on cookies. Usage: | ||||
|  | ||||
|         @vary_on_cookie | ||||
|         def index(request): | ||||
|             ... | ||||
|     """ | ||||
|  | ||||
|     @wraps(func) | ||||
|     def inner_func(*args, **kwargs): | ||||
|         response = func(*args, **kwargs) | ||||
|         patch_vary_headers(response, ("Cookie",)) | ||||
|         return response | ||||
|  | ||||
|     return inner_func | ||||
							
								
								
									
										149
									
								
								srcs/.venv/lib/python3.11/site-packages/django/views/defaults.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								srcs/.venv/lib/python3.11/site-packages/django/views/defaults.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,149 @@ | ||||
| from urllib.parse import quote | ||||
|  | ||||
| from django.http import ( | ||||
|     HttpResponseBadRequest, | ||||
|     HttpResponseForbidden, | ||||
|     HttpResponseNotFound, | ||||
|     HttpResponseServerError, | ||||
| ) | ||||
| from django.template import Context, Engine, TemplateDoesNotExist, loader | ||||
| from django.views.decorators.csrf import requires_csrf_token | ||||
|  | ||||
| ERROR_404_TEMPLATE_NAME = "404.html" | ||||
| ERROR_403_TEMPLATE_NAME = "403.html" | ||||
| ERROR_400_TEMPLATE_NAME = "400.html" | ||||
| ERROR_500_TEMPLATE_NAME = "500.html" | ||||
| ERROR_PAGE_TEMPLATE = """ | ||||
| <!doctype html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|   <title>%(title)s</title> | ||||
| </head> | ||||
| <body> | ||||
|   <h1>%(title)s</h1><p>%(details)s</p> | ||||
| </body> | ||||
| </html> | ||||
| """ | ||||
|  | ||||
|  | ||||
| # These views can be called when CsrfViewMiddleware.process_view() not run, | ||||
| # therefore need @requires_csrf_token in case the template needs | ||||
| # {% csrf_token %}. | ||||
|  | ||||
|  | ||||
| @requires_csrf_token | ||||
| def page_not_found(request, exception, template_name=ERROR_404_TEMPLATE_NAME): | ||||
|     """ | ||||
|     Default 404 handler. | ||||
|  | ||||
|     Templates: :template:`404.html` | ||||
|     Context: | ||||
|         request_path | ||||
|             The path of the requested URL (e.g., '/app/pages/bad_page/'). It's | ||||
|             quoted to prevent a content injection attack. | ||||
|         exception | ||||
|             The message from the exception which triggered the 404 (if one was | ||||
|             supplied), or the exception class name | ||||
|     """ | ||||
|     exception_repr = exception.__class__.__name__ | ||||
|     # Try to get an "interesting" exception message, if any (and not the ugly | ||||
|     # Resolver404 dictionary) | ||||
|     try: | ||||
|         message = exception.args[0] | ||||
|     except (AttributeError, IndexError): | ||||
|         pass | ||||
|     else: | ||||
|         if isinstance(message, str): | ||||
|             exception_repr = message | ||||
|     context = { | ||||
|         "request_path": quote(request.path), | ||||
|         "exception": exception_repr, | ||||
|     } | ||||
|     try: | ||||
|         template = loader.get_template(template_name) | ||||
|         body = template.render(context, request) | ||||
|     except TemplateDoesNotExist: | ||||
|         if template_name != ERROR_404_TEMPLATE_NAME: | ||||
|             # Reraise if it's a missing custom template. | ||||
|             raise | ||||
|         # Render template (even though there are no substitutions) to allow | ||||
|         # inspecting the context in tests. | ||||
|         template = Engine().from_string( | ||||
|             ERROR_PAGE_TEMPLATE | ||||
|             % { | ||||
|                 "title": "Not Found", | ||||
|                 "details": "The requested resource was not found on this server.", | ||||
|             }, | ||||
|         ) | ||||
|         body = template.render(Context(context)) | ||||
|     return HttpResponseNotFound(body) | ||||
|  | ||||
|  | ||||
| @requires_csrf_token | ||||
| def server_error(request, template_name=ERROR_500_TEMPLATE_NAME): | ||||
|     """ | ||||
|     500 error handler. | ||||
|  | ||||
|     Templates: :template:`500.html` | ||||
|     Context: None | ||||
|     """ | ||||
|     try: | ||||
|         template = loader.get_template(template_name) | ||||
|     except TemplateDoesNotExist: | ||||
|         if template_name != ERROR_500_TEMPLATE_NAME: | ||||
|             # Reraise if it's a missing custom template. | ||||
|             raise | ||||
|         return HttpResponseServerError( | ||||
|             ERROR_PAGE_TEMPLATE % {"title": "Server Error (500)", "details": ""}, | ||||
|         ) | ||||
|     return HttpResponseServerError(template.render()) | ||||
|  | ||||
|  | ||||
| @requires_csrf_token | ||||
| def bad_request(request, exception, template_name=ERROR_400_TEMPLATE_NAME): | ||||
|     """ | ||||
|     400 error handler. | ||||
|  | ||||
|     Templates: :template:`400.html` | ||||
|     Context: None | ||||
|     """ | ||||
|     try: | ||||
|         template = loader.get_template(template_name) | ||||
|     except TemplateDoesNotExist: | ||||
|         if template_name != ERROR_400_TEMPLATE_NAME: | ||||
|             # Reraise if it's a missing custom template. | ||||
|             raise | ||||
|         return HttpResponseBadRequest( | ||||
|             ERROR_PAGE_TEMPLATE % {"title": "Bad Request (400)", "details": ""}, | ||||
|         ) | ||||
|     # No exception content is passed to the template, to not disclose any | ||||
|     # sensitive information. | ||||
|     return HttpResponseBadRequest(template.render()) | ||||
|  | ||||
|  | ||||
| @requires_csrf_token | ||||
| def permission_denied(request, exception, template_name=ERROR_403_TEMPLATE_NAME): | ||||
|     """ | ||||
|     Permission denied (403) handler. | ||||
|  | ||||
|     Templates: :template:`403.html` | ||||
|     Context: | ||||
|         exception | ||||
|             The message from the exception which triggered the 403 (if one was | ||||
|             supplied). | ||||
|  | ||||
|     If the template does not exist, an Http403 response containing the text | ||||
|     "403 Forbidden" (as per RFC 9110 Section 15.5.4) will be returned. | ||||
|     """ | ||||
|     try: | ||||
|         template = loader.get_template(template_name) | ||||
|     except TemplateDoesNotExist: | ||||
|         if template_name != ERROR_403_TEMPLATE_NAME: | ||||
|             # Reraise if it's a missing custom template. | ||||
|             raise | ||||
|         return HttpResponseForbidden( | ||||
|             ERROR_PAGE_TEMPLATE % {"title": "403 Forbidden", "details": ""}, | ||||
|         ) | ||||
|     return HttpResponseForbidden( | ||||
|         template.render(request=request, context={"exception": str(exception)}) | ||||
|     ) | ||||
| @ -0,0 +1,39 @@ | ||||
| from django.views.generic.base import RedirectView, TemplateView, View | ||||
| from django.views.generic.dates import ( | ||||
|     ArchiveIndexView, | ||||
|     DateDetailView, | ||||
|     DayArchiveView, | ||||
|     MonthArchiveView, | ||||
|     TodayArchiveView, | ||||
|     WeekArchiveView, | ||||
|     YearArchiveView, | ||||
| ) | ||||
| from django.views.generic.detail import DetailView | ||||
| from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView | ||||
| from django.views.generic.list import ListView | ||||
|  | ||||
| __all__ = [ | ||||
|     "View", | ||||
|     "TemplateView", | ||||
|     "RedirectView", | ||||
|     "ArchiveIndexView", | ||||
|     "YearArchiveView", | ||||
|     "MonthArchiveView", | ||||
|     "WeekArchiveView", | ||||
|     "DayArchiveView", | ||||
|     "TodayArchiveView", | ||||
|     "DateDetailView", | ||||
|     "DetailView", | ||||
|     "FormView", | ||||
|     "CreateView", | ||||
|     "UpdateView", | ||||
|     "DeleteView", | ||||
|     "ListView", | ||||
|     "GenericViewError", | ||||
| ] | ||||
|  | ||||
|  | ||||
| class GenericViewError(Exception): | ||||
|     """A problem in a generic view.""" | ||||
|  | ||||
|     pass | ||||
| @ -0,0 +1,285 @@ | ||||
| import logging | ||||
|  | ||||
| from asgiref.sync import iscoroutinefunction, markcoroutinefunction | ||||
|  | ||||
| from django.core.exceptions import ImproperlyConfigured | ||||
| from django.http import ( | ||||
|     HttpResponse, | ||||
|     HttpResponseGone, | ||||
|     HttpResponseNotAllowed, | ||||
|     HttpResponsePermanentRedirect, | ||||
|     HttpResponseRedirect, | ||||
| ) | ||||
| from django.template.response import TemplateResponse | ||||
| from django.urls import reverse | ||||
| from django.utils.decorators import classonlymethod | ||||
| from django.utils.functional import classproperty | ||||
|  | ||||
| logger = logging.getLogger("django.request") | ||||
|  | ||||
|  | ||||
| class ContextMixin: | ||||
|     """ | ||||
|     A default context mixin that passes the keyword arguments received by | ||||
|     get_context_data() as the template context. | ||||
|     """ | ||||
|  | ||||
|     extra_context = None | ||||
|  | ||||
|     def get_context_data(self, **kwargs): | ||||
|         kwargs.setdefault("view", self) | ||||
|         if self.extra_context is not None: | ||||
|             kwargs.update(self.extra_context) | ||||
|         return kwargs | ||||
|  | ||||
|  | ||||
| class View: | ||||
|     """ | ||||
|     Intentionally simple parent class for all views. Only implements | ||||
|     dispatch-by-method and simple sanity checking. | ||||
|     """ | ||||
|  | ||||
|     http_method_names = [ | ||||
|         "get", | ||||
|         "post", | ||||
|         "put", | ||||
|         "patch", | ||||
|         "delete", | ||||
|         "head", | ||||
|         "options", | ||||
|         "trace", | ||||
|     ] | ||||
|  | ||||
|     def __init__(self, **kwargs): | ||||
|         """ | ||||
|         Constructor. Called in the URLconf; can contain helpful extra | ||||
|         keyword arguments, and other things. | ||||
|         """ | ||||
|         # Go through keyword arguments, and either save their values to our | ||||
|         # instance, or raise an error. | ||||
|         for key, value in kwargs.items(): | ||||
|             setattr(self, key, value) | ||||
|  | ||||
|     @classproperty | ||||
|     def view_is_async(cls): | ||||
|         handlers = [ | ||||
|             getattr(cls, method) | ||||
|             for method in cls.http_method_names | ||||
|             if (method != "options" and hasattr(cls, method)) | ||||
|         ] | ||||
|         if not handlers: | ||||
|             return False | ||||
|         is_async = iscoroutinefunction(handlers[0]) | ||||
|         if not all(iscoroutinefunction(h) == is_async for h in handlers[1:]): | ||||
|             raise ImproperlyConfigured( | ||||
|                 f"{cls.__qualname__} HTTP handlers must either be all sync or all " | ||||
|                 "async." | ||||
|             ) | ||||
|         return is_async | ||||
|  | ||||
|     @classonlymethod | ||||
|     def as_view(cls, **initkwargs): | ||||
|         """Main entry point for a request-response process.""" | ||||
|         for key in initkwargs: | ||||
|             if key in cls.http_method_names: | ||||
|                 raise TypeError( | ||||
|                     "The method name %s is not accepted as a keyword argument " | ||||
|                     "to %s()." % (key, cls.__name__) | ||||
|                 ) | ||||
|             if not hasattr(cls, key): | ||||
|                 raise TypeError( | ||||
|                     "%s() received an invalid keyword %r. as_view " | ||||
|                     "only accepts arguments that are already " | ||||
|                     "attributes of the class." % (cls.__name__, key) | ||||
|                 ) | ||||
|  | ||||
|         def view(request, *args, **kwargs): | ||||
|             self = cls(**initkwargs) | ||||
|             self.setup(request, *args, **kwargs) | ||||
|             if not hasattr(self, "request"): | ||||
|                 raise AttributeError( | ||||
|                     "%s instance has no 'request' attribute. Did you override " | ||||
|                     "setup() and forget to call super()?" % cls.__name__ | ||||
|                 ) | ||||
|             return self.dispatch(request, *args, **kwargs) | ||||
|  | ||||
|         view.view_class = cls | ||||
|         view.view_initkwargs = initkwargs | ||||
|  | ||||
|         # __name__ and __qualname__ are intentionally left unchanged as | ||||
|         # view_class should be used to robustly determine the name of the view | ||||
|         # instead. | ||||
|         view.__doc__ = cls.__doc__ | ||||
|         view.__module__ = cls.__module__ | ||||
|         view.__annotations__ = cls.dispatch.__annotations__ | ||||
|         # Copy possible attributes set by decorators, e.g. @csrf_exempt, from | ||||
|         # the dispatch method. | ||||
|         view.__dict__.update(cls.dispatch.__dict__) | ||||
|  | ||||
|         # Mark the callback if the view class is async. | ||||
|         if cls.view_is_async: | ||||
|             markcoroutinefunction(view) | ||||
|  | ||||
|         return view | ||||
|  | ||||
|     def setup(self, request, *args, **kwargs): | ||||
|         """Initialize attributes shared by all view methods.""" | ||||
|         if hasattr(self, "get") and not hasattr(self, "head"): | ||||
|             self.head = self.get | ||||
|         self.request = request | ||||
|         self.args = args | ||||
|         self.kwargs = kwargs | ||||
|  | ||||
|     def dispatch(self, request, *args, **kwargs): | ||||
|         # Try to dispatch to the right method; if a method doesn't exist, | ||||
|         # defer to the error handler. Also defer to the error handler if the | ||||
|         # request method isn't on the approved list. | ||||
|         if request.method.lower() in self.http_method_names: | ||||
|             handler = getattr( | ||||
|                 self, request.method.lower(), self.http_method_not_allowed | ||||
|             ) | ||||
|         else: | ||||
|             handler = self.http_method_not_allowed | ||||
|         return handler(request, *args, **kwargs) | ||||
|  | ||||
|     def http_method_not_allowed(self, request, *args, **kwargs): | ||||
|         logger.warning( | ||||
|             "Method Not Allowed (%s): %s", | ||||
|             request.method, | ||||
|             request.path, | ||||
|             extra={"status_code": 405, "request": request}, | ||||
|         ) | ||||
|         response = HttpResponseNotAllowed(self._allowed_methods()) | ||||
|  | ||||
|         if self.view_is_async: | ||||
|  | ||||
|             async def func(): | ||||
|                 return response | ||||
|  | ||||
|             return func() | ||||
|         else: | ||||
|             return response | ||||
|  | ||||
|     def options(self, request, *args, **kwargs): | ||||
|         """Handle responding to requests for the OPTIONS HTTP verb.""" | ||||
|         response = HttpResponse() | ||||
|         response.headers["Allow"] = ", ".join(self._allowed_methods()) | ||||
|         response.headers["Content-Length"] = "0" | ||||
|  | ||||
|         if self.view_is_async: | ||||
|  | ||||
|             async def func(): | ||||
|                 return response | ||||
|  | ||||
|             return func() | ||||
|         else: | ||||
|             return response | ||||
|  | ||||
|     def _allowed_methods(self): | ||||
|         return [m.upper() for m in self.http_method_names if hasattr(self, m)] | ||||
|  | ||||
|  | ||||
| class TemplateResponseMixin: | ||||
|     """A mixin that can be used to render a template.""" | ||||
|  | ||||
|     template_name = None | ||||
|     template_engine = None | ||||
|     response_class = TemplateResponse | ||||
|     content_type = None | ||||
|  | ||||
|     def render_to_response(self, context, **response_kwargs): | ||||
|         """ | ||||
|         Return a response, using the `response_class` for this view, with a | ||||
|         template rendered with the given context. | ||||
|  | ||||
|         Pass response_kwargs to the constructor of the response class. | ||||
|         """ | ||||
|         response_kwargs.setdefault("content_type", self.content_type) | ||||
|         return self.response_class( | ||||
|             request=self.request, | ||||
|             template=self.get_template_names(), | ||||
|             context=context, | ||||
|             using=self.template_engine, | ||||
|             **response_kwargs, | ||||
|         ) | ||||
|  | ||||
|     def get_template_names(self): | ||||
|         """ | ||||
|         Return a list of template names to be used for the request. Must return | ||||
|         a list. May not be called if render_to_response() is overridden. | ||||
|         """ | ||||
|         if self.template_name is None: | ||||
|             raise ImproperlyConfigured( | ||||
|                 "TemplateResponseMixin requires either a definition of " | ||||
|                 "'template_name' or an implementation of 'get_template_names()'" | ||||
|             ) | ||||
|         else: | ||||
|             return [self.template_name] | ||||
|  | ||||
|  | ||||
| class TemplateView(TemplateResponseMixin, ContextMixin, View): | ||||
|     """ | ||||
|     Render a template. Pass keyword arguments from the URLconf to the context. | ||||
|     """ | ||||
|  | ||||
|     def get(self, request, *args, **kwargs): | ||||
|         context = self.get_context_data(**kwargs) | ||||
|         return self.render_to_response(context) | ||||
|  | ||||
|  | ||||
| class RedirectView(View): | ||||
|     """Provide a redirect on any GET request.""" | ||||
|  | ||||
|     permanent = False | ||||
|     url = None | ||||
|     pattern_name = None | ||||
|     query_string = False | ||||
|  | ||||
|     def get_redirect_url(self, *args, **kwargs): | ||||
|         """ | ||||
|         Return the URL redirect to. Keyword arguments from the URL pattern | ||||
|         match generating the redirect request are provided as kwargs to this | ||||
|         method. | ||||
|         """ | ||||
|         if self.url: | ||||
|             url = self.url % kwargs | ||||
|         elif self.pattern_name: | ||||
|             url = reverse(self.pattern_name, args=args, kwargs=kwargs) | ||||
|         else: | ||||
|             return None | ||||
|  | ||||
|         args = self.request.META.get("QUERY_STRING", "") | ||||
|         if args and self.query_string: | ||||
|             url = "%s?%s" % (url, args) | ||||
|         return url | ||||
|  | ||||
|     def get(self, request, *args, **kwargs): | ||||
|         url = self.get_redirect_url(*args, **kwargs) | ||||
|         if url: | ||||
|             if self.permanent: | ||||
|                 return HttpResponsePermanentRedirect(url) | ||||
|             else: | ||||
|                 return HttpResponseRedirect(url) | ||||
|         else: | ||||
|             logger.warning( | ||||
|                 "Gone: %s", request.path, extra={"status_code": 410, "request": request} | ||||
|             ) | ||||
|             return HttpResponseGone() | ||||
|  | ||||
|     def head(self, request, *args, **kwargs): | ||||
|         return self.get(request, *args, **kwargs) | ||||
|  | ||||
|     def post(self, request, *args, **kwargs): | ||||
|         return self.get(request, *args, **kwargs) | ||||
|  | ||||
|     def options(self, request, *args, **kwargs): | ||||
|         return self.get(request, *args, **kwargs) | ||||
|  | ||||
|     def delete(self, request, *args, **kwargs): | ||||
|         return self.get(request, *args, **kwargs) | ||||
|  | ||||
|     def put(self, request, *args, **kwargs): | ||||
|         return self.get(request, *args, **kwargs) | ||||
|  | ||||
|     def patch(self, request, *args, **kwargs): | ||||
|         return self.get(request, *args, **kwargs) | ||||
| @ -0,0 +1,795 @@ | ||||
| import datetime | ||||
|  | ||||
| from django.conf import settings | ||||
| from django.core.exceptions import ImproperlyConfigured | ||||
| from django.db import models | ||||
| from django.http import Http404 | ||||
| from django.utils import timezone | ||||
| from django.utils.functional import cached_property | ||||
| from django.utils.translation import gettext as _ | ||||
| from django.views.generic.base import View | ||||
| from django.views.generic.detail import ( | ||||
|     BaseDetailView, | ||||
|     SingleObjectTemplateResponseMixin, | ||||
| ) | ||||
| from django.views.generic.list import ( | ||||
|     MultipleObjectMixin, | ||||
|     MultipleObjectTemplateResponseMixin, | ||||
| ) | ||||
|  | ||||
|  | ||||
| class YearMixin: | ||||
|     """Mixin for views manipulating year-based data.""" | ||||
|  | ||||
|     year_format = "%Y" | ||||
|     year = None | ||||
|  | ||||
|     def get_year_format(self): | ||||
|         """ | ||||
|         Get a year format string in strptime syntax to be used to parse the | ||||
|         year from url variables. | ||||
|         """ | ||||
|         return self.year_format | ||||
|  | ||||
|     def get_year(self): | ||||
|         """Return the year for which this view should display data.""" | ||||
|         year = self.year | ||||
|         if year is None: | ||||
|             try: | ||||
|                 year = self.kwargs["year"] | ||||
|             except KeyError: | ||||
|                 try: | ||||
|                     year = self.request.GET["year"] | ||||
|                 except KeyError: | ||||
|                     raise Http404(_("No year specified")) | ||||
|         return year | ||||
|  | ||||
|     def get_next_year(self, date): | ||||
|         """Get the next valid year.""" | ||||
|         return _get_next_prev(self, date, is_previous=False, period="year") | ||||
|  | ||||
|     def get_previous_year(self, date): | ||||
|         """Get the previous valid year.""" | ||||
|         return _get_next_prev(self, date, is_previous=True, period="year") | ||||
|  | ||||
|     def _get_next_year(self, date): | ||||
|         """ | ||||
|         Return the start date of the next interval. | ||||
|  | ||||
|         The interval is defined by start date <= item date < next start date. | ||||
|         """ | ||||
|         try: | ||||
|             return date.replace(year=date.year + 1, month=1, day=1) | ||||
|         except ValueError: | ||||
|             raise Http404(_("Date out of range")) | ||||
|  | ||||
|     def _get_current_year(self, date): | ||||
|         """Return the start date of the current interval.""" | ||||
|         return date.replace(month=1, day=1) | ||||
|  | ||||
|  | ||||
| class MonthMixin: | ||||
|     """Mixin for views manipulating month-based data.""" | ||||
|  | ||||
|     month_format = "%b" | ||||
|     month = None | ||||
|  | ||||
|     def get_month_format(self): | ||||
|         """ | ||||
|         Get a month format string in strptime syntax to be used to parse the | ||||
|         month from url variables. | ||||
|         """ | ||||
|         return self.month_format | ||||
|  | ||||
|     def get_month(self): | ||||
|         """Return the month for which this view should display data.""" | ||||
|         month = self.month | ||||
|         if month is None: | ||||
|             try: | ||||
|                 month = self.kwargs["month"] | ||||
|             except KeyError: | ||||
|                 try: | ||||
|                     month = self.request.GET["month"] | ||||
|                 except KeyError: | ||||
|                     raise Http404(_("No month specified")) | ||||
|         return month | ||||
|  | ||||
|     def get_next_month(self, date): | ||||
|         """Get the next valid month.""" | ||||
|         return _get_next_prev(self, date, is_previous=False, period="month") | ||||
|  | ||||
|     def get_previous_month(self, date): | ||||
|         """Get the previous valid month.""" | ||||
|         return _get_next_prev(self, date, is_previous=True, period="month") | ||||
|  | ||||
|     def _get_next_month(self, date): | ||||
|         """ | ||||
|         Return the start date of the next interval. | ||||
|  | ||||
|         The interval is defined by start date <= item date < next start date. | ||||
|         """ | ||||
|         if date.month == 12: | ||||
|             try: | ||||
|                 return date.replace(year=date.year + 1, month=1, day=1) | ||||
|             except ValueError: | ||||
|                 raise Http404(_("Date out of range")) | ||||
|         else: | ||||
|             return date.replace(month=date.month + 1, day=1) | ||||
|  | ||||
|     def _get_current_month(self, date): | ||||
|         """Return the start date of the previous interval.""" | ||||
|         return date.replace(day=1) | ||||
|  | ||||
|  | ||||
| class DayMixin: | ||||
|     """Mixin for views manipulating day-based data.""" | ||||
|  | ||||
|     day_format = "%d" | ||||
|     day = None | ||||
|  | ||||
|     def get_day_format(self): | ||||
|         """ | ||||
|         Get a day format string in strptime syntax to be used to parse the day | ||||
|         from url variables. | ||||
|         """ | ||||
|         return self.day_format | ||||
|  | ||||
|     def get_day(self): | ||||
|         """Return the day for which this view should display data.""" | ||||
|         day = self.day | ||||
|         if day is None: | ||||
|             try: | ||||
|                 day = self.kwargs["day"] | ||||
|             except KeyError: | ||||
|                 try: | ||||
|                     day = self.request.GET["day"] | ||||
|                 except KeyError: | ||||
|                     raise Http404(_("No day specified")) | ||||
|         return day | ||||
|  | ||||
|     def get_next_day(self, date): | ||||
|         """Get the next valid day.""" | ||||
|         return _get_next_prev(self, date, is_previous=False, period="day") | ||||
|  | ||||
|     def get_previous_day(self, date): | ||||
|         """Get the previous valid day.""" | ||||
|         return _get_next_prev(self, date, is_previous=True, period="day") | ||||
|  | ||||
|     def _get_next_day(self, date): | ||||
|         """ | ||||
|         Return the start date of the next interval. | ||||
|  | ||||
|         The interval is defined by start date <= item date < next start date. | ||||
|         """ | ||||
|         return date + datetime.timedelta(days=1) | ||||
|  | ||||
|     def _get_current_day(self, date): | ||||
|         """Return the start date of the current interval.""" | ||||
|         return date | ||||
|  | ||||
|  | ||||
| class WeekMixin: | ||||
|     """Mixin for views manipulating week-based data.""" | ||||
|  | ||||
|     week_format = "%U" | ||||
|     week = None | ||||
|  | ||||
|     def get_week_format(self): | ||||
|         """ | ||||
|         Get a week format string in strptime syntax to be used to parse the | ||||
|         week from url variables. | ||||
|         """ | ||||
|         return self.week_format | ||||
|  | ||||
|     def get_week(self): | ||||
|         """Return the week for which this view should display data.""" | ||||
|         week = self.week | ||||
|         if week is None: | ||||
|             try: | ||||
|                 week = self.kwargs["week"] | ||||
|             except KeyError: | ||||
|                 try: | ||||
|                     week = self.request.GET["week"] | ||||
|                 except KeyError: | ||||
|                     raise Http404(_("No week specified")) | ||||
|         return week | ||||
|  | ||||
|     def get_next_week(self, date): | ||||
|         """Get the next valid week.""" | ||||
|         return _get_next_prev(self, date, is_previous=False, period="week") | ||||
|  | ||||
|     def get_previous_week(self, date): | ||||
|         """Get the previous valid week.""" | ||||
|         return _get_next_prev(self, date, is_previous=True, period="week") | ||||
|  | ||||
|     def _get_next_week(self, date): | ||||
|         """ | ||||
|         Return the start date of the next interval. | ||||
|  | ||||
|         The interval is defined by start date <= item date < next start date. | ||||
|         """ | ||||
|         try: | ||||
|             return date + datetime.timedelta(days=7 - self._get_weekday(date)) | ||||
|         except OverflowError: | ||||
|             raise Http404(_("Date out of range")) | ||||
|  | ||||
|     def _get_current_week(self, date): | ||||
|         """Return the start date of the current interval.""" | ||||
|         return date - datetime.timedelta(self._get_weekday(date)) | ||||
|  | ||||
|     def _get_weekday(self, date): | ||||
|         """ | ||||
|         Return the weekday for a given date. | ||||
|  | ||||
|         The first day according to the week format is 0 and the last day is 6. | ||||
|         """ | ||||
|         week_format = self.get_week_format() | ||||
|         if week_format in {"%W", "%V"}:  # week starts on Monday | ||||
|             return date.weekday() | ||||
|         elif week_format == "%U":  # week starts on Sunday | ||||
|             return (date.weekday() + 1) % 7 | ||||
|         else: | ||||
|             raise ValueError("unknown week format: %s" % week_format) | ||||
|  | ||||
|  | ||||
| class DateMixin: | ||||
|     """Mixin class for views manipulating date-based data.""" | ||||
|  | ||||
|     date_field = None | ||||
|     allow_future = False | ||||
|  | ||||
|     def get_date_field(self): | ||||
|         """Get the name of the date field to be used to filter by.""" | ||||
|         if self.date_field is None: | ||||
|             raise ImproperlyConfigured( | ||||
|                 "%s.date_field is required." % self.__class__.__name__ | ||||
|             ) | ||||
|         return self.date_field | ||||
|  | ||||
|     def get_allow_future(self): | ||||
|         """ | ||||
|         Return `True` if the view should be allowed to display objects from | ||||
|         the future. | ||||
|         """ | ||||
|         return self.allow_future | ||||
|  | ||||
|     # Note: the following three methods only work in subclasses that also | ||||
|     # inherit SingleObjectMixin or MultipleObjectMixin. | ||||
|  | ||||
|     @cached_property | ||||
|     def uses_datetime_field(self): | ||||
|         """ | ||||
|         Return `True` if the date field is a `DateTimeField` and `False` | ||||
|         if it's a `DateField`. | ||||
|         """ | ||||
|         model = self.get_queryset().model if self.model is None else self.model | ||||
|         field = model._meta.get_field(self.get_date_field()) | ||||
|         return isinstance(field, models.DateTimeField) | ||||
|  | ||||
|     def _make_date_lookup_arg(self, value): | ||||
|         """ | ||||
|         Convert a date into a datetime when the date field is a DateTimeField. | ||||
|  | ||||
|         When time zone support is enabled, `date` is assumed to be in the | ||||
|         current time zone, so that displayed items are consistent with the URL. | ||||
|         """ | ||||
|         if self.uses_datetime_field: | ||||
|             value = datetime.datetime.combine(value, datetime.time.min) | ||||
|             if settings.USE_TZ: | ||||
|                 value = timezone.make_aware(value) | ||||
|         return value | ||||
|  | ||||
|     def _make_single_date_lookup(self, date): | ||||
|         """ | ||||
|         Get the lookup kwargs for filtering on a single date. | ||||
|  | ||||
|         If the date field is a DateTimeField, we can't just filter on | ||||
|         date_field=date because that doesn't take the time into account. | ||||
|         """ | ||||
|         date_field = self.get_date_field() | ||||
|         if self.uses_datetime_field: | ||||
|             since = self._make_date_lookup_arg(date) | ||||
|             until = self._make_date_lookup_arg(date + datetime.timedelta(days=1)) | ||||
|             return { | ||||
|                 "%s__gte" % date_field: since, | ||||
|                 "%s__lt" % date_field: until, | ||||
|             } | ||||
|         else: | ||||
|             # Skip self._make_date_lookup_arg, it's a no-op in this branch. | ||||
|             return {date_field: date} | ||||
|  | ||||
|  | ||||
| class BaseDateListView(MultipleObjectMixin, DateMixin, View): | ||||
|     """Abstract base class for date-based views displaying a list of objects.""" | ||||
|  | ||||
|     allow_empty = False | ||||
|     date_list_period = "year" | ||||
|  | ||||
|     def get(self, request, *args, **kwargs): | ||||
|         self.date_list, self.object_list, extra_context = self.get_dated_items() | ||||
|         context = self.get_context_data( | ||||
|             object_list=self.object_list, date_list=self.date_list, **extra_context | ||||
|         ) | ||||
|         return self.render_to_response(context) | ||||
|  | ||||
|     def get_dated_items(self): | ||||
|         """Obtain the list of dates and items.""" | ||||
|         raise NotImplementedError( | ||||
|             "A DateView must provide an implementation of get_dated_items()" | ||||
|         ) | ||||
|  | ||||
|     def get_ordering(self): | ||||
|         """ | ||||
|         Return the field or fields to use for ordering the queryset; use the | ||||
|         date field by default. | ||||
|         """ | ||||
|         return "-%s" % self.get_date_field() if self.ordering is None else self.ordering | ||||
|  | ||||
|     def get_dated_queryset(self, **lookup): | ||||
|         """ | ||||
|         Get a queryset properly filtered according to `allow_future` and any | ||||
|         extra lookup kwargs. | ||||
|         """ | ||||
|         qs = self.get_queryset().filter(**lookup) | ||||
|         date_field = self.get_date_field() | ||||
|         allow_future = self.get_allow_future() | ||||
|         allow_empty = self.get_allow_empty() | ||||
|         paginate_by = self.get_paginate_by(qs) | ||||
|  | ||||
|         if not allow_future: | ||||
|             now = timezone.now() if self.uses_datetime_field else timezone_today() | ||||
|             qs = qs.filter(**{"%s__lte" % date_field: now}) | ||||
|  | ||||
|         if not allow_empty: | ||||
|             # When pagination is enabled, it's better to do a cheap query | ||||
|             # than to load the unpaginated queryset in memory. | ||||
|             is_empty = not qs if paginate_by is None else not qs.exists() | ||||
|             if is_empty: | ||||
|                 raise Http404( | ||||
|                     _("No %(verbose_name_plural)s available") | ||||
|                     % { | ||||
|                         "verbose_name_plural": qs.model._meta.verbose_name_plural, | ||||
|                     } | ||||
|                 ) | ||||
|  | ||||
|         return qs | ||||
|  | ||||
|     def get_date_list_period(self): | ||||
|         """ | ||||
|         Get the aggregation period for the list of dates: 'year', 'month', or | ||||
|         'day'. | ||||
|         """ | ||||
|         return self.date_list_period | ||||
|  | ||||
|     def get_date_list(self, queryset, date_type=None, ordering="ASC"): | ||||
|         """ | ||||
|         Get a date list by calling `queryset.dates/datetimes()`, checking | ||||
|         along the way for empty lists that aren't allowed. | ||||
|         """ | ||||
|         date_field = self.get_date_field() | ||||
|         allow_empty = self.get_allow_empty() | ||||
|         if date_type is None: | ||||
|             date_type = self.get_date_list_period() | ||||
|  | ||||
|         if self.uses_datetime_field: | ||||
|             date_list = queryset.datetimes(date_field, date_type, ordering) | ||||
|         else: | ||||
|             date_list = queryset.dates(date_field, date_type, ordering) | ||||
|         if date_list is not None and not date_list and not allow_empty: | ||||
|             raise Http404( | ||||
|                 _("No %(verbose_name_plural)s available") | ||||
|                 % { | ||||
|                     "verbose_name_plural": queryset.model._meta.verbose_name_plural, | ||||
|                 } | ||||
|             ) | ||||
|  | ||||
|         return date_list | ||||
|  | ||||
|  | ||||
| class BaseArchiveIndexView(BaseDateListView): | ||||
|     """ | ||||
|     Base class for archives of date-based items. Requires a response mixin. | ||||
|     """ | ||||
|  | ||||
|     context_object_name = "latest" | ||||
|  | ||||
|     def get_dated_items(self): | ||||
|         """Return (date_list, items, extra_context) for this request.""" | ||||
|         qs = self.get_dated_queryset() | ||||
|         date_list = self.get_date_list(qs, ordering="DESC") | ||||
|  | ||||
|         if not date_list: | ||||
|             qs = qs.none() | ||||
|  | ||||
|         return (date_list, qs, {}) | ||||
|  | ||||
|  | ||||
| class ArchiveIndexView(MultipleObjectTemplateResponseMixin, BaseArchiveIndexView): | ||||
|     """Top-level archive of date-based items.""" | ||||
|  | ||||
|     template_name_suffix = "_archive" | ||||
|  | ||||
|  | ||||
| class BaseYearArchiveView(YearMixin, BaseDateListView): | ||||
|     """List of objects published in a given year.""" | ||||
|  | ||||
|     date_list_period = "month" | ||||
|     make_object_list = False | ||||
|  | ||||
|     def get_dated_items(self): | ||||
|         """Return (date_list, items, extra_context) for this request.""" | ||||
|         year = self.get_year() | ||||
|  | ||||
|         date_field = self.get_date_field() | ||||
|         date = _date_from_string(year, self.get_year_format()) | ||||
|  | ||||
|         since = self._make_date_lookup_arg(date) | ||||
|         until = self._make_date_lookup_arg(self._get_next_year(date)) | ||||
|         lookup_kwargs = { | ||||
|             "%s__gte" % date_field: since, | ||||
|             "%s__lt" % date_field: until, | ||||
|         } | ||||
|  | ||||
|         qs = self.get_dated_queryset(**lookup_kwargs) | ||||
|         date_list = self.get_date_list(qs) | ||||
|  | ||||
|         if not self.get_make_object_list(): | ||||
|             # We need this to be a queryset since parent classes introspect it | ||||
|             # to find information about the model. | ||||
|             qs = qs.none() | ||||
|  | ||||
|         return ( | ||||
|             date_list, | ||||
|             qs, | ||||
|             { | ||||
|                 "year": date, | ||||
|                 "next_year": self.get_next_year(date), | ||||
|                 "previous_year": self.get_previous_year(date), | ||||
|             }, | ||||
|         ) | ||||
|  | ||||
|     def get_make_object_list(self): | ||||
|         """ | ||||
|         Return `True` if this view should contain the full list of objects in | ||||
|         the given year. | ||||
|         """ | ||||
|         return self.make_object_list | ||||
|  | ||||
|  | ||||
| class YearArchiveView(MultipleObjectTemplateResponseMixin, BaseYearArchiveView): | ||||
|     """List of objects published in a given year.""" | ||||
|  | ||||
|     template_name_suffix = "_archive_year" | ||||
|  | ||||
|  | ||||
| class BaseMonthArchiveView(YearMixin, MonthMixin, BaseDateListView): | ||||
|     """List of objects published in a given month.""" | ||||
|  | ||||
|     date_list_period = "day" | ||||
|  | ||||
|     def get_dated_items(self): | ||||
|         """Return (date_list, items, extra_context) for this request.""" | ||||
|         year = self.get_year() | ||||
|         month = self.get_month() | ||||
|  | ||||
|         date_field = self.get_date_field() | ||||
|         date = _date_from_string( | ||||
|             year, self.get_year_format(), month, self.get_month_format() | ||||
|         ) | ||||
|  | ||||
|         since = self._make_date_lookup_arg(date) | ||||
|         until = self._make_date_lookup_arg(self._get_next_month(date)) | ||||
|         lookup_kwargs = { | ||||
|             "%s__gte" % date_field: since, | ||||
|             "%s__lt" % date_field: until, | ||||
|         } | ||||
|  | ||||
|         qs = self.get_dated_queryset(**lookup_kwargs) | ||||
|         date_list = self.get_date_list(qs) | ||||
|  | ||||
|         return ( | ||||
|             date_list, | ||||
|             qs, | ||||
|             { | ||||
|                 "month": date, | ||||
|                 "next_month": self.get_next_month(date), | ||||
|                 "previous_month": self.get_previous_month(date), | ||||
|             }, | ||||
|         ) | ||||
|  | ||||
|  | ||||
| class MonthArchiveView(MultipleObjectTemplateResponseMixin, BaseMonthArchiveView): | ||||
|     """List of objects published in a given month.""" | ||||
|  | ||||
|     template_name_suffix = "_archive_month" | ||||
|  | ||||
|  | ||||
| class BaseWeekArchiveView(YearMixin, WeekMixin, BaseDateListView): | ||||
|     """List of objects published in a given week.""" | ||||
|  | ||||
|     def get_dated_items(self): | ||||
|         """Return (date_list, items, extra_context) for this request.""" | ||||
|         year = self.get_year() | ||||
|         week = self.get_week() | ||||
|  | ||||
|         date_field = self.get_date_field() | ||||
|         week_format = self.get_week_format() | ||||
|         week_choices = {"%W": "1", "%U": "0", "%V": "1"} | ||||
|         try: | ||||
|             week_start = week_choices[week_format] | ||||
|         except KeyError: | ||||
|             raise ValueError( | ||||
|                 "Unknown week format %r. Choices are: %s" | ||||
|                 % ( | ||||
|                     week_format, | ||||
|                     ", ".join(sorted(week_choices)), | ||||
|                 ) | ||||
|             ) | ||||
|         year_format = self.get_year_format() | ||||
|         if week_format == "%V" and year_format != "%G": | ||||
|             raise ValueError( | ||||
|                 "ISO week directive '%s' is incompatible with the year " | ||||
|                 "directive '%s'. Use the ISO year '%%G' instead." | ||||
|                 % ( | ||||
|                     week_format, | ||||
|                     year_format, | ||||
|                 ) | ||||
|             ) | ||||
|         date = _date_from_string(year, year_format, week_start, "%w", week, week_format) | ||||
|         since = self._make_date_lookup_arg(date) | ||||
|         until = self._make_date_lookup_arg(self._get_next_week(date)) | ||||
|         lookup_kwargs = { | ||||
|             "%s__gte" % date_field: since, | ||||
|             "%s__lt" % date_field: until, | ||||
|         } | ||||
|  | ||||
|         qs = self.get_dated_queryset(**lookup_kwargs) | ||||
|  | ||||
|         return ( | ||||
|             None, | ||||
|             qs, | ||||
|             { | ||||
|                 "week": date, | ||||
|                 "next_week": self.get_next_week(date), | ||||
|                 "previous_week": self.get_previous_week(date), | ||||
|             }, | ||||
|         ) | ||||
|  | ||||
|  | ||||
| class WeekArchiveView(MultipleObjectTemplateResponseMixin, BaseWeekArchiveView): | ||||
|     """List of objects published in a given week.""" | ||||
|  | ||||
|     template_name_suffix = "_archive_week" | ||||
|  | ||||
|  | ||||
| class BaseDayArchiveView(YearMixin, MonthMixin, DayMixin, BaseDateListView): | ||||
|     """List of objects published on a given day.""" | ||||
|  | ||||
|     def get_dated_items(self): | ||||
|         """Return (date_list, items, extra_context) for this request.""" | ||||
|         year = self.get_year() | ||||
|         month = self.get_month() | ||||
|         day = self.get_day() | ||||
|  | ||||
|         date = _date_from_string( | ||||
|             year, | ||||
|             self.get_year_format(), | ||||
|             month, | ||||
|             self.get_month_format(), | ||||
|             day, | ||||
|             self.get_day_format(), | ||||
|         ) | ||||
|  | ||||
|         return self._get_dated_items(date) | ||||
|  | ||||
|     def _get_dated_items(self, date): | ||||
|         """ | ||||
|         Do the actual heavy lifting of getting the dated items; this accepts a | ||||
|         date object so that TodayArchiveView can be trivial. | ||||
|         """ | ||||
|         lookup_kwargs = self._make_single_date_lookup(date) | ||||
|         qs = self.get_dated_queryset(**lookup_kwargs) | ||||
|  | ||||
|         return ( | ||||
|             None, | ||||
|             qs, | ||||
|             { | ||||
|                 "day": date, | ||||
|                 "previous_day": self.get_previous_day(date), | ||||
|                 "next_day": self.get_next_day(date), | ||||
|                 "previous_month": self.get_previous_month(date), | ||||
|                 "next_month": self.get_next_month(date), | ||||
|             }, | ||||
|         ) | ||||
|  | ||||
|  | ||||
| class DayArchiveView(MultipleObjectTemplateResponseMixin, BaseDayArchiveView): | ||||
|     """List of objects published on a given day.""" | ||||
|  | ||||
|     template_name_suffix = "_archive_day" | ||||
|  | ||||
|  | ||||
| class BaseTodayArchiveView(BaseDayArchiveView): | ||||
|     """List of objects published today.""" | ||||
|  | ||||
|     def get_dated_items(self): | ||||
|         """Return (date_list, items, extra_context) for this request.""" | ||||
|         return self._get_dated_items(datetime.date.today()) | ||||
|  | ||||
|  | ||||
| class TodayArchiveView(MultipleObjectTemplateResponseMixin, BaseTodayArchiveView): | ||||
|     """List of objects published today.""" | ||||
|  | ||||
|     template_name_suffix = "_archive_day" | ||||
|  | ||||
|  | ||||
| class BaseDateDetailView(YearMixin, MonthMixin, DayMixin, DateMixin, BaseDetailView): | ||||
|     """ | ||||
|     Detail view of a single object on a single date; this differs from the | ||||
|     standard DetailView by accepting a year/month/day in the URL. | ||||
|     """ | ||||
|  | ||||
|     def get_object(self, queryset=None): | ||||
|         """Get the object this request displays.""" | ||||
|         year = self.get_year() | ||||
|         month = self.get_month() | ||||
|         day = self.get_day() | ||||
|         date = _date_from_string( | ||||
|             year, | ||||
|             self.get_year_format(), | ||||
|             month, | ||||
|             self.get_month_format(), | ||||
|             day, | ||||
|             self.get_day_format(), | ||||
|         ) | ||||
|  | ||||
|         # Use a custom queryset if provided | ||||
|         qs = self.get_queryset() if queryset is None else queryset | ||||
|  | ||||
|         if not self.get_allow_future() and date > datetime.date.today(): | ||||
|             raise Http404( | ||||
|                 _( | ||||
|                     "Future %(verbose_name_plural)s not available because " | ||||
|                     "%(class_name)s.allow_future is False." | ||||
|                 ) | ||||
|                 % { | ||||
|                     "verbose_name_plural": qs.model._meta.verbose_name_plural, | ||||
|                     "class_name": self.__class__.__name__, | ||||
|                 } | ||||
|             ) | ||||
|  | ||||
|         # Filter down a queryset from self.queryset using the date from the | ||||
|         # URL. This'll get passed as the queryset to DetailView.get_object, | ||||
|         # which'll handle the 404 | ||||
|         lookup_kwargs = self._make_single_date_lookup(date) | ||||
|         qs = qs.filter(**lookup_kwargs) | ||||
|  | ||||
|         return super().get_object(queryset=qs) | ||||
|  | ||||
|  | ||||
| class DateDetailView(SingleObjectTemplateResponseMixin, BaseDateDetailView): | ||||
|     """ | ||||
|     Detail view of a single object on a single date; this differs from the | ||||
|     standard DetailView by accepting a year/month/day in the URL. | ||||
|     """ | ||||
|  | ||||
|     template_name_suffix = "_detail" | ||||
|  | ||||
|  | ||||
| def _date_from_string( | ||||
|     year, year_format, month="", month_format="", day="", day_format="", delim="__" | ||||
| ): | ||||
|     """ | ||||
|     Get a datetime.date object given a format string and a year, month, and day | ||||
|     (only year is mandatory). Raise a 404 for an invalid date. | ||||
|     """ | ||||
|     format = year_format + delim + month_format + delim + day_format | ||||
|     datestr = str(year) + delim + str(month) + delim + str(day) | ||||
|     try: | ||||
|         return datetime.datetime.strptime(datestr, format).date() | ||||
|     except ValueError: | ||||
|         raise Http404( | ||||
|             _("Invalid date string “%(datestr)s” given format “%(format)s”") | ||||
|             % { | ||||
|                 "datestr": datestr, | ||||
|                 "format": format, | ||||
|             } | ||||
|         ) | ||||
|  | ||||
|  | ||||
| def _get_next_prev(generic_view, date, is_previous, period): | ||||
|     """ | ||||
|     Get the next or the previous valid date. The idea is to allow links on | ||||
|     month/day views to never be 404s by never providing a date that'll be | ||||
|     invalid for the given view. | ||||
|  | ||||
|     This is a bit complicated since it handles different intervals of time, | ||||
|     hence the coupling to generic_view. | ||||
|  | ||||
|     However in essence the logic comes down to: | ||||
|  | ||||
|         * If allow_empty and allow_future are both true, this is easy: just | ||||
|           return the naive result (just the next/previous day/week/month, | ||||
|           regardless of object existence.) | ||||
|  | ||||
|         * If allow_empty is true, allow_future is false, and the naive result | ||||
|           isn't in the future, then return it; otherwise return None. | ||||
|  | ||||
|         * If allow_empty is false and allow_future is true, return the next | ||||
|           date *that contains a valid object*, even if it's in the future. If | ||||
|           there are no next objects, return None. | ||||
|  | ||||
|         * If allow_empty is false and allow_future is false, return the next | ||||
|           date that contains a valid object. If that date is in the future, or | ||||
|           if there are no next objects, return None. | ||||
|     """ | ||||
|     date_field = generic_view.get_date_field() | ||||
|     allow_empty = generic_view.get_allow_empty() | ||||
|     allow_future = generic_view.get_allow_future() | ||||
|  | ||||
|     get_current = getattr(generic_view, "_get_current_%s" % period) | ||||
|     get_next = getattr(generic_view, "_get_next_%s" % period) | ||||
|  | ||||
|     # Bounds of the current interval | ||||
|     start, end = get_current(date), get_next(date) | ||||
|  | ||||
|     # If allow_empty is True, the naive result will be valid | ||||
|     if allow_empty: | ||||
|         if is_previous: | ||||
|             result = get_current(start - datetime.timedelta(days=1)) | ||||
|         else: | ||||
|             result = end | ||||
|  | ||||
|         if allow_future or result <= timezone_today(): | ||||
|             return result | ||||
|         else: | ||||
|             return None | ||||
|  | ||||
|     # Otherwise, we'll need to go to the database to look for an object | ||||
|     # whose date_field is at least (greater than/less than) the given | ||||
|     # naive result | ||||
|     else: | ||||
|         # Construct a lookup and an ordering depending on whether we're doing | ||||
|         # a previous date or a next date lookup. | ||||
|         if is_previous: | ||||
|             lookup = {"%s__lt" % date_field: generic_view._make_date_lookup_arg(start)} | ||||
|             ordering = "-%s" % date_field | ||||
|         else: | ||||
|             lookup = {"%s__gte" % date_field: generic_view._make_date_lookup_arg(end)} | ||||
|             ordering = date_field | ||||
|  | ||||
|         # Filter out objects in the future if appropriate. | ||||
|         if not allow_future: | ||||
|             # Fortunately, to match the implementation of allow_future, | ||||
|             # we need __lte, which doesn't conflict with __lt above. | ||||
|             if generic_view.uses_datetime_field: | ||||
|                 now = timezone.now() | ||||
|             else: | ||||
|                 now = timezone_today() | ||||
|             lookup["%s__lte" % date_field] = now | ||||
|  | ||||
|         qs = generic_view.get_queryset().filter(**lookup).order_by(ordering) | ||||
|  | ||||
|         # Snag the first object from the queryset; if it doesn't exist that | ||||
|         # means there's no next/previous link available. | ||||
|         try: | ||||
|             result = getattr(qs[0], date_field) | ||||
|         except IndexError: | ||||
|             return None | ||||
|  | ||||
|         # Convert datetimes to dates in the current time zone. | ||||
|         if generic_view.uses_datetime_field: | ||||
|             if settings.USE_TZ: | ||||
|                 result = timezone.localtime(result) | ||||
|             result = result.date() | ||||
|  | ||||
|         # Return the first day of the period. | ||||
|         return get_current(result) | ||||
|  | ||||
|  | ||||
| def timezone_today(): | ||||
|     """Return the current date in the current time zone.""" | ||||
|     if settings.USE_TZ: | ||||
|         return timezone.localdate() | ||||
|     else: | ||||
|         return datetime.date.today() | ||||
| @ -0,0 +1,180 @@ | ||||
| from django.core.exceptions import ImproperlyConfigured | ||||
| from django.db import models | ||||
| from django.http import Http404 | ||||
| from django.utils.translation import gettext as _ | ||||
| from django.views.generic.base import ContextMixin, TemplateResponseMixin, View | ||||
|  | ||||
|  | ||||
| class SingleObjectMixin(ContextMixin): | ||||
|     """ | ||||
|     Provide the ability to retrieve a single object for further manipulation. | ||||
|     """ | ||||
|  | ||||
|     model = None | ||||
|     queryset = None | ||||
|     slug_field = "slug" | ||||
|     context_object_name = None | ||||
|     slug_url_kwarg = "slug" | ||||
|     pk_url_kwarg = "pk" | ||||
|     query_pk_and_slug = False | ||||
|  | ||||
|     def get_object(self, queryset=None): | ||||
|         """ | ||||
|         Return the object the view is displaying. | ||||
|  | ||||
|         Require `self.queryset` and a `pk` or `slug` argument in the URLconf. | ||||
|         Subclasses can override this to return any object. | ||||
|         """ | ||||
|         # Use a custom queryset if provided; this is required for subclasses | ||||
|         # like DateDetailView | ||||
|         if queryset is None: | ||||
|             queryset = self.get_queryset() | ||||
|  | ||||
|         # Next, try looking up by primary key. | ||||
|         pk = self.kwargs.get(self.pk_url_kwarg) | ||||
|         slug = self.kwargs.get(self.slug_url_kwarg) | ||||
|         if pk is not None: | ||||
|             queryset = queryset.filter(pk=pk) | ||||
|  | ||||
|         # Next, try looking up by slug. | ||||
|         if slug is not None and (pk is None or self.query_pk_and_slug): | ||||
|             slug_field = self.get_slug_field() | ||||
|             queryset = queryset.filter(**{slug_field: slug}) | ||||
|  | ||||
|         # If none of those are defined, it's an error. | ||||
|         if pk is None and slug is None: | ||||
|             raise AttributeError( | ||||
|                 "Generic detail view %s must be called with either an object " | ||||
|                 "pk or a slug in the URLconf." % self.__class__.__name__ | ||||
|             ) | ||||
|  | ||||
|         try: | ||||
|             # Get the single item from the filtered queryset | ||||
|             obj = queryset.get() | ||||
|         except queryset.model.DoesNotExist: | ||||
|             raise Http404( | ||||
|                 _("No %(verbose_name)s found matching the query") | ||||
|                 % {"verbose_name": queryset.model._meta.verbose_name} | ||||
|             ) | ||||
|         return obj | ||||
|  | ||||
|     def get_queryset(self): | ||||
|         """ | ||||
|         Return the `QuerySet` that will be used to look up the object. | ||||
|  | ||||
|         This method is called by the default implementation of get_object() and | ||||
|         may not be called if get_object() is overridden. | ||||
|         """ | ||||
|         if self.queryset is None: | ||||
|             if self.model: | ||||
|                 return self.model._default_manager.all() | ||||
|             else: | ||||
|                 raise ImproperlyConfigured( | ||||
|                     "%(cls)s is missing a QuerySet. Define " | ||||
|                     "%(cls)s.model, %(cls)s.queryset, or override " | ||||
|                     "%(cls)s.get_queryset()." % {"cls": self.__class__.__name__} | ||||
|                 ) | ||||
|         return self.queryset.all() | ||||
|  | ||||
|     def get_slug_field(self): | ||||
|         """Get the name of a slug field to be used to look up by slug.""" | ||||
|         return self.slug_field | ||||
|  | ||||
|     def get_context_object_name(self, obj): | ||||
|         """Get the name to use for the object.""" | ||||
|         if self.context_object_name: | ||||
|             return self.context_object_name | ||||
|         elif isinstance(obj, models.Model): | ||||
|             return obj._meta.model_name | ||||
|         else: | ||||
|             return None | ||||
|  | ||||
|     def get_context_data(self, **kwargs): | ||||
|         """Insert the single object into the context dict.""" | ||||
|         context = {} | ||||
|         if self.object: | ||||
|             context["object"] = self.object | ||||
|             context_object_name = self.get_context_object_name(self.object) | ||||
|             if context_object_name: | ||||
|                 context[context_object_name] = self.object | ||||
|         context.update(kwargs) | ||||
|         return super().get_context_data(**context) | ||||
|  | ||||
|  | ||||
| class BaseDetailView(SingleObjectMixin, View): | ||||
|     """A base view for displaying a single object.""" | ||||
|  | ||||
|     def get(self, request, *args, **kwargs): | ||||
|         self.object = self.get_object() | ||||
|         context = self.get_context_data(object=self.object) | ||||
|         return self.render_to_response(context) | ||||
|  | ||||
|  | ||||
| class SingleObjectTemplateResponseMixin(TemplateResponseMixin): | ||||
|     template_name_field = None | ||||
|     template_name_suffix = "_detail" | ||||
|  | ||||
|     def get_template_names(self): | ||||
|         """ | ||||
|         Return a list of template names to be used for the request. May not be | ||||
|         called if render_to_response() is overridden. Return the following list: | ||||
|  | ||||
|         * the value of ``template_name`` on the view (if provided) | ||||
|         * the contents of the ``template_name_field`` field on the | ||||
|           object instance that the view is operating upon (if available) | ||||
|         * ``<app_label>/<model_name><template_name_suffix>.html`` | ||||
|         """ | ||||
|         try: | ||||
|             names = super().get_template_names() | ||||
|         except ImproperlyConfigured: | ||||
|             # If template_name isn't specified, it's not a problem -- | ||||
|             # we just start with an empty list. | ||||
|             names = [] | ||||
|  | ||||
|             # If self.template_name_field is set, grab the value of the field | ||||
|             # of that name from the object; this is the most specific template | ||||
|             # name, if given. | ||||
|             if self.object and self.template_name_field: | ||||
|                 name = getattr(self.object, self.template_name_field, None) | ||||
|                 if name: | ||||
|                     names.insert(0, name) | ||||
|  | ||||
|             # The least-specific option is the default <app>/<model>_detail.html; | ||||
|             # only use this if the object in question is a model. | ||||
|             if isinstance(self.object, models.Model): | ||||
|                 object_meta = self.object._meta | ||||
|                 names.append( | ||||
|                     "%s/%s%s.html" | ||||
|                     % ( | ||||
|                         object_meta.app_label, | ||||
|                         object_meta.model_name, | ||||
|                         self.template_name_suffix, | ||||
|                     ) | ||||
|                 ) | ||||
|             elif getattr(self, "model", None) is not None and issubclass( | ||||
|                 self.model, models.Model | ||||
|             ): | ||||
|                 names.append( | ||||
|                     "%s/%s%s.html" | ||||
|                     % ( | ||||
|                         self.model._meta.app_label, | ||||
|                         self.model._meta.model_name, | ||||
|                         self.template_name_suffix, | ||||
|                     ) | ||||
|                 ) | ||||
|  | ||||
|             # If we still haven't managed to find any template names, we should | ||||
|             # re-raise the ImproperlyConfigured to alert the user. | ||||
|             if not names: | ||||
|                 raise | ||||
|  | ||||
|         return names | ||||
|  | ||||
|  | ||||
| class DetailView(SingleObjectTemplateResponseMixin, BaseDetailView): | ||||
|     """ | ||||
|     Render a "detail" view of an object. | ||||
|  | ||||
|     By default this is a model instance looked up from `self.queryset`, but the | ||||
|     view will support display of *any* object by overriding `self.get_object()`. | ||||
|     """ | ||||
| @ -0,0 +1,294 @@ | ||||
| import warnings | ||||
|  | ||||
| from django.core.exceptions import ImproperlyConfigured | ||||
| from django.forms import Form | ||||
| from django.forms import models as model_forms | ||||
| from django.http import HttpResponseRedirect | ||||
| from django.views.generic.base import ContextMixin, TemplateResponseMixin, View | ||||
| from django.views.generic.detail import ( | ||||
|     BaseDetailView, | ||||
|     SingleObjectMixin, | ||||
|     SingleObjectTemplateResponseMixin, | ||||
| ) | ||||
|  | ||||
|  | ||||
| class FormMixin(ContextMixin): | ||||
|     """Provide a way to show and handle a form in a request.""" | ||||
|  | ||||
|     initial = {} | ||||
|     form_class = None | ||||
|     success_url = None | ||||
|     prefix = None | ||||
|  | ||||
|     def get_initial(self): | ||||
|         """Return the initial data to use for forms on this view.""" | ||||
|         return self.initial.copy() | ||||
|  | ||||
|     def get_prefix(self): | ||||
|         """Return the prefix to use for forms.""" | ||||
|         return self.prefix | ||||
|  | ||||
|     def get_form_class(self): | ||||
|         """Return the form class to use.""" | ||||
|         return self.form_class | ||||
|  | ||||
|     def get_form(self, form_class=None): | ||||
|         """Return an instance of the form to be used in this view.""" | ||||
|         if form_class is None: | ||||
|             form_class = self.get_form_class() | ||||
|         return form_class(**self.get_form_kwargs()) | ||||
|  | ||||
|     def get_form_kwargs(self): | ||||
|         """Return the keyword arguments for instantiating the form.""" | ||||
|         kwargs = { | ||||
|             "initial": self.get_initial(), | ||||
|             "prefix": self.get_prefix(), | ||||
|         } | ||||
|  | ||||
|         if self.request.method in ("POST", "PUT"): | ||||
|             kwargs.update( | ||||
|                 { | ||||
|                     "data": self.request.POST, | ||||
|                     "files": self.request.FILES, | ||||
|                 } | ||||
|             ) | ||||
|         return kwargs | ||||
|  | ||||
|     def get_success_url(self): | ||||
|         """Return the URL to redirect to after processing a valid form.""" | ||||
|         if not self.success_url: | ||||
|             raise ImproperlyConfigured("No URL to redirect to. Provide a success_url.") | ||||
|         return str(self.success_url)  # success_url may be lazy | ||||
|  | ||||
|     def form_valid(self, form): | ||||
|         """If the form is valid, redirect to the supplied URL.""" | ||||
|         return HttpResponseRedirect(self.get_success_url()) | ||||
|  | ||||
|     def form_invalid(self, form): | ||||
|         """If the form is invalid, render the invalid form.""" | ||||
|         return self.render_to_response(self.get_context_data(form=form)) | ||||
|  | ||||
|     def get_context_data(self, **kwargs): | ||||
|         """Insert the form into the context dict.""" | ||||
|         if "form" not in kwargs: | ||||
|             kwargs["form"] = self.get_form() | ||||
|         return super().get_context_data(**kwargs) | ||||
|  | ||||
|  | ||||
| class ModelFormMixin(FormMixin, SingleObjectMixin): | ||||
|     """Provide a way to show and handle a ModelForm in a request.""" | ||||
|  | ||||
|     fields = None | ||||
|  | ||||
|     def get_form_class(self): | ||||
|         """Return the form class to use in this view.""" | ||||
|         if self.fields is not None and self.form_class: | ||||
|             raise ImproperlyConfigured( | ||||
|                 "Specifying both 'fields' and 'form_class' is not permitted." | ||||
|             ) | ||||
|         if self.form_class: | ||||
|             return self.form_class | ||||
|         else: | ||||
|             if self.model is not None: | ||||
|                 # If a model has been explicitly provided, use it | ||||
|                 model = self.model | ||||
|             elif getattr(self, "object", None) is not None: | ||||
|                 # If this view is operating on a single object, use | ||||
|                 # the class of that object | ||||
|                 model = self.object.__class__ | ||||
|             else: | ||||
|                 # Try to get a queryset and extract the model class | ||||
|                 # from that | ||||
|                 model = self.get_queryset().model | ||||
|  | ||||
|             if self.fields is None: | ||||
|                 raise ImproperlyConfigured( | ||||
|                     "Using ModelFormMixin (base class of %s) without " | ||||
|                     "the 'fields' attribute is prohibited." % self.__class__.__name__ | ||||
|                 ) | ||||
|  | ||||
|             return model_forms.modelform_factory(model, fields=self.fields) | ||||
|  | ||||
|     def get_form_kwargs(self): | ||||
|         """Return the keyword arguments for instantiating the form.""" | ||||
|         kwargs = super().get_form_kwargs() | ||||
|         if hasattr(self, "object"): | ||||
|             kwargs.update({"instance": self.object}) | ||||
|         return kwargs | ||||
|  | ||||
|     def get_success_url(self): | ||||
|         """Return the URL to redirect to after processing a valid form.""" | ||||
|         if self.success_url: | ||||
|             url = self.success_url.format(**self.object.__dict__) | ||||
|         else: | ||||
|             try: | ||||
|                 url = self.object.get_absolute_url() | ||||
|             except AttributeError: | ||||
|                 raise ImproperlyConfigured( | ||||
|                     "No URL to redirect to.  Either provide a url or define" | ||||
|                     " a get_absolute_url method on the Model." | ||||
|                 ) | ||||
|         return url | ||||
|  | ||||
|     def form_valid(self, form): | ||||
|         """If the form is valid, save the associated model.""" | ||||
|         self.object = form.save() | ||||
|         return super().form_valid(form) | ||||
|  | ||||
|  | ||||
| class ProcessFormView(View): | ||||
|     """Render a form on GET and processes it on POST.""" | ||||
|  | ||||
|     def get(self, request, *args, **kwargs): | ||||
|         """Handle GET requests: instantiate a blank version of the form.""" | ||||
|         return self.render_to_response(self.get_context_data()) | ||||
|  | ||||
|     def post(self, request, *args, **kwargs): | ||||
|         """ | ||||
|         Handle POST requests: instantiate a form instance with the passed | ||||
|         POST variables and then check if it's valid. | ||||
|         """ | ||||
|         form = self.get_form() | ||||
|         if form.is_valid(): | ||||
|             return self.form_valid(form) | ||||
|         else: | ||||
|             return self.form_invalid(form) | ||||
|  | ||||
|     # PUT is a valid HTTP verb for creating (with a known URL) or editing an | ||||
|     # object, note that browsers only support POST for now. | ||||
|     def put(self, *args, **kwargs): | ||||
|         return self.post(*args, **kwargs) | ||||
|  | ||||
|  | ||||
| class BaseFormView(FormMixin, ProcessFormView): | ||||
|     """A base view for displaying a form.""" | ||||
|  | ||||
|  | ||||
| class FormView(TemplateResponseMixin, BaseFormView): | ||||
|     """A view for displaying a form and rendering a template response.""" | ||||
|  | ||||
|  | ||||
| class BaseCreateView(ModelFormMixin, ProcessFormView): | ||||
|     """ | ||||
|     Base view for creating a new object instance. | ||||
|  | ||||
|     Using this base class requires subclassing to provide a response mixin. | ||||
|     """ | ||||
|  | ||||
|     def get(self, request, *args, **kwargs): | ||||
|         self.object = None | ||||
|         return super().get(request, *args, **kwargs) | ||||
|  | ||||
|     def post(self, request, *args, **kwargs): | ||||
|         self.object = None | ||||
|         return super().post(request, *args, **kwargs) | ||||
|  | ||||
|  | ||||
| class CreateView(SingleObjectTemplateResponseMixin, BaseCreateView): | ||||
|     """ | ||||
|     View for creating a new object, with a response rendered by a template. | ||||
|     """ | ||||
|  | ||||
|     template_name_suffix = "_form" | ||||
|  | ||||
|  | ||||
| class BaseUpdateView(ModelFormMixin, ProcessFormView): | ||||
|     """ | ||||
|     Base view for updating an existing object. | ||||
|  | ||||
|     Using this base class requires subclassing to provide a response mixin. | ||||
|     """ | ||||
|  | ||||
|     def get(self, request, *args, **kwargs): | ||||
|         self.object = self.get_object() | ||||
|         return super().get(request, *args, **kwargs) | ||||
|  | ||||
|     def post(self, request, *args, **kwargs): | ||||
|         self.object = self.get_object() | ||||
|         return super().post(request, *args, **kwargs) | ||||
|  | ||||
|  | ||||
| class UpdateView(SingleObjectTemplateResponseMixin, BaseUpdateView): | ||||
|     """View for updating an object, with a response rendered by a template.""" | ||||
|  | ||||
|     template_name_suffix = "_form" | ||||
|  | ||||
|  | ||||
| class DeletionMixin: | ||||
|     """Provide the ability to delete objects.""" | ||||
|  | ||||
|     success_url = None | ||||
|  | ||||
|     def delete(self, request, *args, **kwargs): | ||||
|         """ | ||||
|         Call the delete() method on the fetched object and then redirect to the | ||||
|         success URL. | ||||
|         """ | ||||
|         self.object = self.get_object() | ||||
|         success_url = self.get_success_url() | ||||
|         self.object.delete() | ||||
|         return HttpResponseRedirect(success_url) | ||||
|  | ||||
|     # Add support for browsers which only accept GET and POST for now. | ||||
|     def post(self, request, *args, **kwargs): | ||||
|         return self.delete(request, *args, **kwargs) | ||||
|  | ||||
|     def get_success_url(self): | ||||
|         if self.success_url: | ||||
|             return self.success_url.format(**self.object.__dict__) | ||||
|         else: | ||||
|             raise ImproperlyConfigured("No URL to redirect to. Provide a success_url.") | ||||
|  | ||||
|  | ||||
| # RemovedInDjango50Warning. | ||||
| class DeleteViewCustomDeleteWarning(Warning): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class BaseDeleteView(DeletionMixin, FormMixin, BaseDetailView): | ||||
|     """ | ||||
|     Base view for deleting an object. | ||||
|  | ||||
|     Using this base class requires subclassing to provide a response mixin. | ||||
|     """ | ||||
|  | ||||
|     form_class = Form | ||||
|  | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         # RemovedInDjango50Warning. | ||||
|         if self.__class__.delete is not DeletionMixin.delete: | ||||
|             warnings.warn( | ||||
|                 f"DeleteView uses FormMixin to handle POST requests. As a " | ||||
|                 f"consequence, any custom deletion logic in " | ||||
|                 f"{self.__class__.__name__}.delete() handler should be moved " | ||||
|                 f"to form_valid().", | ||||
|                 DeleteViewCustomDeleteWarning, | ||||
|                 stacklevel=2, | ||||
|             ) | ||||
|         super().__init__(*args, **kwargs) | ||||
|  | ||||
|     def post(self, request, *args, **kwargs): | ||||
|         # Set self.object before the usual form processing flow. | ||||
|         # Inlined because having DeletionMixin as the first base, for | ||||
|         # get_success_url(), makes leveraging super() with ProcessFormView | ||||
|         # overly complex. | ||||
|         self.object = self.get_object() | ||||
|         form = self.get_form() | ||||
|         if form.is_valid(): | ||||
|             return self.form_valid(form) | ||||
|         else: | ||||
|             return self.form_invalid(form) | ||||
|  | ||||
|     def form_valid(self, form): | ||||
|         success_url = self.get_success_url() | ||||
|         self.object.delete() | ||||
|         return HttpResponseRedirect(success_url) | ||||
|  | ||||
|  | ||||
| class DeleteView(SingleObjectTemplateResponseMixin, BaseDeleteView): | ||||
|     """ | ||||
|     View for deleting an object retrieved with self.get_object(), with a | ||||
|     response rendered by a template. | ||||
|     """ | ||||
|  | ||||
|     template_name_suffix = "_confirm_delete" | ||||
| @ -0,0 +1,220 @@ | ||||
| from django.core.exceptions import ImproperlyConfigured | ||||
| from django.core.paginator import InvalidPage, Paginator | ||||
| from django.db.models import QuerySet | ||||
| from django.http import Http404 | ||||
| from django.utils.translation import gettext as _ | ||||
| from django.views.generic.base import ContextMixin, TemplateResponseMixin, View | ||||
|  | ||||
|  | ||||
| class MultipleObjectMixin(ContextMixin): | ||||
|     """A mixin for views manipulating multiple objects.""" | ||||
|  | ||||
|     allow_empty = True | ||||
|     queryset = None | ||||
|     model = None | ||||
|     paginate_by = None | ||||
|     paginate_orphans = 0 | ||||
|     context_object_name = None | ||||
|     paginator_class = Paginator | ||||
|     page_kwarg = "page" | ||||
|     ordering = None | ||||
|  | ||||
|     def get_queryset(self): | ||||
|         """ | ||||
|         Return the list of items for this view. | ||||
|  | ||||
|         The return value must be an iterable and may be an instance of | ||||
|         `QuerySet` in which case `QuerySet` specific behavior will be enabled. | ||||
|         """ | ||||
|         if self.queryset is not None: | ||||
|             queryset = self.queryset | ||||
|             if isinstance(queryset, QuerySet): | ||||
|                 queryset = queryset.all() | ||||
|         elif self.model is not None: | ||||
|             queryset = self.model._default_manager.all() | ||||
|         else: | ||||
|             raise ImproperlyConfigured( | ||||
|                 "%(cls)s is missing a QuerySet. Define " | ||||
|                 "%(cls)s.model, %(cls)s.queryset, or override " | ||||
|                 "%(cls)s.get_queryset()." % {"cls": self.__class__.__name__} | ||||
|             ) | ||||
|         ordering = self.get_ordering() | ||||
|         if ordering: | ||||
|             if isinstance(ordering, str): | ||||
|                 ordering = (ordering,) | ||||
|             queryset = queryset.order_by(*ordering) | ||||
|  | ||||
|         return queryset | ||||
|  | ||||
|     def get_ordering(self): | ||||
|         """Return the field or fields to use for ordering the queryset.""" | ||||
|         return self.ordering | ||||
|  | ||||
|     def paginate_queryset(self, queryset, page_size): | ||||
|         """Paginate the queryset, if needed.""" | ||||
|         paginator = self.get_paginator( | ||||
|             queryset, | ||||
|             page_size, | ||||
|             orphans=self.get_paginate_orphans(), | ||||
|             allow_empty_first_page=self.get_allow_empty(), | ||||
|         ) | ||||
|         page_kwarg = self.page_kwarg | ||||
|         page = self.kwargs.get(page_kwarg) or self.request.GET.get(page_kwarg) or 1 | ||||
|         try: | ||||
|             page_number = int(page) | ||||
|         except ValueError: | ||||
|             if page == "last": | ||||
|                 page_number = paginator.num_pages | ||||
|             else: | ||||
|                 raise Http404( | ||||
|                     _("Page is not “last”, nor can it be converted to an int.") | ||||
|                 ) | ||||
|         try: | ||||
|             page = paginator.page(page_number) | ||||
|             return (paginator, page, page.object_list, page.has_other_pages()) | ||||
|         except InvalidPage as e: | ||||
|             raise Http404( | ||||
|                 _("Invalid page (%(page_number)s): %(message)s") | ||||
|                 % {"page_number": page_number, "message": str(e)} | ||||
|             ) | ||||
|  | ||||
|     def get_paginate_by(self, queryset): | ||||
|         """ | ||||
|         Get the number of items to paginate by, or ``None`` for no pagination. | ||||
|         """ | ||||
|         return self.paginate_by | ||||
|  | ||||
|     def get_paginator( | ||||
|         self, queryset, per_page, orphans=0, allow_empty_first_page=True, **kwargs | ||||
|     ): | ||||
|         """Return an instance of the paginator for this view.""" | ||||
|         return self.paginator_class( | ||||
|             queryset, | ||||
|             per_page, | ||||
|             orphans=orphans, | ||||
|             allow_empty_first_page=allow_empty_first_page, | ||||
|             **kwargs, | ||||
|         ) | ||||
|  | ||||
|     def get_paginate_orphans(self): | ||||
|         """ | ||||
|         Return the maximum number of orphans extend the last page by when | ||||
|         paginating. | ||||
|         """ | ||||
|         return self.paginate_orphans | ||||
|  | ||||
|     def get_allow_empty(self): | ||||
|         """ | ||||
|         Return ``True`` if the view should display empty lists and ``False`` | ||||
|         if a 404 should be raised instead. | ||||
|         """ | ||||
|         return self.allow_empty | ||||
|  | ||||
|     def get_context_object_name(self, object_list): | ||||
|         """Get the name of the item to be used in the context.""" | ||||
|         if self.context_object_name: | ||||
|             return self.context_object_name | ||||
|         elif hasattr(object_list, "model"): | ||||
|             return "%s_list" % object_list.model._meta.model_name | ||||
|         else: | ||||
|             return None | ||||
|  | ||||
|     def get_context_data(self, *, object_list=None, **kwargs): | ||||
|         """Get the context for this view.""" | ||||
|         queryset = object_list if object_list is not None else self.object_list | ||||
|         page_size = self.get_paginate_by(queryset) | ||||
|         context_object_name = self.get_context_object_name(queryset) | ||||
|         if page_size: | ||||
|             paginator, page, queryset, is_paginated = self.paginate_queryset( | ||||
|                 queryset, page_size | ||||
|             ) | ||||
|             context = { | ||||
|                 "paginator": paginator, | ||||
|                 "page_obj": page, | ||||
|                 "is_paginated": is_paginated, | ||||
|                 "object_list": queryset, | ||||
|             } | ||||
|         else: | ||||
|             context = { | ||||
|                 "paginator": None, | ||||
|                 "page_obj": None, | ||||
|                 "is_paginated": False, | ||||
|                 "object_list": queryset, | ||||
|             } | ||||
|         if context_object_name is not None: | ||||
|             context[context_object_name] = queryset | ||||
|         context.update(kwargs) | ||||
|         return super().get_context_data(**context) | ||||
|  | ||||
|  | ||||
| class BaseListView(MultipleObjectMixin, View): | ||||
|     """A base view for displaying a list of objects.""" | ||||
|  | ||||
|     def get(self, request, *args, **kwargs): | ||||
|         self.object_list = self.get_queryset() | ||||
|         allow_empty = self.get_allow_empty() | ||||
|  | ||||
|         if not allow_empty: | ||||
|             # When pagination is enabled and object_list is a queryset, | ||||
|             # it's better to do a cheap query than to load the unpaginated | ||||
|             # queryset in memory. | ||||
|             if self.get_paginate_by(self.object_list) is not None and hasattr( | ||||
|                 self.object_list, "exists" | ||||
|             ): | ||||
|                 is_empty = not self.object_list.exists() | ||||
|             else: | ||||
|                 is_empty = not self.object_list | ||||
|             if is_empty: | ||||
|                 raise Http404( | ||||
|                     _("Empty list and “%(class_name)s.allow_empty” is False.") | ||||
|                     % { | ||||
|                         "class_name": self.__class__.__name__, | ||||
|                     } | ||||
|                 ) | ||||
|         context = self.get_context_data() | ||||
|         return self.render_to_response(context) | ||||
|  | ||||
|  | ||||
| class MultipleObjectTemplateResponseMixin(TemplateResponseMixin): | ||||
|     """Mixin for responding with a template and list of objects.""" | ||||
|  | ||||
|     template_name_suffix = "_list" | ||||
|  | ||||
|     def get_template_names(self): | ||||
|         """ | ||||
|         Return a list of template names to be used for the request. Must return | ||||
|         a list. May not be called if render_to_response is overridden. | ||||
|         """ | ||||
|         try: | ||||
|             names = super().get_template_names() | ||||
|         except ImproperlyConfigured: | ||||
|             # If template_name isn't specified, it's not a problem -- | ||||
|             # we just start with an empty list. | ||||
|             names = [] | ||||
|  | ||||
|         # If the list is a queryset, we'll invent a template name based on the | ||||
|         # app and model name. This name gets put at the end of the template | ||||
|         # name list so that user-supplied names override the automatically- | ||||
|         # generated ones. | ||||
|         if hasattr(self.object_list, "model"): | ||||
|             opts = self.object_list.model._meta | ||||
|             names.append( | ||||
|                 "%s/%s%s.html" | ||||
|                 % (opts.app_label, opts.model_name, self.template_name_suffix) | ||||
|             ) | ||||
|         elif not names: | ||||
|             raise ImproperlyConfigured( | ||||
|                 "%(cls)s requires either a 'template_name' attribute " | ||||
|                 "or a get_queryset() method that returns a QuerySet." | ||||
|                 % { | ||||
|                     "cls": self.__class__.__name__, | ||||
|                 } | ||||
|             ) | ||||
|         return names | ||||
|  | ||||
|  | ||||
| class ListView(MultipleObjectTemplateResponseMixin, BaseListView): | ||||
|     """ | ||||
|     Render some list of objects, set by `self.model` or `self.queryset`. | ||||
|     `self.queryset` can actually be any iterable of items, not just a queryset. | ||||
|     """ | ||||
							
								
								
									
										345
									
								
								srcs/.venv/lib/python3.11/site-packages/django/views/i18n.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										345
									
								
								srcs/.venv/lib/python3.11/site-packages/django/views/i18n.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,345 @@ | ||||
| import json | ||||
| import os | ||||
| import re | ||||
|  | ||||
| from django.apps import apps | ||||
| from django.conf import settings | ||||
| from django.http import HttpResponse, HttpResponseRedirect, JsonResponse | ||||
| from django.template import Context, Engine | ||||
| from django.urls import translate_url | ||||
| from django.utils.formats import get_format | ||||
| from django.utils.http import url_has_allowed_host_and_scheme | ||||
| from django.utils.translation import check_for_language, get_language | ||||
| from django.utils.translation.trans_real import DjangoTranslation | ||||
| from django.views.generic import View | ||||
|  | ||||
| LANGUAGE_QUERY_PARAMETER = "language" | ||||
|  | ||||
|  | ||||
| def set_language(request): | ||||
|     """ | ||||
|     Redirect to a given URL while setting the chosen language in the session | ||||
|     (if enabled) and in a cookie. The URL and the language code need to be | ||||
|     specified in the request parameters. | ||||
|  | ||||
|     Since this view changes how the user will see the rest of the site, it must | ||||
|     only be accessed as a POST request. If called as a GET request, it will | ||||
|     redirect to the page in the request (the 'next' parameter) without changing | ||||
|     any state. | ||||
|     """ | ||||
|     next_url = request.POST.get("next", request.GET.get("next")) | ||||
|     if ( | ||||
|         next_url or request.accepts("text/html") | ||||
|     ) and not url_has_allowed_host_and_scheme( | ||||
|         url=next_url, | ||||
|         allowed_hosts={request.get_host()}, | ||||
|         require_https=request.is_secure(), | ||||
|     ): | ||||
|         next_url = request.META.get("HTTP_REFERER") | ||||
|         if not url_has_allowed_host_and_scheme( | ||||
|             url=next_url, | ||||
|             allowed_hosts={request.get_host()}, | ||||
|             require_https=request.is_secure(), | ||||
|         ): | ||||
|             next_url = "/" | ||||
|     response = HttpResponseRedirect(next_url) if next_url else HttpResponse(status=204) | ||||
|     if request.method == "POST": | ||||
|         lang_code = request.POST.get(LANGUAGE_QUERY_PARAMETER) | ||||
|         if lang_code and check_for_language(lang_code): | ||||
|             if next_url: | ||||
|                 next_trans = translate_url(next_url, lang_code) | ||||
|                 if next_trans != next_url: | ||||
|                     response = HttpResponseRedirect(next_trans) | ||||
|             response.set_cookie( | ||||
|                 settings.LANGUAGE_COOKIE_NAME, | ||||
|                 lang_code, | ||||
|                 max_age=settings.LANGUAGE_COOKIE_AGE, | ||||
|                 path=settings.LANGUAGE_COOKIE_PATH, | ||||
|                 domain=settings.LANGUAGE_COOKIE_DOMAIN, | ||||
|                 secure=settings.LANGUAGE_COOKIE_SECURE, | ||||
|                 httponly=settings.LANGUAGE_COOKIE_HTTPONLY, | ||||
|                 samesite=settings.LANGUAGE_COOKIE_SAMESITE, | ||||
|             ) | ||||
|     return response | ||||
|  | ||||
|  | ||||
| def get_formats(): | ||||
|     """Return all formats strings required for i18n to work.""" | ||||
|     FORMAT_SETTINGS = ( | ||||
|         "DATE_FORMAT", | ||||
|         "DATETIME_FORMAT", | ||||
|         "TIME_FORMAT", | ||||
|         "YEAR_MONTH_FORMAT", | ||||
|         "MONTH_DAY_FORMAT", | ||||
|         "SHORT_DATE_FORMAT", | ||||
|         "SHORT_DATETIME_FORMAT", | ||||
|         "FIRST_DAY_OF_WEEK", | ||||
|         "DECIMAL_SEPARATOR", | ||||
|         "THOUSAND_SEPARATOR", | ||||
|         "NUMBER_GROUPING", | ||||
|         "DATE_INPUT_FORMATS", | ||||
|         "TIME_INPUT_FORMATS", | ||||
|         "DATETIME_INPUT_FORMATS", | ||||
|     ) | ||||
|     return {attr: get_format(attr) for attr in FORMAT_SETTINGS} | ||||
|  | ||||
|  | ||||
| js_catalog_template = r""" | ||||
| {% autoescape off %} | ||||
| 'use strict'; | ||||
| { | ||||
|   const globals = this; | ||||
|   const django = globals.django || (globals.django = {}); | ||||
|  | ||||
|   {% if plural %} | ||||
|   django.pluralidx = function(n) { | ||||
|     const v = {{ plural }}; | ||||
|     if (typeof v === 'boolean') { | ||||
|       return v ? 1 : 0; | ||||
|     } else { | ||||
|       return v; | ||||
|     } | ||||
|   }; | ||||
|   {% else %} | ||||
|   django.pluralidx = function(count) { return (count == 1) ? 0 : 1; }; | ||||
|   {% endif %} | ||||
|  | ||||
|   /* gettext library */ | ||||
|  | ||||
|   django.catalog = django.catalog || {}; | ||||
|   {% if catalog_str %} | ||||
|   const newcatalog = {{ catalog_str }}; | ||||
|   for (const key in newcatalog) { | ||||
|     django.catalog[key] = newcatalog[key]; | ||||
|   } | ||||
|   {% endif %} | ||||
|  | ||||
|   if (!django.jsi18n_initialized) { | ||||
|     django.gettext = function(msgid) { | ||||
|       const value = django.catalog[msgid]; | ||||
|       if (typeof value === 'undefined') { | ||||
|         return msgid; | ||||
|       } else { | ||||
|         return (typeof value === 'string') ? value : value[0]; | ||||
|       } | ||||
|     }; | ||||
|  | ||||
|     django.ngettext = function(singular, plural, count) { | ||||
|       const value = django.catalog[singular]; | ||||
|       if (typeof value === 'undefined') { | ||||
|         return (count == 1) ? singular : plural; | ||||
|       } else { | ||||
|         return value.constructor === Array ? value[django.pluralidx(count)] : value; | ||||
|       } | ||||
|     }; | ||||
|  | ||||
|     django.gettext_noop = function(msgid) { return msgid; }; | ||||
|  | ||||
|     django.pgettext = function(context, msgid) { | ||||
|       let value = django.gettext(context + '\x04' + msgid); | ||||
|       if (value.includes('\x04')) { | ||||
|         value = msgid; | ||||
|       } | ||||
|       return value; | ||||
|     }; | ||||
|  | ||||
|     django.npgettext = function(context, singular, plural, count) { | ||||
|       let value = django.ngettext(context + '\x04' + singular, context + '\x04' + plural, count); | ||||
|       if (value.includes('\x04')) { | ||||
|         value = django.ngettext(singular, plural, count); | ||||
|       } | ||||
|       return value; | ||||
|     }; | ||||
|  | ||||
|     django.interpolate = function(fmt, obj, named) { | ||||
|       if (named) { | ||||
|         return fmt.replace(/%\(\w+\)s/g, function(match){return String(obj[match.slice(2,-2)])}); | ||||
|       } else { | ||||
|         return fmt.replace(/%s/g, function(match){return String(obj.shift())}); | ||||
|       } | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     /* formatting library */ | ||||
|  | ||||
|     django.formats = {{ formats_str }}; | ||||
|  | ||||
|     django.get_format = function(format_type) { | ||||
|       const value = django.formats[format_type]; | ||||
|       if (typeof value === 'undefined') { | ||||
|         return format_type; | ||||
|       } else { | ||||
|         return value; | ||||
|       } | ||||
|     }; | ||||
|  | ||||
|     /* add to global namespace */ | ||||
|     globals.pluralidx = django.pluralidx; | ||||
|     globals.gettext = django.gettext; | ||||
|     globals.ngettext = django.ngettext; | ||||
|     globals.gettext_noop = django.gettext_noop; | ||||
|     globals.pgettext = django.pgettext; | ||||
|     globals.npgettext = django.npgettext; | ||||
|     globals.interpolate = django.interpolate; | ||||
|     globals.get_format = django.get_format; | ||||
|  | ||||
|     django.jsi18n_initialized = true; | ||||
|   } | ||||
| }; | ||||
| {% endautoescape %} | ||||
| """  # NOQA | ||||
|  | ||||
|  | ||||
| class JavaScriptCatalog(View): | ||||
|     """ | ||||
|     Return the selected language catalog as a JavaScript library. | ||||
|  | ||||
|     Receive the list of packages to check for translations in the `packages` | ||||
|     kwarg either from the extra dictionary passed to the path() function or as | ||||
|     a plus-sign delimited string from the request. Default is 'django.conf'. | ||||
|  | ||||
|     You can override the gettext domain for this view, but usually you don't | ||||
|     want to do that as JavaScript messages go to the djangojs domain. This | ||||
|     might be needed if you deliver your JavaScript source from Django templates. | ||||
|     """ | ||||
|  | ||||
|     domain = "djangojs" | ||||
|     packages = None | ||||
|  | ||||
|     def get(self, request, *args, **kwargs): | ||||
|         locale = get_language() | ||||
|         domain = kwargs.get("domain", self.domain) | ||||
|         # If packages are not provided, default to all installed packages, as | ||||
|         # DjangoTranslation without localedirs harvests them all. | ||||
|         packages = kwargs.get("packages", "") | ||||
|         packages = packages.split("+") if packages else self.packages | ||||
|         paths = self.get_paths(packages) if packages else None | ||||
|         self.translation = DjangoTranslation(locale, domain=domain, localedirs=paths) | ||||
|         context = self.get_context_data(**kwargs) | ||||
|         return self.render_to_response(context) | ||||
|  | ||||
|     def get_paths(self, packages): | ||||
|         allowable_packages = { | ||||
|             app_config.name: app_config for app_config in apps.get_app_configs() | ||||
|         } | ||||
|         app_configs = [ | ||||
|             allowable_packages[p] for p in packages if p in allowable_packages | ||||
|         ] | ||||
|         if len(app_configs) < len(packages): | ||||
|             excluded = [p for p in packages if p not in allowable_packages] | ||||
|             raise ValueError( | ||||
|                 "Invalid package(s) provided to JavaScriptCatalog: %s" | ||||
|                 % ",".join(excluded) | ||||
|             ) | ||||
|         # paths of requested packages | ||||
|         return [os.path.join(app.path, "locale") for app in app_configs] | ||||
|  | ||||
|     @property | ||||
|     def _num_plurals(self): | ||||
|         """ | ||||
|         Return the number of plurals for this catalog language, or 2 if no | ||||
|         plural string is available. | ||||
|         """ | ||||
|         match = re.search(r"nplurals=\s*(\d+)", self._plural_string or "") | ||||
|         if match: | ||||
|             return int(match[1]) | ||||
|         return 2 | ||||
|  | ||||
|     @property | ||||
|     def _plural_string(self): | ||||
|         """ | ||||
|         Return the plural string (including nplurals) for this catalog language, | ||||
|         or None if no plural string is available. | ||||
|         """ | ||||
|         if "" in self.translation._catalog: | ||||
|             for line in self.translation._catalog[""].split("\n"): | ||||
|                 if line.startswith("Plural-Forms:"): | ||||
|                     return line.split(":", 1)[1].strip() | ||||
|         return None | ||||
|  | ||||
|     def get_plural(self): | ||||
|         plural = self._plural_string | ||||
|         if plural is not None: | ||||
|             # This should be a compiled function of a typical plural-form: | ||||
|             # Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : | ||||
|             #               n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2; | ||||
|             plural = [ | ||||
|                 el.strip() | ||||
|                 for el in plural.split(";") | ||||
|                 if el.strip().startswith("plural=") | ||||
|             ][0].split("=", 1)[1] | ||||
|         return plural | ||||
|  | ||||
|     def get_catalog(self): | ||||
|         pdict = {} | ||||
|         catalog = {} | ||||
|         translation = self.translation | ||||
|         seen_keys = set() | ||||
|         while True: | ||||
|             for key, value in translation._catalog.items(): | ||||
|                 if key == "" or key in seen_keys: | ||||
|                     continue | ||||
|                 if isinstance(key, str): | ||||
|                     catalog[key] = value | ||||
|                 elif isinstance(key, tuple): | ||||
|                     msgid, cnt = key | ||||
|                     pdict.setdefault(msgid, {})[cnt] = value | ||||
|                 else: | ||||
|                     raise TypeError(key) | ||||
|                 seen_keys.add(key) | ||||
|             if translation._fallback: | ||||
|                 translation = translation._fallback | ||||
|             else: | ||||
|                 break | ||||
|  | ||||
|         num_plurals = self._num_plurals | ||||
|         for k, v in pdict.items(): | ||||
|             catalog[k] = [v.get(i, "") for i in range(num_plurals)] | ||||
|         return catalog | ||||
|  | ||||
|     def get_context_data(self, **kwargs): | ||||
|         return { | ||||
|             "catalog": self.get_catalog(), | ||||
|             "formats": get_formats(), | ||||
|             "plural": self.get_plural(), | ||||
|         } | ||||
|  | ||||
|     def render_to_response(self, context, **response_kwargs): | ||||
|         def indent(s): | ||||
|             return s.replace("\n", "\n  ") | ||||
|  | ||||
|         template = Engine().from_string(js_catalog_template) | ||||
|         context["catalog_str"] = ( | ||||
|             indent(json.dumps(context["catalog"], sort_keys=True, indent=2)) | ||||
|             if context["catalog"] | ||||
|             else None | ||||
|         ) | ||||
|         context["formats_str"] = indent( | ||||
|             json.dumps(context["formats"], sort_keys=True, indent=2) | ||||
|         ) | ||||
|  | ||||
|         return HttpResponse( | ||||
|             template.render(Context(context)), 'text/javascript; charset="utf-8"' | ||||
|         ) | ||||
|  | ||||
|  | ||||
| class JSONCatalog(JavaScriptCatalog): | ||||
|     """ | ||||
|     Return the selected language catalog as a JSON object. | ||||
|  | ||||
|     Receive the same parameters as JavaScriptCatalog and return a response | ||||
|     with a JSON object of the following format: | ||||
|  | ||||
|         { | ||||
|             "catalog": { | ||||
|                 # Translations catalog | ||||
|             }, | ||||
|             "formats": { | ||||
|                 # Language formats for date, time, etc. | ||||
|             }, | ||||
|             "plural": '...'  # Expression for plural forms, or null. | ||||
|         } | ||||
|     """ | ||||
|  | ||||
|     def render_to_response(self, context, **response_kwargs): | ||||
|         return JsonResponse(context) | ||||
							
								
								
									
										132
									
								
								srcs/.venv/lib/python3.11/site-packages/django/views/static.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								srcs/.venv/lib/python3.11/site-packages/django/views/static.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,132 @@ | ||||
| """ | ||||
| Views and functions for serving static files. These are only to be used | ||||
| during development, and SHOULD NOT be used in a production setting. | ||||
| """ | ||||
| import mimetypes | ||||
| import posixpath | ||||
| from pathlib import Path | ||||
|  | ||||
| from django.http import FileResponse, Http404, HttpResponse, HttpResponseNotModified | ||||
| from django.template import Context, Engine, TemplateDoesNotExist, loader | ||||
| from django.utils._os import safe_join | ||||
| from django.utils.http import http_date, parse_http_date | ||||
| from django.utils.translation import gettext as _ | ||||
| from django.utils.translation import gettext_lazy | ||||
|  | ||||
|  | ||||
| def serve(request, path, document_root=None, show_indexes=False): | ||||
|     """ | ||||
|     Serve static files below a given point in the directory structure. | ||||
|  | ||||
|     To use, put a URL pattern such as:: | ||||
|  | ||||
|         from django.views.static import serve | ||||
|  | ||||
|         path('<path:path>', serve, {'document_root': '/path/to/my/files/'}) | ||||
|  | ||||
|     in your URLconf. You must provide the ``document_root`` param. You may | ||||
|     also set ``show_indexes`` to ``True`` if you'd like to serve a basic index | ||||
|     of the directory.  This index view will use the template hardcoded below, | ||||
|     but if you'd like to override it, you can create a template called | ||||
|     ``static/directory_index.html``. | ||||
|     """ | ||||
|     path = posixpath.normpath(path).lstrip("/") | ||||
|     fullpath = Path(safe_join(document_root, path)) | ||||
|     if fullpath.is_dir(): | ||||
|         if show_indexes: | ||||
|             return directory_index(path, fullpath) | ||||
|         raise Http404(_("Directory indexes are not allowed here.")) | ||||
|     if not fullpath.exists(): | ||||
|         raise Http404(_("“%(path)s” does not exist") % {"path": fullpath}) | ||||
|     # Respect the If-Modified-Since header. | ||||
|     statobj = fullpath.stat() | ||||
|     if not was_modified_since( | ||||
|         request.META.get("HTTP_IF_MODIFIED_SINCE"), statobj.st_mtime | ||||
|     ): | ||||
|         return HttpResponseNotModified() | ||||
|     content_type, encoding = mimetypes.guess_type(str(fullpath)) | ||||
|     content_type = content_type or "application/octet-stream" | ||||
|     response = FileResponse(fullpath.open("rb"), content_type=content_type) | ||||
|     response.headers["Last-Modified"] = http_date(statobj.st_mtime) | ||||
|     if encoding: | ||||
|         response.headers["Content-Encoding"] = encoding | ||||
|     return response | ||||
|  | ||||
|  | ||||
| DEFAULT_DIRECTORY_INDEX_TEMPLATE = """ | ||||
| {% load i18n %} | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
|   <head> | ||||
|     <meta http-equiv="Content-type" content="text/html; charset=utf-8"> | ||||
|     <meta http-equiv="Content-Language" content="en-us"> | ||||
|     <meta name="robots" content="NONE,NOARCHIVE"> | ||||
|     <title>{% blocktranslate %}Index of {{ directory }}{% endblocktranslate %}</title> | ||||
|   </head> | ||||
|   <body> | ||||
|     <h1>{% blocktranslate %}Index of {{ directory }}{% endblocktranslate %}</h1> | ||||
|     <ul> | ||||
|       {% if directory != "/" %} | ||||
|       <li><a href="../">../</a></li> | ||||
|       {% endif %} | ||||
|       {% for f in file_list %} | ||||
|       <li><a href="{{ f|urlencode }}">{{ f }}</a></li> | ||||
|       {% endfor %} | ||||
|     </ul> | ||||
|   </body> | ||||
| </html> | ||||
| """ | ||||
| template_translatable = gettext_lazy("Index of %(directory)s") | ||||
|  | ||||
|  | ||||
| def directory_index(path, fullpath): | ||||
|     try: | ||||
|         t = loader.select_template( | ||||
|             [ | ||||
|                 "static/directory_index.html", | ||||
|                 "static/directory_index", | ||||
|             ] | ||||
|         ) | ||||
|     except TemplateDoesNotExist: | ||||
|         t = Engine(libraries={"i18n": "django.templatetags.i18n"}).from_string( | ||||
|             DEFAULT_DIRECTORY_INDEX_TEMPLATE | ||||
|         ) | ||||
|         c = Context() | ||||
|     else: | ||||
|         c = {} | ||||
|     files = [] | ||||
|     for f in fullpath.iterdir(): | ||||
|         if not f.name.startswith("."): | ||||
|             url = str(f.relative_to(fullpath)) | ||||
|             if f.is_dir(): | ||||
|                 url += "/" | ||||
|             files.append(url) | ||||
|     c.update( | ||||
|         { | ||||
|             "directory": path + "/", | ||||
|             "file_list": files, | ||||
|         } | ||||
|     ) | ||||
|     return HttpResponse(t.render(c)) | ||||
|  | ||||
|  | ||||
| def was_modified_since(header=None, mtime=0): | ||||
|     """ | ||||
|     Was something modified since the user last downloaded it? | ||||
|  | ||||
|     header | ||||
|       This is the value of the If-Modified-Since header.  If this is None, | ||||
|       I'll just return True. | ||||
|  | ||||
|     mtime | ||||
|       This is the modification time of the item we're talking about. | ||||
|     """ | ||||
|     try: | ||||
|         if header is None: | ||||
|             raise ValueError | ||||
|         header_mtime = parse_http_date(header) | ||||
|         if int(mtime) > header_mtime: | ||||
|             raise ValueError | ||||
|     except (ValueError, OverflowError): | ||||
|         return True | ||||
|     return False | ||||
| @ -0,0 +1,253 @@ | ||||
| {% load i18n %} | ||||
| <!doctype html> | ||||
| {% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %} | ||||
| <html lang="{{ LANGUAGE_CODE|default:'en-us' }}" dir="{{ LANGUAGE_BIDI|yesno:'rtl,ltr,auto' }}"> | ||||
|     <head> | ||||
|         <meta charset="utf-8"> | ||||
|         <title>{% translate "The install worked successfully! Congratulations!" %}</title> | ||||
|         <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|         <style> | ||||
|           html { | ||||
|             line-height: 1.15; | ||||
|           } | ||||
|           a { | ||||
|             color: #19865C; | ||||
|           } | ||||
|           header { | ||||
|             border-bottom: 1px solid #efefef; | ||||
|           } | ||||
|           body { | ||||
|             max-width: 960px; | ||||
|             color: #525252; | ||||
|             font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", ui-system, sans-serif; | ||||
|             margin: 0 auto; | ||||
|           } | ||||
|           main { | ||||
|             text-align: center; | ||||
|           } | ||||
|           h1, h2, h3, h4, h5, p, ul { | ||||
|             padding: 0; | ||||
|             margin: 0; | ||||
|             font-weight: 400; | ||||
|           } | ||||
|           header { | ||||
|             display: grid; | ||||
|             grid-template-columns: auto auto; | ||||
|             align-items: self-end; | ||||
|             justify-content: space-between; | ||||
|             gap: 7px; | ||||
|             padding-top: 20px; | ||||
|             padding-bottom: 10px; | ||||
|           } | ||||
|           .logo { | ||||
|             font-weight: 700; | ||||
|             font-size: 1.375rem; | ||||
|             text-decoration: none; | ||||
|           } | ||||
|           .figure { | ||||
|             margin-top: 19vh; | ||||
|             max-width: 265px; | ||||
|             position: relative; | ||||
|             z-index: -9; | ||||
|             overflow: visible; | ||||
|           } | ||||
|           .exhaust__line { | ||||
|             animation: thrust 70ms 100 ease-in-out alternate; | ||||
|           } | ||||
|           .smoke { | ||||
|             animation: smoke .1s 70 ease-in-out alternate; | ||||
|           } | ||||
|           @keyframes smoke { | ||||
|             0% { | ||||
|               transform: translate3d(-5px, 0, 0); | ||||
|             } | ||||
|             100% { | ||||
|               transform: translate3d(5px, 0, 0); | ||||
|             } | ||||
|           } | ||||
|           .flame { | ||||
|             animation: burnInner2 .1s 70 ease-in-out alternate; | ||||
|           } | ||||
|           @keyframes burnInner2 { | ||||
|             0% { | ||||
|               transform: translate3d(0, 0, 0); | ||||
|             } | ||||
|             100% { | ||||
|               transform: translate3d(0, 3px, 0); | ||||
|             } | ||||
|           } | ||||
|           @keyframes thrust { | ||||
|             0% { | ||||
|               opacity: 1; | ||||
|             } | ||||
|             100% { | ||||
|               opacity: .5; | ||||
|             } | ||||
|           } | ||||
|           @media (prefers-reduced-motion: reduce) { | ||||
|             .exhaust__line, | ||||
|             .smoke, | ||||
|             .flame { | ||||
|               animation: none; | ||||
|             } | ||||
|           } | ||||
|           h1 { | ||||
|             font-size: 1.375rem; | ||||
|             max-width: 32rem; | ||||
|             margin: 5px auto 0; | ||||
|           } | ||||
|           main p { | ||||
|             line-height: 1.25; | ||||
|             max-width: 26rem; | ||||
|             margin: 15px auto 0; | ||||
|           } | ||||
|           footer { | ||||
|             display: grid; | ||||
|             grid-template-columns: 1fr 1fr 1fr; | ||||
|             gap: 5px; | ||||
|             padding: 25px 0; | ||||
|             position: fixed; | ||||
|             box-sizing: border-box; | ||||
|             left: 50%; | ||||
|             bottom: 0; | ||||
|             width: 960px; | ||||
|             transform: translateX(-50%); | ||||
|             transform-style: preserve-3d; | ||||
|             border-top: 1px solid #efefef; | ||||
|           } | ||||
|           .option { | ||||
|             display: grid; | ||||
|             grid-template-columns: min-content 1fr; | ||||
|             gap: 10px; | ||||
|             box-sizing: border-box; | ||||
|             text-decoration: none; | ||||
|           } | ||||
|           .option svg { | ||||
|             width: 1.5rem; | ||||
|             height: 1.5rem; | ||||
|             fill: gray; | ||||
|             border: 1px solid #d6d6d6; | ||||
|             padding: 5px; | ||||
|             border-radius: 100%; | ||||
|           } | ||||
|           .option p { | ||||
|             font-weight: 300; | ||||
|             line-height: 1.25; | ||||
|             color: #525252; | ||||
|             display: table; | ||||
|           } | ||||
|           .option .option__heading { | ||||
|             color: #19865C; | ||||
|             font-size: 1.25rem; | ||||
|             font-weight: 400; | ||||
|           } | ||||
|           @media (max-width: 996px) { | ||||
|             body, footer { | ||||
|               max-width: 780px; | ||||
|             } | ||||
|           } | ||||
|           @media (max-width: 800px) { | ||||
|             footer { | ||||
|               height: 100%; | ||||
|               grid-template-columns: 1fr; | ||||
|               gap: 60px; | ||||
|               position: relative; | ||||
|               padding: 25px; | ||||
|             } | ||||
|             .figure { | ||||
|               margin-top: 10px; | ||||
|             } | ||||
|             main { | ||||
|               padding: 0 25px; | ||||
|             } | ||||
|             main h1 { | ||||
|               font-size: 1.25rem; | ||||
|             } | ||||
|             header { | ||||
|               grid-template-columns: 1fr; | ||||
|               padding-left: 20px; | ||||
|               padding-right: 20px; | ||||
|             } | ||||
|             footer { | ||||
|               width: 100%; | ||||
|               margin-top: 50px; | ||||
|             } | ||||
|           } | ||||
|           @media (min-width: 801px) and (max-height: 730px) { | ||||
|             .figure { | ||||
|               margin-top: 80px; | ||||
|             } | ||||
|           } | ||||
|           @media (min-width: 801px) and (max-height: 600px) { | ||||
|             footer { | ||||
|               position: relative; | ||||
|               margin: 135px auto 0; | ||||
|             } | ||||
|             .figure { | ||||
|               margin-top: 50px; | ||||
|             } | ||||
|           } | ||||
|           .sr-only { | ||||
|             clip: rect(1px, 1px, 1px, 1px); | ||||
|             clip-path: inset(50%); | ||||
|             height: 1px; | ||||
|             overflow: hidden; | ||||
|             position: absolute; | ||||
|             white-space: nowrap; | ||||
|             width: 1px; | ||||
|           } | ||||
|         </style> | ||||
|     </head> | ||||
|     <body> | ||||
|       <header> | ||||
|           <a class="logo" href="https://www.djangoproject.com/" target="_blank" rel="noopener"> | ||||
|             django | ||||
|           </a> | ||||
|           <p>{% blocktranslate %}View <a href="https://docs.djangoproject.com/en/{{ version }}/releases/" target="_blank" rel="noopener">release notes</a> for Django {{ version }}{% endblocktranslate %}</p> | ||||
|       </header> | ||||
|       <main> | ||||
|         <svg class="figure" viewBox="0 0 508 268" aria-hidden="true"> | ||||
|           <path d="M305.2 156.6c0 4.6-.5 9-1.6 13.2-2.5-4.4-5.6-8.4-9.2-12-4.6-4.6-10-8.4-16-11.2 2.8-11.2 4.5-22.9 5-34.6 1.8 1.4 3.5 2.9 5 4.5 10.5 10.3 16.8 24.5 16.8 40.1zm-75-10c-6 2.8-11.4 6.6-16 11.2-3.5 3.6-6.6 7.6-9.1 12-1-4.3-1.6-8.7-1.6-13.2 0-15.7 6.3-29.9 16.6-40.1 1.6-1.6 3.3-3.1 5.1-4.5.6 11.8 2.2 23.4 5 34.6z" fill="#2E3B39" fill-rule="nonzero"/> | ||||
|           <path d="M282.981 152.6c16.125-48.1 6.375-104-29.25-142.6-35.625 38.5-45.25 94.5-29.25 142.6h58.5z" stroke="#FFF" stroke-width="3.396" fill="#6DDCBD"/> | ||||
|           <path d="M271 29.7c-4.4-10.6-9.9-20.6-16.6-29.7-6.7 9-12.2 19-16.6 29.7H271z" stroke="#FFF" stroke-width="3" fill="#2E3B39"/> | ||||
|           <circle fill="#FFF" cx="254.3" cy="76.8" r="15.5"/> | ||||
|           <circle stroke="#FFF" stroke-width="7" fill="#6DDCBD" cx="254.3" cy="76.8" r="12.2"/> | ||||
|           <path class="smoke" d="M507.812 234.24c0-2.16-.632-4.32-1.58-6.24-3.318-6.72-11.85-11.52-21.804-11.52-1.106 0-2.212.12-3.318.24-.474-11.52-12.956-20.76-28.282-20.76-3.318 0-6.636.48-9.638 1.32-4.74-6.72-14.062-11.28-24.806-11.28-.79 0-1.58 0-2.37.12-.79 0-1.58-.12-2.37-.12-10.744 0-20.066 4.56-24.806 11.28a35.326 35.326 0 00-9.638-1.32c-15.642 0-28.282 9.6-28.282 21.48 0 1.32.158 2.76.474 3.96a26.09 26.09 0 00-4.424-.36c-8.058 0-15.01 3.12-19.118 7.8-3.476-1.68-7.742-2.76-12.324-2.76-12.008 0-21.804 7.08-22.752 15.96h-.158c-9.322 0-17.38 4.32-20.856 10.44-4.108-3.6-10.27-6-17.222-6h-1.264c-6.794 0-12.956 2.28-17.222 6-3.476-6.12-11.534-10.44-20.856-10.44h-.158c-.948-9-10.744-15.96-22.752-15.96-4.582 0-8.69.96-12.324 2.76-4.108-4.68-11.06-7.8-19.118-7.8-1.422 0-3.002.12-4.424.36.316-1.32.474-2.64.474-3.96 0-11.88-12.64-21.48-28.282-21.48-3.318 0-6.636.48-9.638 1.32-4.74-6.72-14.062-11.28-24.806-11.28-.79 0-1.58 0-2.37.12-.79 0-1.58-.12-2.37-.12-10.744 0-20.066 4.56-24.806 11.28a35.326 35.326 0 00-9.638-1.32c-15.326 0-27.808 9.24-28.282 20.76-1.106-.12-2.212-.24-3.318-.24-9.954 0-18.486 4.8-21.804 11.52-.948 1.92-1.58 4.08-1.58 6.24 0 4.8 2.528 9.12 6.636 12.36-.79 1.44-1.264 3.12-1.264 4.8 0 7.2 7.742 13.08 17.222 13.08h462.15c9.48 0 17.222-5.88 17.222-13.08 0-1.68-.474-3.36-1.264-4.8 4.582-3.24 7.11-7.56 7.11-12.36z" fill="#E6E9EE"/> | ||||
|           <path fill="#6DDCBD" d="M239 152h30v8h-30z"/> | ||||
|           <path class="exhaust__line" fill="#E6E9EE" d="M250 172h7v90h-7z"/> | ||||
|           <path class="flame" d="M250.27 178.834l-5.32-8.93s-2.47-5.7 3.458-6.118h10.26s6.232.266 3.306 6.194l-5.244 8.93s-3.23 4.37-6.46 0v-.076z" fill="#AA2247"/> | ||||
|         </svg> | ||||
|         <h1>{% translate "The install worked successfully! Congratulations!" %}</h1> | ||||
|         <p>{% blocktranslate %}You are seeing this page because <a href="https://docs.djangoproject.com/en/{{ version }}/ref/settings/#debug" target="_blank" rel="noopener">DEBUG=True</a> is in your settings file and you have not configured any URLs.{% endblocktranslate %}</p> | ||||
|       </main> | ||||
|       <footer> | ||||
|         <a class="option" href="https://docs.djangoproject.com/en/{{ version }}/" target="_blank" rel="noopener"> | ||||
|           <svg viewBox="0 0 24 24" aria-hidden="true"> | ||||
|             <path d="M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z"></path> | ||||
|           </svg> | ||||
|           <p> | ||||
|             <span class="option__heading">{% translate "Django Documentation" %}</span><span class="sr-only">.</span><br> | ||||
|             {% translate 'Topics, references, & how-to’s' %} | ||||
|           </p> | ||||
|         </a> | ||||
|         <a class="option" href="https://docs.djangoproject.com/en/{{ version }}/intro/tutorial01/" target="_blank" rel="noopener"> | ||||
|           <svg viewBox="0 0 24 24" aria-hidden="true"> | ||||
|             <path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"></path> | ||||
|           </svg> | ||||
|           <p> | ||||
|             <span class="option__heading">{% translate "Tutorial: A Polling App" %}</span><span class="sr-only">.</span><br> | ||||
|             {% translate "Get started with Django" %} | ||||
|           </p> | ||||
|         </a> | ||||
|         <a class="option" href="https://www.djangoproject.com/community/" target="_blank" rel="noopener"> | ||||
|           <svg viewBox="0 0 24 24" aria-hidden="true"> | ||||
|             <path d="M16.5 13c-1.2 0-3.07.34-4.5 1-1.43-.67-3.3-1-4.5-1C5.33 13 1 14.08 1 16.25V19h22v-2.75c0-2.17-4.33-3.25-6.5-3.25zm-4 4.5h-10v-1.25c0-.54 2.56-1.75 5-1.75s5 1.21 5 1.75v1.25zm9 0H14v-1.25c0-.46-.2-.86-.52-1.22.88-.3 1.96-.53 3.02-.53 2.44 0 5 1.21 5 1.75v1.25zM7.5 12c1.93 0 3.5-1.57 3.5-3.5S9.43 5 7.5 5 4 6.57 4 8.5 5.57 12 7.5 12zm0-5.5c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm9 5.5c1.93 0 3.5-1.57 3.5-3.5S18.43 5 16.5 5 13 6.57 13 8.5s1.57 3.5 3.5 3.5zm0-5.5c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2z"></path> | ||||
|           </svg> | ||||
|           <p> | ||||
|             <span class="option__heading">{% translate "Django Community" %}</span><span class="sr-only">.</span><br> | ||||
|             {% translate "Connect, get help, or contribute" %} | ||||
|           </p> | ||||
|         </a> | ||||
|       </footer> | ||||
|     </body> | ||||
| </html> | ||||
| @ -0,0 +1,82 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|   <meta http-equiv="content-type" content="text/html; charset=utf-8"> | ||||
|   <title>Page not found at {{ request.path_info }}</title> | ||||
|   <meta name="robots" content="NONE,NOARCHIVE"> | ||||
|   <style type="text/css"> | ||||
|     html * { padding:0; margin:0; } | ||||
|     body * { padding:10px 20px; } | ||||
|     body * * { padding:0; } | ||||
|     body { font:small sans-serif; background:#eee; color:#000; } | ||||
|     body>div { border-bottom:1px solid #ddd; } | ||||
|     h1 { font-weight:normal; margin-bottom:.4em; } | ||||
|     h1 span { font-size:60%; color:#666; font-weight:normal; } | ||||
|     table { border:none; border-collapse: collapse; width:100%; } | ||||
|     td, th { vertical-align:top; padding:2px 3px; } | ||||
|     th { width:12em; text-align:right; color:#666; padding-right:.5em; } | ||||
|     #info { background:#f6f6f6; } | ||||
|     #info ol { margin: 0.5em 4em; } | ||||
|     #info ol li { font-family: monospace; } | ||||
|     #summary { background: #ffc; } | ||||
|     #explanation { background:#eee; border-bottom: 0px none; } | ||||
|     pre.exception_value { font-family: sans-serif; color: #575757; font-size: 1.5em; margin: 10px 0 10px 0; } | ||||
|   </style> | ||||
| </head> | ||||
| <body> | ||||
|   <div id="summary"> | ||||
|     <h1>Page not found <span>(404)</span></h1> | ||||
|     {% if reason and resolved %}<pre class="exception_value">{{ reason }}</pre>{% endif %} | ||||
|     <table class="meta"> | ||||
|       <tr> | ||||
|         <th>Request Method:</th> | ||||
|         <td>{{ request.META.REQUEST_METHOD }}</td> | ||||
|       </tr> | ||||
|       <tr> | ||||
|         <th>Request URL:</th> | ||||
|         <td>{{ request.build_absolute_uri }}</td> | ||||
|       </tr> | ||||
|       {% if raising_view_name %} | ||||
|       <tr> | ||||
|         <th>Raised by:</th> | ||||
|         <td>{{ raising_view_name }}</td> | ||||
|       </tr> | ||||
|       {% endif %} | ||||
|     </table> | ||||
|   </div> | ||||
|   <div id="info"> | ||||
|     {% if urlpatterns %} | ||||
|       <p> | ||||
|       Using the URLconf defined in <code>{{ urlconf }}</code>, | ||||
|       Django tried these URL patterns, in this order: | ||||
|       </p> | ||||
|       <ol> | ||||
|         {% for pattern in urlpatterns %} | ||||
|           <li> | ||||
|             {% for pat in pattern %} | ||||
|                 {{ pat.pattern }} | ||||
|                 {% if forloop.last and pat.name %}[name='{{ pat.name }}']{% endif %} | ||||
|             {% endfor %} | ||||
|           </li> | ||||
|         {% endfor %} | ||||
|       </ol> | ||||
|       <p> | ||||
|         {% if request_path %} | ||||
|           The current path, <code>{{ request_path }}</code>, | ||||
|         {% else %} | ||||
|           The empty path | ||||
|         {% endif %} | ||||
|         {% if resolved %}matched the last one.{% else %}didn’t match any of these.{% endif %} | ||||
|       </p> | ||||
|     {% endif %} | ||||
|   </div> | ||||
|  | ||||
|   <div id="explanation"> | ||||
|     <p> | ||||
|       You’re seeing this error because you have <code>DEBUG = True</code> in | ||||
|       your Django settings file. Change that to <code>False</code>, and Django | ||||
|       will display a standard 404 page. | ||||
|     </p> | ||||
|   </div> | ||||
| </body> | ||||
| </html> | ||||
| @ -0,0 +1,491 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|   <meta http-equiv="content-type" content="text/html; charset=utf-8"> | ||||
|   <meta name="robots" content="NONE,NOARCHIVE"> | ||||
|   <title>{% if exception_type %}{{ exception_type }}{% else %}Report{% endif %} | ||||
|          {% if request %} at {{ request.path_info }}{% endif %}</title> | ||||
|   <style type="text/css"> | ||||
|     html * { padding:0; margin:0; } | ||||
|     body * { padding:10px 20px; } | ||||
|     body * * { padding:0; } | ||||
|     body { font:small sans-serif; background-color:#fff; color:#000; } | ||||
|     body>div { border-bottom:1px solid #ddd; } | ||||
|     h1 { font-weight:normal; } | ||||
|     h2 { margin-bottom:.8em; } | ||||
|     h3 { margin:1em 0 .5em 0; } | ||||
|     h4 { margin:0 0 .5em 0; font-weight: normal; } | ||||
|     code, pre { font-size: 100%; white-space: pre-wrap; word-break: break-word; } | ||||
|     summary { cursor: pointer; } | ||||
|     table { border:1px solid #ccc; border-collapse: collapse; width:100%; background:white; } | ||||
|     tbody td, tbody th { vertical-align:top; padding:2px 3px; } | ||||
|     thead th { | ||||
|       padding:1px 6px 1px 3px; background:#fefefe; text-align:left; | ||||
|       font-weight:normal; font-size:11px; border:1px solid #ddd; | ||||
|     } | ||||
|     tbody th { width:12em; text-align:right; color:#666; padding-right:.5em; } | ||||
|     table.vars { margin:5px 10px 2px 40px; width: auto; } | ||||
|     table.vars td, table.req td { font-family:monospace; } | ||||
|     table td.code { width:100%; } | ||||
|     table td.code pre { overflow:hidden; } | ||||
|     table.source th { color:#666; } | ||||
|     table.source td { font-family:monospace; white-space:pre; border-bottom:1px solid #eee; } | ||||
|     ul.traceback { list-style-type:none; color: #222; } | ||||
|     ul.traceback li.cause { word-break: break-word; } | ||||
|     ul.traceback li.frame { padding-bottom:1em; color:#4f4f4f; } | ||||
|     ul.traceback li.user { background-color:#e0e0e0; color:#000 } | ||||
|     div.context { padding:10px 0; overflow:hidden; } | ||||
|     div.context ol { padding-left:30px; margin:0 10px; list-style-position: inside; } | ||||
|     div.context ol li { font-family:monospace; white-space:pre; color:#777; cursor:pointer; padding-left: 2px; } | ||||
|     div.context ol li pre { display:inline; } | ||||
|     div.context ol.context-line li { color:#464646; background-color:#dfdfdf; padding: 3px 2px; } | ||||
|     div.context ol.context-line li span { position:absolute; right:32px; } | ||||
|     .user div.context ol.context-line li { background-color:#bbb; color:#000; } | ||||
|     .user div.context ol li { color:#666; } | ||||
|     div.commands, summary.commands { margin-left: 40px; } | ||||
|     div.commands a, summary.commands { color:#555; text-decoration:none; } | ||||
|     .user div.commands a { color: black; } | ||||
|     #summary { background: #ffc; } | ||||
|     #summary h2 { font-weight: normal; color: #666; } | ||||
|     #explanation { background:#eee; } | ||||
|     #template, #template-not-exist { background:#f6f6f6; } | ||||
|     #template-not-exist ul { margin: 0 0 10px 20px; } | ||||
|     #template-not-exist .postmortem-section { margin-bottom: 3px; } | ||||
|     #unicode-hint { background:#eee; } | ||||
|     #traceback { background:#eee; } | ||||
|     #requestinfo { background:#f6f6f6; padding-left:120px; } | ||||
|     #summary table { border:none; background:transparent; } | ||||
|     #requestinfo h2, #requestinfo h3 { position:relative; margin-left:-100px; } | ||||
|     #requestinfo h3 { margin-bottom:-1em; } | ||||
|     .error { background: #ffc; } | ||||
|     .specific { color:#cc3300; font-weight:bold; } | ||||
|     h2 span.commands { font-size:.7em; font-weight:normal; } | ||||
|     span.commands a:link {color:#5E5694;} | ||||
|     pre.exception_value { font-family: sans-serif; color: #575757; font-size: 1.5em; margin: 10px 0 10px 0; } | ||||
|     .append-bottom { margin-bottom: 10px; } | ||||
|     .fname { user-select: all; } | ||||
|   </style> | ||||
|   {% if not is_email %} | ||||
|   <script> | ||||
|     function hideAll(elems) { | ||||
|       for (var e = 0; e < elems.length; e++) { | ||||
|         elems[e].style.display = 'none'; | ||||
|       } | ||||
|     } | ||||
|     window.onload = function() { | ||||
|       hideAll(document.querySelectorAll('ol.pre-context')); | ||||
|       hideAll(document.querySelectorAll('ol.post-context')); | ||||
|       hideAll(document.querySelectorAll('div.pastebin')); | ||||
|     } | ||||
|     function toggle() { | ||||
|       for (var i = 0; i < arguments.length; i++) { | ||||
|         var e = document.getElementById(arguments[i]); | ||||
|         if (e) { | ||||
|           e.style.display = e.style.display == 'none' ? 'block': 'none'; | ||||
|         } | ||||
|       } | ||||
|       return false; | ||||
|     } | ||||
|     function switchPastebinFriendly(link) { | ||||
|       s1 = "Switch to copy-and-paste view"; | ||||
|       s2 = "Switch back to interactive view"; | ||||
|       link.textContent = link.textContent.trim() == s1 ? s2: s1; | ||||
|       toggle('browserTraceback', 'pastebinTraceback'); | ||||
|       return false; | ||||
|     } | ||||
|   </script> | ||||
|   {% endif %} | ||||
| </head> | ||||
| <body> | ||||
| <div id="summary"> | ||||
|   <h1>{% if exception_type %}{{ exception_type }}{% else %}Report{% endif %} | ||||
|       {% if request %} at {{ request.path_info }}{% endif %}</h1> | ||||
|   <pre class="exception_value">{% if exception_value %}{{ exception_value|force_escape }}{% if exception_notes %}{{ exception_notes }}{% endif %}{% else %}No exception message supplied{% endif %}</pre> | ||||
|   <table class="meta"> | ||||
| {% if request %} | ||||
|     <tr> | ||||
|       <th>Request Method:</th> | ||||
|       <td>{{ request.META.REQUEST_METHOD }}</td> | ||||
|     </tr> | ||||
|     <tr> | ||||
|       <th>Request URL:</th> | ||||
|       <td>{{ request_insecure_uri }}</td> | ||||
|     </tr> | ||||
| {% endif %} | ||||
|     <tr> | ||||
|       <th>Django Version:</th> | ||||
|       <td>{{ django_version_info }}</td> | ||||
|     </tr> | ||||
| {% if exception_type %} | ||||
|     <tr> | ||||
|       <th>Exception Type:</th> | ||||
|       <td>{{ exception_type }}</td> | ||||
|     </tr> | ||||
| {% endif %} | ||||
| {% if exception_type and exception_value %} | ||||
|     <tr> | ||||
|       <th>Exception Value:</th> | ||||
|       <td><pre>{{ exception_value|force_escape }}</pre></td> | ||||
|     </tr> | ||||
| {% endif %} | ||||
| {% if lastframe %} | ||||
|     <tr> | ||||
|       <th>Exception Location:</th> | ||||
|       <td><span class="fname">{{ lastframe.filename }}</span>, line {{ lastframe.lineno }}, in {{ lastframe.function }}</td> | ||||
|     </tr> | ||||
| {% endif %} | ||||
| {% if raising_view_name %} | ||||
|     <tr> | ||||
|       <th>Raised during:</th> | ||||
|       <td>{{ raising_view_name }}</td> | ||||
|     </tr> | ||||
| {% endif %} | ||||
|     <tr> | ||||
|       <th>Python Executable:</th> | ||||
|       <td>{{ sys_executable }}</td> | ||||
|     </tr> | ||||
|     <tr> | ||||
|       <th>Python Version:</th> | ||||
|       <td>{{ sys_version_info }}</td> | ||||
|     </tr> | ||||
|     <tr> | ||||
|       <th>Python Path:</th> | ||||
|       <td><pre>{{ sys_path|pprint }}</pre></td> | ||||
|     </tr> | ||||
|     <tr> | ||||
|       <th>Server time:</th> | ||||
|       <td>{{server_time|date:"r"}}</td> | ||||
|     </tr> | ||||
|   </table> | ||||
| </div> | ||||
| {% if unicode_hint %} | ||||
| <div id="unicode-hint"> | ||||
|     <h2>Unicode error hint</h2> | ||||
|     <p>The string that could not be encoded/decoded was: <strong>{{ unicode_hint }}</strong></p> | ||||
| </div> | ||||
| {% endif %} | ||||
| {% if template_does_not_exist %} | ||||
| <div id="template-not-exist"> | ||||
|     <h2>Template-loader postmortem</h2> | ||||
|     {% if postmortem %} | ||||
|         <p class="append-bottom">Django tried loading these templates, in this order:</p> | ||||
|         {% for entry in postmortem %} | ||||
|             <p class="postmortem-section">Using engine <code>{{ entry.backend.name }}</code>:</p> | ||||
|             <ul> | ||||
|                 {% if entry.tried %} | ||||
|                     {% for attempt in entry.tried %} | ||||
|                         <li><code>{{ attempt.0.loader_name }}</code>: {{ attempt.0.name }} ({{ attempt.1 }})</li> | ||||
|                     {% endfor %} | ||||
|                 {% else %} | ||||
|                     <li>This engine did not provide a list of tried templates.</li> | ||||
|                 {% endif %} | ||||
|             </ul> | ||||
|         {% endfor %} | ||||
|     {% else %} | ||||
|         <p>No templates were found because your 'TEMPLATES' setting is not configured.</p> | ||||
|     {% endif %} | ||||
| </div> | ||||
| {% endif %} | ||||
| {% if template_info %} | ||||
| <div id="template"> | ||||
|    <h2>Error during template rendering</h2> | ||||
|    <p>In template <code>{{ template_info.name }}</code>, error at line <strong>{{ template_info.line }}</strong></p> | ||||
|    <h3>{{ template_info.message|force_escape }}</h3> | ||||
|    <table class="source{% if template_info.top %} cut-top{% endif %} | ||||
|       {% if template_info.bottom != template_info.total %} cut-bottom{% endif %}"> | ||||
|    {% for source_line in template_info.source_lines %} | ||||
|    {% if source_line.0 == template_info.line %} | ||||
|    <tr class="error"><th>{{ source_line.0 }}</th> | ||||
|      <td>{{ template_info.before }}<span class="specific">{{ template_info.during }}</span>{{ template_info.after }}</td> | ||||
|    </tr> | ||||
|    {% else %} | ||||
|       <tr><th>{{ source_line.0 }}</th> | ||||
|       <td>{{ source_line.1 }}</td></tr> | ||||
|    {% endif %} | ||||
|    {% endfor %} | ||||
|    </table> | ||||
| </div> | ||||
| {% endif %} | ||||
| {% if frames %} | ||||
| <div id="traceback"> | ||||
|   <h2>Traceback{% if not is_email %} <span class="commands"><a href="#" onclick="return switchPastebinFriendly(this);"> | ||||
|     Switch to copy-and-paste view</a></span>{% endif %} | ||||
|   </h2> | ||||
|   <div id="browserTraceback"> | ||||
|     <ul class="traceback"> | ||||
|       {% for frame in frames %} | ||||
|         {% ifchanged frame.exc_cause %}{% if frame.exc_cause %} | ||||
|           <li class="cause"><h3> | ||||
|           {% if frame.exc_cause_explicit %} | ||||
|             The above exception ({{ frame.exc_cause|force_escape }}) was the direct cause of the following exception: | ||||
|           {% else %} | ||||
|             During handling of the above exception ({{ frame.exc_cause|force_escape }}), another exception occurred: | ||||
|           {% endif %} | ||||
|         </h3></li> | ||||
|         {% endif %}{% endifchanged %} | ||||
|         <li class="frame {{ frame.type }}"> | ||||
|           {% if frame.tb %} | ||||
|             <code class="fname">{{ frame.filename }}</code>, line {{ frame.lineno }}, in {{ frame.function }} | ||||
|           {% elif forloop.first %} | ||||
|             None | ||||
|           {% else %} | ||||
|             Traceback: None | ||||
|           {% endif %} | ||||
|  | ||||
|           {% if frame.context_line %} | ||||
|             <div class="context" id="c{{ frame.id }}"> | ||||
|               {% if frame.pre_context and not is_email %} | ||||
|                 <ol start="{{ frame.pre_context_lineno }}" class="pre-context" id="pre{{ frame.id }}"> | ||||
|                 {% for line in frame.pre_context %} | ||||
|                   <li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre>{{ line }}</pre></li> | ||||
|                 {% endfor %} | ||||
|                 </ol> | ||||
|               {% endif %} | ||||
|               <ol start="{{ frame.lineno }}" class="context-line"> | ||||
|                 <li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre>{{ frame.context_line }}{{ frame.colno }}</pre>{% if not is_email %} <span>…</span>{% endif %}</li> | ||||
|               </ol> | ||||
|               {% if frame.post_context and not is_email  %} | ||||
|                 <ol start='{{ frame.lineno|add:"1" }}' class="post-context" id="post{{ frame.id }}"> | ||||
|                   {% for line in frame.post_context %} | ||||
|                   <li onclick="toggle('pre{{ frame.id }}', 'post{{ frame.id }}')"><pre>{{ line }}</pre></li> | ||||
|                   {% endfor %} | ||||
|               </ol> | ||||
|               {% endif %} | ||||
|             </div> | ||||
|           {% endif %} | ||||
|  | ||||
|           {% if frame.vars %} | ||||
|             {% if is_email %} | ||||
|               <div class="commands"> | ||||
|                 <h2>Local Vars</h2> | ||||
|               </div> | ||||
|             {% else %} | ||||
|               <details> | ||||
|                 <summary class="commands">Local vars</summary> | ||||
|             {% endif %} | ||||
|             <table class="vars" id="v{{ frame.id }}"> | ||||
|               <thead> | ||||
|                 <tr> | ||||
|                   <th>Variable</th> | ||||
|                   <th>Value</th> | ||||
|                 </tr> | ||||
|               </thead> | ||||
|               <tbody> | ||||
|                 {% for var in frame.vars|dictsort:0 %} | ||||
|                   <tr> | ||||
|                     <td>{{ var.0 }}</td> | ||||
|                     <td class="code"><pre>{{ var.1 }}</pre></td> | ||||
|                   </tr> | ||||
|                 {% endfor %} | ||||
|               </tbody> | ||||
|             </table> | ||||
|             {% if not is_email %}</details>{% endif %} | ||||
|           {% endif %} | ||||
|         </li> | ||||
|       {% endfor %} | ||||
|     </ul> | ||||
|   </div> | ||||
| {% if not is_email %} | ||||
|   <form action="https://dpaste.com/" name="pasteform" id="pasteform" method="post"> | ||||
|   <div id="pastebinTraceback" class="pastebin"> | ||||
|     <input type="hidden" name="language" value="PythonConsole"> | ||||
|     <input type="hidden" name="title" | ||||
|       value="{{ exception_type }}{% if request %} at {{ request.path_info }}{% endif %}"> | ||||
|     <input type="hidden" name="source" value="Django Dpaste Agent"> | ||||
|     <input type="hidden" name="poster" value="Django"> | ||||
|     <textarea name="content" id="traceback_area" cols="140" rows="25"> | ||||
| Environment: | ||||
|  | ||||
| {% if request %} | ||||
| Request Method: {{ request.META.REQUEST_METHOD }} | ||||
| Request URL: {{ request_insecure_uri }} | ||||
| {% endif %} | ||||
| Django Version: {{ django_version_info }} | ||||
| Python Version: {{ sys_version_info }} | ||||
| Installed Applications: | ||||
| {{ settings.INSTALLED_APPS|pprint }} | ||||
| Installed Middleware: | ||||
| {{ settings.MIDDLEWARE|pprint }} | ||||
|  | ||||
| {% if template_does_not_exist %}Template loader postmortem | ||||
| {% if postmortem %}Django tried loading these templates, in this order: | ||||
| {% for entry in postmortem %} | ||||
| Using engine {{ entry.backend.name }}: | ||||
| {% if entry.tried %}{% for attempt in entry.tried %}    * {{ attempt.0.loader_name }}: {{ attempt.0.name }} ({{ attempt.1 }}) | ||||
| {% endfor %}{% else %}    This engine did not provide a list of tried templates. | ||||
| {% endif %}{% endfor %} | ||||
| {% else %}No templates were found because your 'TEMPLATES' setting is not configured. | ||||
| {% endif %}{% endif %}{% if template_info %} | ||||
| Template error: | ||||
| In template {{ template_info.name }}, error at line {{ template_info.line }} | ||||
|    {{ template_info.message|force_escape }} | ||||
| {% for source_line in template_info.source_lines %}{% if source_line.0 == template_info.line %}   {{ source_line.0 }} : {{ template_info.before }} {{ template_info.during }} {{ template_info.after }}{% else %}   {{ source_line.0 }} : {{ source_line.1 }}{% endif %}{% endfor %}{% endif %} | ||||
|  | ||||
| Traceback (most recent call last):{% for frame in frames %} | ||||
| {% ifchanged frame.exc_cause %}{% if frame.exc_cause %}{% if frame.exc_cause_explicit %} | ||||
| The above exception ({{ frame.exc_cause|force_escape }}) was the direct cause of the following exception: | ||||
| {% else %} | ||||
| During handling of the above exception ({{ frame.exc_cause|force_escape }}), another exception occurred: | ||||
| {% endif %}{% endif %}{% endifchanged %}  {% if frame.tb %}File "{{ frame.filename }}"{% if frame.context_line %}, line {{ frame.lineno }}{% endif %}, in {{ frame.function }} | ||||
| {% if frame.context_line %}    {% spaceless %}{{ frame.context_line }}{% endspaceless %}{{ frame.tb_area_colno }}{% endif %}{% elif forloop.first %}None{% else %}Traceback: None{% endif %}{% endfor %} | ||||
|  | ||||
| Exception Type: {{ exception_type }}{% if request %} at {{ request.path_info }}{% endif %} | ||||
| Exception Value: {{ exception_value|force_escape }}{% if exception_notes %}{{ exception_notes }}{% endif %} | ||||
| </textarea> | ||||
|   <br><br> | ||||
|   <input type="submit" value="Share this traceback on a public website"> | ||||
|   </div> | ||||
| </form> | ||||
| {% endif %} | ||||
| </div> | ||||
| {% endif %} | ||||
|  | ||||
| <div id="requestinfo"> | ||||
|   <h2>Request information</h2> | ||||
|  | ||||
| {% if request %} | ||||
|   {% if user_str %} | ||||
|     <h3 id="user-info">USER</h3> | ||||
|     <p>{{ user_str }}</p> | ||||
|   {% endif %} | ||||
|  | ||||
|   <h3 id="get-info">GET</h3> | ||||
|   {% if request.GET %} | ||||
|     <table class="req"> | ||||
|       <thead> | ||||
|         <tr> | ||||
|           <th>Variable</th> | ||||
|           <th>Value</th> | ||||
|         </tr> | ||||
|       </thead> | ||||
|       <tbody> | ||||
|         {% for k, v in request_GET_items %} | ||||
|           <tr> | ||||
|             <td>{{ k }}</td> | ||||
|             <td class="code"><pre>{{ v|pprint }}</pre></td> | ||||
|           </tr> | ||||
|         {% endfor %} | ||||
|       </tbody> | ||||
|     </table> | ||||
|   {% else %} | ||||
|     <p>No GET data</p> | ||||
|   {% endif %} | ||||
|  | ||||
|   <h3 id="post-info">POST</h3> | ||||
|   {% if filtered_POST_items %} | ||||
|     <table class="req"> | ||||
|       <thead> | ||||
|         <tr> | ||||
|           <th>Variable</th> | ||||
|           <th>Value</th> | ||||
|         </tr> | ||||
|       </thead> | ||||
|       <tbody> | ||||
|         {% for k, v in filtered_POST_items %} | ||||
|           <tr> | ||||
|             <td>{{ k }}</td> | ||||
|             <td class="code"><pre>{{ v|pprint }}</pre></td> | ||||
|           </tr> | ||||
|         {% endfor %} | ||||
|       </tbody> | ||||
|     </table> | ||||
|   {% else %} | ||||
|     <p>No POST data</p> | ||||
|   {% endif %} | ||||
|  | ||||
|   <h3 id="files-info">FILES</h3> | ||||
|   {% if request.FILES %} | ||||
|     <table class="req"> | ||||
|       <thead> | ||||
|         <tr> | ||||
|           <th>Variable</th> | ||||
|           <th>Value</th> | ||||
|         </tr> | ||||
|       </thead> | ||||
|       <tbody> | ||||
|         {% for k, v in request_FILES_items %} | ||||
|           <tr> | ||||
|             <td>{{ k }}</td> | ||||
|             <td class="code"><pre>{{ v|pprint }}</pre></td> | ||||
|           </tr> | ||||
|         {% endfor %} | ||||
|       </tbody> | ||||
|     </table> | ||||
|   {% else %} | ||||
|     <p>No FILES data</p> | ||||
|   {% endif %} | ||||
|  | ||||
|   <h3 id="cookie-info">COOKIES</h3> | ||||
|   {% if request.COOKIES %} | ||||
|     <table class="req"> | ||||
|       <thead> | ||||
|         <tr> | ||||
|           <th>Variable</th> | ||||
|           <th>Value</th> | ||||
|         </tr> | ||||
|       </thead> | ||||
|       <tbody> | ||||
|         {% for k, v in request_COOKIES_items %} | ||||
|           <tr> | ||||
|             <td>{{ k }}</td> | ||||
|             <td class="code"><pre>{{ v|pprint }}</pre></td> | ||||
|           </tr> | ||||
|         {% endfor %} | ||||
|       </tbody> | ||||
|     </table> | ||||
|   {% else %} | ||||
|     <p>No cookie data</p> | ||||
|   {% endif %} | ||||
|  | ||||
|   <h3 id="meta-info">META</h3> | ||||
|   <table class="req"> | ||||
|     <thead> | ||||
|       <tr> | ||||
|         <th>Variable</th> | ||||
|         <th>Value</th> | ||||
|       </tr> | ||||
|     </thead> | ||||
|     <tbody> | ||||
|       {% for k, v in request_meta.items|dictsort:0 %} | ||||
|         <tr> | ||||
|           <td>{{ k }}</td> | ||||
|           <td class="code"><pre>{{ v|pprint }}</pre></td> | ||||
|         </tr> | ||||
|       {% endfor %} | ||||
|     </tbody> | ||||
|   </table> | ||||
| {% else %} | ||||
|   <p>Request data not supplied</p> | ||||
| {% endif %} | ||||
|  | ||||
|   <h3 id="settings-info">Settings</h3> | ||||
|   <h4>Using settings module <code>{{ settings.SETTINGS_MODULE }}</code></h4> | ||||
|   <table class="req"> | ||||
|     <thead> | ||||
|       <tr> | ||||
|         <th>Setting</th> | ||||
|         <th>Value</th> | ||||
|       </tr> | ||||
|     </thead> | ||||
|     <tbody> | ||||
|       {% for k, v in settings.items|dictsort:0 %} | ||||
|         <tr> | ||||
|           <td>{{ k }}</td> | ||||
|           <td class="code"><pre>{{ v|pprint }}</pre></td> | ||||
|         </tr> | ||||
|       {% endfor %} | ||||
|     </tbody> | ||||
|   </table> | ||||
|  | ||||
| </div> | ||||
| {% if not is_email %} | ||||
|   <div id="explanation"> | ||||
|     <p> | ||||
|       You’re seeing this error because you have <code>DEBUG = True</code> in your | ||||
|       Django settings file. Change that to <code>False</code>, and Django will | ||||
|       display a standard page generated by the handler for this status code. | ||||
|     </p> | ||||
|   </div> | ||||
| {% endif %} | ||||
| </body> | ||||
| </html> | ||||
| @ -0,0 +1,66 @@ | ||||
| {% firstof exception_type 'Report' %}{% if request %} at {{ request.path_info }}{% endif %} | ||||
| {% firstof exception_value 'No exception message supplied' %} | ||||
| {% if request %} | ||||
| Request Method: {{ request.META.REQUEST_METHOD }} | ||||
| Request URL: {{ request_insecure_uri }}{% endif %} | ||||
| Django Version: {{ django_version_info }} | ||||
| Python Executable: {{ sys_executable }} | ||||
| Python Version: {{ sys_version_info }} | ||||
| Python Path: {{ sys_path }} | ||||
| Server time: {{server_time|date:"r"}} | ||||
| Installed Applications: | ||||
| {{ settings.INSTALLED_APPS|pprint }} | ||||
| Installed Middleware: | ||||
| {{ settings.MIDDLEWARE|pprint }} | ||||
| {% if template_does_not_exist %}Template loader postmortem | ||||
| {% if postmortem %}Django tried loading these templates, in this order: | ||||
| {% for entry in postmortem %} | ||||
| Using engine {{ entry.backend.name }}: | ||||
| {% if entry.tried %}{% for attempt in entry.tried %}    * {{ attempt.0.loader_name }}: {{ attempt.0.name }} ({{ attempt.1 }}) | ||||
| {% endfor %}{% else %}    This engine did not provide a list of tried templates. | ||||
| {% endif %}{% endfor %} | ||||
| {% else %}No templates were found because your 'TEMPLATES' setting is not configured. | ||||
| {% endif %} | ||||
| {% endif %}{% if template_info %} | ||||
| Template error: | ||||
| In template {{ template_info.name }}, error at line {{ template_info.line }} | ||||
|    {{ template_info.message }} | ||||
| {% for source_line in template_info.source_lines %}{% if source_line.0 == template_info.line %}   {{ source_line.0 }} : {{ template_info.before }} {{ template_info.during }} {{ template_info.after }}{% else %}   {{ source_line.0 }} : {{ source_line.1 }}{% endif %}{% endfor %}{% endif %}{% if frames %} | ||||
|  | ||||
| Traceback (most recent call last): | ||||
| {% for frame in frames %}{% ifchanged frame.exc_cause %}{% if frame.exc_cause %} | ||||
| {% if frame.exc_cause_explicit %}The above exception ({{ frame.exc_cause }}) was the direct cause of the following exception:{% else %}During handling of the above exception ({{ frame.exc_cause }}), another exception occurred:{% endif %} | ||||
| {% endif %}{% endifchanged %}  {% if frame.tb %}File "{{ frame.filename }}"{% if frame.context_line %}, line {{ frame.lineno }}{% endif %}, in {{ frame.function }} | ||||
| {% if frame.context_line %}    {% spaceless %}{{ frame.context_line }}{% endspaceless %}{{ frame.tb_area_colno }}{% endif %}{% elif forloop.first %}None{% else %}Traceback: None{% endif %} | ||||
| {% endfor %} | ||||
| {% if exception_type %}Exception Type: {{ exception_type }}{% if request %} at {{ request.path_info }}{% endif %} | ||||
| {% if exception_value %}Exception Value: {{ exception_value }}{% endif %}{% if exception_notes %}{{ exception_notes }}{% endif %}{% endif %}{% endif %} | ||||
| {% if raising_view_name %}Raised during: {{ raising_view_name }}{% endif %} | ||||
| {% if request %}Request information: | ||||
| {% if user_str %}USER: {{ user_str }}{% endif %} | ||||
|  | ||||
| GET:{% for k, v in request_GET_items %} | ||||
| {{ k }} = {{ v|stringformat:"r" }}{% empty %} No GET data{% endfor %} | ||||
|  | ||||
| POST:{% for k, v in filtered_POST_items %} | ||||
| {{ k }} = {{ v|stringformat:"r" }}{% empty %} No POST data{% endfor %} | ||||
|  | ||||
| FILES:{% for k, v in request_FILES_items %} | ||||
| {{ k }} = {{ v|stringformat:"r" }}{% empty %} No FILES data{% endfor %} | ||||
|  | ||||
| COOKIES:{% for k, v in request_COOKIES_items %} | ||||
| {{ k }} = {{ v|stringformat:"r" }}{% empty %} No cookie data{% endfor %} | ||||
|  | ||||
| META:{% for k, v in request_meta.items|dictsort:0 %} | ||||
| {{ k }} = {{ v|stringformat:"r" }}{% endfor %} | ||||
| {% else %}Request data not supplied | ||||
| {% endif %} | ||||
| Settings: | ||||
| Using settings module {{ settings.SETTINGS_MODULE }}{% for k, v in settings.items|dictsort:0 %} | ||||
| {{ k }} = {{ v|stringformat:"r" }}{% endfor %} | ||||
|  | ||||
| {% if not is_email %} | ||||
| You’re seeing this error because you have DEBUG = True in your | ||||
| Django settings file. Change that to False, and Django will | ||||
| display a standard page generated by the handler for this status code. | ||||
| {% endif %} | ||||
		Reference in New Issue
	
	Block a user