diff --git a/README.rst b/README.rst index e90778b..ca06ff4 100644 --- a/README.rst +++ b/README.rst @@ -27,6 +27,16 @@ perform arithmetic on them within the database. Usage ----- +Setup: + +.. code:: python + + INSTALLED_APPS = [ + '...', + 'relativedeltafield', + '...' + ] + Using the field is straightforward. You can add the field to your model like so: @@ -79,6 +89,14 @@ sure that after a ``save()``, your fields are both normalized and validated. +You can also use on admin: + +.. code:: python + from django.contrib import admin + + admin.site.register(MyModel) + + Limitations and pitfalls ------------------------ diff --git a/relativedeltafield/__init__.py b/relativedeltafield/__init__.py index 8c5a91a..d3fd801 100644 --- a/relativedeltafield/__init__.py +++ b/relativedeltafield/__init__.py @@ -1,10 +1,13 @@ import re from django.core.exceptions import ValidationError +from django.contrib.admin.options import FORMFIELD_FOR_DBFIELD_DEFAULTS from django.db import models +from django.forms.widgets import MultiWidget, TimeInput, NumberInput from django.utils.translation import ugettext_lazy as _ +from django import forms -from datetime import timedelta +from datetime import timedelta, time from dateutil.relativedelta import relativedelta # This is not quite ISO8601, as it allows the SQL/Postgres extension @@ -75,6 +78,61 @@ def format_relativedelta(relativedelta): +class RelativeDetailInput(MultiWidget): + template_name = 'admin/widgets/relativedelta.html' + + def __init__(self, attrs=None): + widgets = ( + NumberInput(attrs=attrs), + NumberInput(attrs=attrs), + NumberInput(attrs=attrs), + TimeInput(attrs=attrs), + ) + super().__init__(widgets) + + def get_context(self, name, value, attrs): + context = super().get_context(name, value, attrs) + context.update({ + 'year_label': _('Year'), + 'month_label': _('Month'), + 'day_label': _('Day'), + 'time_label': _('Time'), + }) + return context + + def decompress(self, value): + if value: + return [value.years, value.months, value.days, + time(hour=value.hours, minute=value.minutes, + second=value.seconds, + microsecond=value.microseconds)] + return [0, 0, 0, time()] + + +class RelativeDetailFormField(forms.MultiValueField): + def __init__(self, **kwargs): + fields = [ + forms.IntegerField(), + forms.IntegerField(), + forms.IntegerField(), + forms.TimeField(), + ] + super().__init__( + fields=fields, + require_all_fields=False, **kwargs + ) + + def compress(self, data_list): + kwargs = { + 'years': data_list[0], + 'months': data_list[1], + 'days': data_list[2], + 'hours': data_list[3].hour, + 'minutes': data_list[3].minute, + } + return relativedelta(**kwargs).normalized() + + class RelativeDeltaField(models.Field): """Stores dateutil.relativedelta.relativedelta objects. @@ -150,3 +208,10 @@ def convert_relativedeltafield_value(self, value, expression, connection, contex def value_to_string(self, obj): val = self.value_from_object(obj) return '' if val is None else format_relativedelta(val) + + +FORMFIELD_FOR_DBFIELD_DEFAULTS[RelativeDeltaField] = { + 'form_class': RelativeDetailFormField, + 'widget': RelativeDetailInput +} + diff --git a/relativedeltafield/templates/admin/widgets/relativedelta.html b/relativedeltafield/templates/admin/widgets/relativedelta.html new file mode 100644 index 0000000..a229de4 --- /dev/null +++ b/relativedeltafield/templates/admin/widgets/relativedelta.html @@ -0,0 +1,6 @@ +
+ {{ year_label }}: {% with widget=widget.subwidgets.0 %}{% include widget.template_name %}{% endwith %}
+ {{ month_label }}: {% with widget=widget.subwidgets.1 %}{% include widget.template_name %}{% endwith %}
+ {{ day_label }}: {% with widget=widget.subwidgets.2 %}{% include widget.template_name %}{% endwith %}
+ {{ time_label }}: {% with widget=widget.subwidgets.3 %}{% include widget.template_name %}{% endwith %}
+