Skip to content

Commit

Permalink
feat: localized datetime filtering (#857)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukasvinclav authored Nov 17, 2024
1 parent 4f24e4a commit d8cba95
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 34 deletions.
56 changes: 23 additions & 33 deletions src/unfold/contrib/filters/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@
)
from django.forms import ValidationError
from django.http import HttpRequest
from django.utils.dateparse import parse_datetime
from django.utils.translation import gettext_lazy as _

from unfold.utils import parse_date_str, parse_datetime_str

from .forms import (
DropdownForm,
RangeDateForm,
Expand Down Expand Up @@ -468,25 +469,13 @@ def __init__(
def queryset(self, request: HttpRequest, queryset: QuerySet) -> QuerySet:
filters = {}

value_from = self.used_parameters.get(self.parameter_name + "_from", None)
value_from = self.used_parameters.get(self.parameter_name + "_from")
if value_from not in EMPTY_VALUES:
filters.update(
{
self.parameter_name + "__gte": self.used_parameters.get(
self.parameter_name + "_from", None
),
}
)
filters.update({self.parameter_name + "__gte": parse_date_str(value_from)})

value_to = self.used_parameters.get(self.parameter_name + "_to", None)
value_to = self.used_parameters.get(self.parameter_name + "_to")
if value_to not in EMPTY_VALUES:
filters.update(
{
self.parameter_name + "__lte": self.used_parameters.get(
self.parameter_name + "_to", None
),
}
)
filters.update({self.parameter_name + "__lte": parse_date_str(value_to)})

try:
return queryset.filter(**filters)
Expand Down Expand Up @@ -546,18 +535,22 @@ def __init__(

if self.parameter_name + "_from_0" in params:
value = params.pop(self.field_path + "_from_0")
value = value[0] if isinstance(value, list) else value
self.used_parameters[self.field_path + "_from_0"] = value

if self.parameter_name + "_from_1" in params:
value = params.pop(self.field_path + "_from_1")
value = value[0] if isinstance(value, list) else value
self.used_parameters[self.field_path + "_from_1"] = value

if self.parameter_name + "_to_0" in params:
value = params.pop(self.field_path + "_to_0")
value = value[0] if isinstance(value, list) else value
self.used_parameters[self.field_path + "_to_0"] = value

if self.parameter_name + "_to_1" in params:
value = params.pop(self.field_path + "_to_1")
value = value[0] if isinstance(value, list) else value
self.used_parameters[self.field_path + "_to_1"] = value

def expected_parameters(self) -> List[str]:
Expand All @@ -571,29 +564,26 @@ def expected_parameters(self) -> List[str]:
def queryset(self, request: HttpRequest, queryset: QuerySet) -> QuerySet:
filters = {}

date_value_from = self.used_parameters.get(
self.parameter_name + "_from_0", None
)
time_value_from = self.used_parameters.get(
self.parameter_name + "_from_1", None
)
date_value_to = self.used_parameters.get(self.parameter_name + "_to_0", None)
time_value_to = self.used_parameters.get(self.parameter_name + "_to_1", None)
date_value_from = self.used_parameters.get(self.parameter_name + "_from_0")
time_value_from = self.used_parameters.get(self.parameter_name + "_from_1")

date_value_to = self.used_parameters.get(self.parameter_name + "_to_0")
time_value_to = self.used_parameters.get(self.parameter_name + "_to_1")

if date_value_from not in EMPTY_VALUES and time_value_from not in EMPTY_VALUES:
filters.update(
{
f"{self.parameter_name}__gte": parse_datetime(
f"{date_value_from}T{time_value_from}"
f"{self.parameter_name}__gte": parse_datetime_str(
f"{date_value_from} {time_value_from}"
),
}
)

if date_value_to not in EMPTY_VALUES and time_value_to not in EMPTY_VALUES:
filters.update(
{
f"{self.parameter_name}__lte": parse_datetime(
f"{date_value_to}T{time_value_to}"
f"{self.parameter_name}__lte": parse_datetime_str(
f"{date_value_to} {time_value_to}"
),
}
)
Expand All @@ -612,16 +602,16 @@ def choices(self, changelist: ChangeList) -> Tuple[Dict[str, Any], ...]:
name=self.parameter_name,
data={
self.parameter_name + "_from_0": self.used_parameters.get(
self.parameter_name + "_from_0", None
self.parameter_name + "_from_0"
),
self.parameter_name + "_from_1": self.used_parameters.get(
self.parameter_name + "_from_1", None
self.parameter_name + "_from_1"
),
self.parameter_name + "_to_0": self.used_parameters.get(
self.parameter_name + "_to_0", None
self.parameter_name + "_to_0"
),
self.parameter_name + "_to_1": self.used_parameters.get(
self.parameter_name + "_to_1", None
self.parameter_name + "_to_1"
),
},
),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="datetime flex flex-col max-w-2xl">
<div class="datetime flex flex-col max-w-2xl w-full">
<div class="flex flex-col flex-wrap mb-2">
{% if date_label %}
<div class="mb-2">
Expand Down
17 changes: 17 additions & 0 deletions src/unfold/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import json
from typing import Any, Iterable, List, Optional

from django.conf import settings
from django.db import models
from django.template.loader import render_to_string
from django.utils import formats, timezone
Expand Down Expand Up @@ -146,3 +147,19 @@ def format_response(response: str, theme: str) -> str:
f'<div class="block dark:hidden">{format_response(response, "colorful")}</div>'
f'<div class="hidden dark:block">{format_response(response, "monokai")}</div>'
)


def parse_date_str(value: str) -> Optional[datetime.date]:
for format in settings.DATE_INPUT_FORMATS:
try:
return datetime.datetime.strptime(value, format).date()
except (ValueError, TypeError):
continue


def parse_datetime_str(value: str) -> Optional[datetime.datetime]:
for format in settings.DATETIME_INPUT_FORMATS:
try:
return datetime.datetime.strptime(value, format)
except (ValueError, TypeError):
continue
4 changes: 4 additions & 0 deletions src/unfold/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,8 @@ class UnfoldAdminImageSmallFieldWidget(FileFieldMixin, AdminFileWidget):


class UnfoldAdminDateWidget(AdminDateWidget):
template_name = "unfold/widgets/date.html"

def __init__(
self, attrs: Optional[Dict[str, Any]] = None, format: Optional[str] = None
) -> None:
Expand All @@ -358,6 +360,8 @@ def __init__(


class UnfoldAdminTimeWidget(AdminTimeWidget):
template_name = "unfold/widgets/time.html"

def __init__(
self, attrs: Optional[Dict[str, Any]] = None, format: Optional[str] = None
) -> None:
Expand Down

0 comments on commit d8cba95

Please sign in to comment.