Buckets:
ktongue/docker_container / simsite /venv /lib /python3.14 /site-packages /django /views /generic /dates.py
| 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. | |
| 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): | |
| """ | |
| Base class for date-based views displaying a list of objects. | |
| This requires subclassing to provide a response mixin. | |
| """ | |
| 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 view for archives of date-based items. | |
| This requires subclassing to provide 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): | |
| """ | |
| Base view for a list of objects published in a given year. | |
| This requires subclassing to provide a response mixin. | |
| """ | |
| 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): | |
| """ | |
| Base view for a list of objects published in a given month. | |
| This requires subclassing to provide a response mixin. | |
| """ | |
| 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): | |
| """ | |
| Base view for a list of objects published in a given week. | |
| This requires subclassing to provide a response mixin. | |
| """ | |
| 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): | |
| """ | |
| Base view for a list of objects published on a given day. | |
| This requires subclassing to provide a response mixin. | |
| """ | |
| 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): | |
| """ | |
| Base view for a list of objects published today. | |
| This requires subclassing to provide a response mixin. | |
| """ | |
| 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): | |
| """ | |
| Base detail view for a single object on a single date; this differs from | |
| the standard DetailView by accepting a year/month/day in the URL. | |
| This requires subclassing to provide a response mixin. | |
| """ | |
| 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() | |
Xet Storage Details
- Size:
- 26.9 kB
- Xet hash:
- 817e7b93095fbb136bddeb4750e43bbefeb5ab2e1b0e7aaea89628150c5a6bd9
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.