Hello everyone,

I was wondering if it would be possible to extend the admin date_hierarchy 
logic to also accept a list of field names instead of just a single field 
name.

This would be useful for models that have multiple date fields that could 
be equally important to filter by (such as invoices, which typically have 4 
associated dates).

By modifying the *change_list *admin template and the *date_hierarchy *admin 
template 
tag a little (see attachments) I was able to make it accept and use a list 
of strings. I unfortunately couldn't figure out how to make it also work 
with a single string, which would be important for not breaking existing 
configurations. It is aslo worth mentioning that I had to use 
*--skip-checks* for testing purposes, so the admin checks would also need 
to be adjusted accordingly.

Any thoughts on this?

Regards,
Adam

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/b14f8f51-214a-48c0-9f03-821e53e61f00n%40googlegroups.com.
{% extends "admin/change_list.html" %} {% load custom_admin_list %} {% block date_hierarchy %} {% if cl.date_hierarchy %} {% for date_field in cl.date_hierarchy %} {{ date_field }}: {% date_hierarchy_list cl date_field %} {% endfor %} {% endif %} {% endblock %}
import datetime
from django.conf import settings
from django.contrib.admin.templatetags.base import InclusionAdminNode
from django.contrib.admin.utils import (
    get_fields_from_path,
)
from django.db import models
from django import template
from django.utils import formats, timezone
from django.utils.text import capfirst
from django.utils.translation import gettext as _

register = template.Library()


def date_hierarchy_list(cl, field_name):
    """
    Display the date hierarchy for date drill-down functionality.
    """
    if cl.date_hierarchy:
        field = get_fields_from_path(cl.model, field_name)[-1]
        if isinstance(field, models.DateTimeField):
            dates_or_datetimes = "datetimes"
            qs_kwargs = {"is_dst": True} if settings.USE_DEPRECATED_PYTZ else {}
        else:
            dates_or_datetimes = "dates"
            qs_kwargs = {}
        year_field = "%s__year" % field_name
        month_field = "%s__month" % field_name
        day_field = "%s__day" % field_name
        field_generic = "%s__" % field_name
        year_lookup = cl.params.get(year_field)
        month_lookup = cl.params.get(month_field)
        day_lookup = cl.params.get(day_field)

        def link(filters):
            return cl.get_query_string(filters, [field_generic])

        if not (year_lookup or month_lookup or day_lookup):
            # select appropriate start level
            date_range = cl.queryset.aggregate(
                first=models.Min(field_name), last=models.Max(field_name)
            )
            if date_range["first"] and date_range["last"]:
                if dates_or_datetimes == "datetimes":
                    date_range = {
                        k: timezone.localtime(v) if timezone.is_aware(v) else v
                        for k, v in date_range.items()
                    }
                if date_range["first"].year == date_range["last"].year:
                    year_lookup = date_range["first"].year
                    if date_range["first"].month == date_range["last"].month:
                        month_lookup = date_range["first"].month

        if year_lookup and month_lookup and day_lookup:
            day = datetime.date(int(year_lookup), int(month_lookup), int(day_lookup))
            return {
                "show": True,
                "back": {
                    "link": link({year_field: year_lookup, month_field: month_lookup}),
                    "title": capfirst(formats.date_format(day, "YEAR_MONTH_FORMAT")),
                },
                "choices": [
                    {"title": capfirst(formats.date_format(day, "MONTH_DAY_FORMAT"))}
                ],
            }
        elif year_lookup and month_lookup:
            days = getattr(cl.queryset, dates_or_datetimes)(
                field_name, "day", **qs_kwargs
            )
            return {
                "show": True,
                "back": {
                    "link": link({year_field: year_lookup}),
                    "title": str(year_lookup),
                },
                "choices": [
                    {
                        "link": link(
                            {
                                year_field: year_lookup,
                                month_field: month_lookup,
                                day_field: day.day,
                            }
                        ),
                        "title": capfirst(formats.date_format(day, "MONTH_DAY_FORMAT")),
                    }
                    for day in days
                ],
            }
        elif year_lookup:
            months = getattr(cl.queryset, dates_or_datetimes)(
                field_name, "month", **qs_kwargs
            )
            return {
                "show": True,
                "back": {"link": link({}), "title": _("All dates")},
                "choices": [
                    {
                        "link": link(
                            {year_field: year_lookup, month_field: month.month}
                        ),
                        "title": capfirst(
                            formats.date_format(month, "YEAR_MONTH_FORMAT")
                        ),
                    }
                    for month in months
                ],
            }
        else:
            years = getattr(cl.queryset, dates_or_datetimes)(
                field_name, "year", **qs_kwargs
            )
            return {
                "show": True,
                "back": None,
                "choices": [
                    {
                        "link": link({year_field: str(year.year)}),
                        "title": str(year.year),
                    }
                    for year in years
                ],
            }


@register.tag(name="date_hierarchy_list")
def date_hierarchy_tag(parser, token):
    return InclusionAdminNode(
        parser,
        token,
        func=date_hierarchy_list,
        template_name="date_hierarchy.html",
        takes_context=False,
    )

Reply via email to