Django

Code

Ticket #2443 (new)

Opened 3 years ago

Last modified 3 weeks ago

Add IntervalField to database models

Reported by: ben.tucker@gmail.com Assigned to: Adys
Milestone: Component: Database layer (models, ORM)
Version: SVN Keywords: IntervalField interval duration DurationField
Cc: jm.bugtracking@gmail.com, treborhudson@gmail.com, patrick.lauber@divio.ch, hv@tbz-pariv.de, michal@salaban.info Triage Stage: Accepted
Has patch: 1 Needs documentation: 0
Needs tests: 1 Patch needs improvement: 1

Description

Although not all backends support the interval data type, it can be implemented using some other data type. This field is quite useful and it would be nice if it were available.

Attachments

durationfield.diff (1.0 kB) - added by Marty Alchin <gulopine@gamemusic.org> on 04/05/07 14:50:23.
DurationField
durationfield.2.diff (1.1 kB) - added by Marty Alchin <gulopine@gamemusic.org> on 04/09/07 22:02:01.
Updated DurationField location to actually work (I hadn't tested the other one)
durationfield.3.diff (5.4 kB) - added by Marty Alchin <gulopine@gamemusic.org> on 05/16/07 20:00:04.
Complete patch with widget, supporting oldforms and newforms, including newforms-admin
durationfield.4.diff (6.5 kB) - added by Marty Alchin <gulopine@gamemusic.org> on 05/28/07 12:21:08.
New patch, complete with documentation
durationfield.5.diff (9.0 kB) - added by Gulopine on 11/03/07 22:18:40.
Updated to work with latest trunk, and to no longer rely on #3982
durationfield.6.diff (8.9 kB) - added by Gulopine on 11/03/07 22:31:30.
Fixed a problem with creating objects by hand, and removed a couple debug lines
durationfield.7.diff (6.9 kB) - added by Adys on 04/13/09 01:47:58.
Updated patch for current trunk, without docs (no idea how much they've changed)
durationfield-new.diff (6.7 kB) - added by Adys on 05/11/09 01:32:13.
New DurationField implementation (try 3)
durationfield-new.2.diff (6.4 kB) - added by Adys on 05/26/09 00:26:40.
Fully working DurationField implementation with changes and fixes from Yuri Baburov
durationfield.patch (7.5 kB) - added by Adys on 06/16/09 20:41:29.
DurationField patch, with year/month support removed, bugfixes and rebased against last svn

Change History

07/28/06 17:14:16 changed by adrian

  • priority changed from normal to lowest.
  • severity changed from normal to minor.

02/17/07 10:15:09 changed by Simon G. <dev@simon.net.nz>

  • status changed from new to closed.
  • resolution set to wontfix.

I'm tentatively marking this as wontfix, as surely a combination of a "start-interval" and "end-interval" fields is better normalisation.

Feel free to correct me if I'm wrong though!

02/25/07 12:48:49 changed by anonymous

  • status changed from closed to reopened.
  • resolution deleted.

start-interval and end-interval doesn't work for all situations. Suppose you want to track "Amount of time spent driving in the past week" Unless I'm misunderstanding your use of start-interval and end-interval, there isn't an easy way to store this value with date/time fields. The real bonus of intervals comes in when you are able to do computations with them (such as "Amount of time spent driving in May" + "Amount of time spent driving in June")

02/25/07 21:19:36 changed by Gary Wilson <gary.wilson@gmail.com>

  • stage changed from Unreviewed to Design decision needed.

patches are welcome.

04/05/07 14:50:23 changed by Marty Alchin <gulopine@gamemusic.org>

  • attachment durationfield.diff added.

04/05/07 15:05:17 changed by Marty Alchin <gulopine@gamemusic.org>

  • keywords changed from IntervalField interval to IntervalField interval DurationField.
  • needs_better_patch set to 1.
  • has_patch set to 1.
  • needs_tests set to 1.

This simple patch adds a DurationField, which, in my opinion, better encapsulates the use cases described by anonymous above. Calling it a duration implies that it's not tied to any specific points in time, but rather just the passage of a certain amount of time. This makes more sense for things like the length (duration!) of audio and video content, for instance.

The patch itself is fairly simple, based on a FloatField, whose value represents the number of seconds to be represented. This seems sufficient for database storage, since all supported backends already support the equivalent of a FloatField, while max_digits and decimal_places allow for a number large enough to represent timedelta.max, complete with a full set of microseconds. It doesn't take calendars into account at all, but neither does timedelta, which is also in keeping with the duration concept as opposed to intervals.

The one problem I have with it so far is that it seems like Django is relying on backend database connection modules to handle coercion into Python types, rather than using to_python, so the value isn't changed into a timedelta unless you manually call .validate() on the model. This also prevents the validation errors from occurring properly when editing in the admin. That may be another issue for another day, however. I've marked the issue as "Patch needs improvement" in case I'm missing something that would make this happen.

P.S. Pardon the WikiFormatting on the attachment comment. That is, unless it gets a wiki page, in which case it'll be fine.

04/05/07 15:06:47 changed by Marty Alchin <gulopine@gamemusic.org>

I should also add, under the heading of "Patch needs improvement," that typing in decimals as a float value is hardly intuitive, and a better UI should be used. I, unfortunately, haven't thought of a good way to visually represent it, so I don't have any suggestions.

04/09/07 22:02:01 changed by Marty Alchin <gulopine@gamemusic.org>

  • attachment durationfield.2.diff added.

Updated DurationField location to actually work (I hadn't tested the other one)

05/16/07 20:00:04 changed by Marty Alchin <gulopine@gamemusic.org>

  • attachment durationfield.3.diff added.

Complete patch with widget, supporting oldforms and newforms, including newforms-admin

05/16/07 20:26:48 changed by Marty Alchin <gulopine@gamemusic.org>

  • needs_better_patch deleted.
  • needs_docs set to 1.

This most recent patch covers many more aspects of DurationField, including a newforms field and widget, fully compatible with newforms-admin. This new widget implements a bit better of a UI, separating the value into its components (days, hours, minutes, seconds, microseconds).

This new widget can't reasonably be done with oldforms, and since it's going out anyway, oldforms (and the current admin) will simply use a FloatField describing the number of seconds (including microseconds).

Also, this version of the patch relies on #3982 to enable support of the coerce method.

05/17/07 20:02:10 changed by anonymous

  • cc set to jm.bugtracking@gmail.com.

05/28/07 12:21:08 changed by Marty Alchin <gulopine@gamemusic.org>

  • attachment durationfield.4.diff added.

New patch, complete with documentation

05/28/07 12:25:45 changed by Marty Alchin <gulopine@gamemusic.org>

  • needs_docs deleted.

Once again, this patch supercedes all others, and once again relies on a new patch from #3982. However, it now uses lazy instantiation of timedelta objects, which should improve performance. Documentation is also included for trunk, with a few minor changes needed for the newforms-admin branch (due to the newforms widget included in the patch). Tests will come shortly, once I have a few other patches ironed out.

11/03/07 22:18:40 changed by Gulopine

  • attachment durationfield.5.diff added.

Updated to work with latest trunk, and to no longer rely on #3982

11/03/07 22:25:15 changed by Gulopine

  • keywords changed from IntervalField interval DurationField to IntervalField interval duration DurationField.
  • needs_better_patch set to 1.
  • status changed from reopened to new.
  • owner changed from nobody to Gulopine.

This latest patch is missing a few things before it can be considered final, but it's getting close. It no longer relies on #3982, but it doesn't yet support the recent error_messages stuff, and fails with the JSON serializer. It works with the XML serializer though, and should work in newforms-admin as is.

11/03/07 22:31:30 changed by Gulopine

  • attachment durationfield.6.diff added.

Fixed a problem with creating objects by hand, and removed a couple debug lines

11/09/07 10:52:31 changed by stefanfoulis

I've had some cases (mainly in the admin-interface) where get_db_prep_save(self, value) gets called with a value of type Decimal. I don't know why that happens, but I added this code to fix DurationField in these circumstances:

def get_db_prep_save(self, value):
    if value is None:
        return None
    if (type(value) == decimal.Decimal) or (type(value)==float):
        return str(value)
    return str(value.days * 24 * 3600 + value.seconds + float(value.microseconds) / 1000000)

If this is the correct way to fix this, please add it to the next diff you make.

11/09/07 11:15:54 changed by Gulopine

I'm more interested to know why it's getting a Decimal, because that would probably break other expectations as well. Do you have any code that reliably shows that to happen? If you don't know exactly where it's going wrong, that's fine, just being able to reproduce it will help me figure it out.

11/09/07 15:00:04 changed by stefanfoulis

OK... I did some testing and was able to reproduce the condition. In a simple Model with a DurationField create a record in Admin and enter 0 as the value. django-admin will throw a AttributeError?: 'Decimal' object has no attribute 'days' and will prevent saving the value.
If you enter 0 directly into the database or save a datetime.timedelta(0) from the django shell (which works, unlike in the admin) that attribute will return a Decimal("0") instead of the timedelta object (once you re-fetch it from the database).
So my proposed patch to get_db_prep_save is the wrong place to fix this problem. It seems this condition is rooted where the data is converted from to database to the native python object.
Could this have something to do with the backend driver doing the coercion to python types that was reported in #3982?
I've been testing this with sqlite3 btw.

11/09/07 15:20:52 changed by stefanfoulis

In DurationProxy.__get__ the value that instance.__dict__[self.field_name] returns is a Decimal object (if the value in the database is 0). With other values it is a TimeDelta

11/30/07 15:48:44 changed by jacob

  • stage changed from Design decision needed to Accepted.

This needs some UI help, but it's otherwise perfect.

03/02/08 11:42:12 changed by RahmCoff+dj@Radio1190.org

Is there any plan to use PostgreSQL’s Interval type with this?

03/02/08 11:50:18 changed by Gulopine

I have no plans to support interval, since DecimalField allows for compatibility with all of Django's backends. The only real reason to support interval is for inspecting existing databases that use it. If you'd like support for that, feel free to add in whatever code it would take to do it. I'm just too bogged down with other things at the moment, and supporting interval isn't high on my priority list.

04/01/08 18:33:07 changed by Rob Hudson <treborhudson@gmail.com>

  • cc changed from jm.bugtracking@gmail.com to jm.bugtracking@gmail.com, treborhudson@gmail.com.

I tried to apply this patch and it no longer applies cleanly. I believe django/newforms/fields.py and django/newforms/widgets.py had a reject and some fuzz.

When I did get it applied I was getting some errors when I went into the admin and tried to add data to my model...

    Traceback:
    File ".../django_trunk/django/contrib/admin/views/main.py" in add_stage
      287.         new_data = manipulator.flatten_data()
    Exception Value: 'str' object has no attribute 'items'

09/08/08 05:00:48 changed by Patrick Lauber <patrick.lauber@divio.ch>

  • cc changed from jm.bugtracking@gmail.com, treborhudson@gmail.com to jm.bugtracking@gmail.com, treborhudson@gmail.com, patrick.lauber@divio.ch.

10/17/08 04:07:39 changed by guettli

  • cc changed from jm.bugtracking@gmail.com, treborhudson@gmail.com, patrick.lauber@divio.ch to jm.bugtracking@gmail.com, treborhudson@gmail.com, patrick.lauber@divio.ch, hv@tbz-pariv.de.

04/13/09 01:47:58 changed by Adys

  • attachment durationfield.7.diff added.

Updated patch for current trunk, without docs (no idea how much they've changed)

04/13/09 02:09:12 changed by Adys

What's holding DurationField from going upstream (at least in contrib)? I can help fix whatever is necessary.

04/13/09 06:11:23 changed by Gulopine

Well, you'll notice that yours is the first patch to hit this ticket since Jacob said it "needs some UI help." That criticism still stands, since a half-dozen input fields don't make for a very good user experience.

04/21/09 13:34:50 changed by Adys

I had a look through the patch today. Using a DecimalField? and playing with commas seems unreasonably hacky. Is using a 64-bit int an option? It shouldn't be a problem under postgres, but I don't know about the other db backends.

04/21/09 13:49:50 changed by Adys

For the interface part, there's two decent choices.
Either go for a parse of the values in a single field, for example (input) "3d 12h 4m 7s", "7d 10m", "150000 ms"; output would be highest value possible first - 60000ms would be "1m", 75000ms would be "1m 15s" (abbreviations are up for change, would go for the standard ones).

Or, go for a single field with a <select> box, with some js shortcuts. [_] [ Unit ... ] (X)

In both cases, the possible units would be years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds (assuming microsecond precision). Thoughts?

04/21/09 14:11:54 changed by Gulopine

I'm a bit confused as to what you mean by "playing with commas," but thus far, I don't see anything at all hacky with using a DecimalField. Going with a 64-bit int would only be an option if Django supports a standalone cross-database 64-bit integer field. I don't want to invent one just for this. As for being hacky, I find it extremely elegant that the current approach stores seconds in the database, allowing for super-simple conversion: value = datetime.timedelta(seconds=float(value)). Plus, it works with all existing backends and maintains the ability to order records as you would expect (though, admittedly, your 64-bit int would also preserve that feature). What's exactly is hacky about it?

On the interface front, there was (once upon a time) a prevailing notion of using two text boxes: one for days and one for everything else. Basically, the days would be cleaned as a standard integer field, while the other would be cleaned as a time field, so the syntax for stuff less than a day would follow the "hh:mm:ss" format. The only thing that held me back from actually implementing that was that it would also be great to have a JavaScript helper similar to that of TimeField, showing a few useful options to get you started. Something like: 30 seconds, 2 hours, 1 day, 6 weeks (or some such similar options).

I obviously haven't gotten back around to this ticket in quite some time, but I'll see if I can devote some time to it soon to get the original UI proposal up and running.

04/21/09 14:53:29 changed by Adys

It feels wrong to "store" as seconds. Then again, just my opinion.

I was thinking the same thing about a js helper, and a friend came up with the idea of just parsing the field for units. I'll see if I can get a few mins to work on it tonight, I really like the idea. Three big advantages:
- No need for javascript (6 weeks would be "6w", 30 seconds would be "30s", 2 hours would be "2h", etc)
- Easy to understand, very user friendly (parsing "3h 10d" is okay, I suppose we'd give validationerror on "1h 10m 2h" or similar tho)
- No need for multiple input boxes, avoids confusion

On a slightly-related note, I don't know what to do about microseconds. Standard display is µs, but that could cause problems for users with special keyboard layouts etc.

05/10/09 18:13:52 changed by emes

  • cc changed from jm.bugtracking@gmail.com, treborhudson@gmail.com, patrick.lauber@divio.ch, hv@tbz-pariv.de to jm.bugtracking@gmail.com, treborhudson@gmail.com, patrick.lauber@divio.ch, hv@tbz-pariv.de, michal@salaban.info.

I think the year and month units are misleading, as in real life they have no fixed length. This is probably why the timedelta type does accept day as the longest unit possible.

We can assume that year is an astronomical unit, but it's almost never useful in real life. A year has to round to days and it's length is known only when applied to a certain start date. The same (or even worse) happens with a month. In Gregorian calendar there are 4 different lengths possible: 28, 29, 30, 31 days. We could assume that month is astronomical unit again (moon cycle) but it will roughly match only the Muslim calendar.

I'd prefer to leave a week as the longest unit possible.

05/10/09 20:09:42 changed by Adys

  • cc changed from jm.bugtracking@gmail.com, treborhudson@gmail.com, patrick.lauber@divio.ch, hv@tbz-pariv.de, michal@salaban.info to jm.bugtracking@gmail.com, treborhudson@gmail.com, patrick.lauber@divio.ch, hv@tbz-pariv.de.

Emes, I agree with you. I just wanted to get the implementation in. It's easier to remove than to add-on.

I sent the Dev list a mail a couple of days ago: http://groups.google.com/group/django-developers/browse_thread/thread/612e6c641d109f40?hl=en

Another question I bumped into this morning: Should we allow negative values?

05/10/09 20:10:53 changed by Adys

  • cc changed from jm.bugtracking@gmail.com, treborhudson@gmail.com, patrick.lauber@divio.ch, hv@tbz-pariv.de to jm.bugtracking@gmail.com, treborhudson@gmail.com, patrick.lauber@divio.ch, hv@tbz-pariv.de, michal@salaban.info.

cc michal@salaban.info, sorry for the spam, Firefox is horrible.

05/11/09 01:32:13 changed by Adys

  • attachment durationfield-new.diff added.

New DurationField implementation (try 3)

05/22/09 05:28:40 changed by Adys

Any feedback on the DurationField patch? I still need to figure out the admin output.

05/26/09 00:26:40 changed by Adys

  • attachment durationfield-new.2.diff added.

Fully working DurationField implementation with changes and fixes from Yuri Baburov

06/16/09 08:18:15 changed by Adys

  • owner changed from Gulopine to Adys.

Yuri did some updates to the patch posted above; I'll post a rebase by tomorrow. This rebase also comments out support for months and years as per list discussion; since it's just two values, might as well leave them commented.

1.1's almost out if I am to trust the targeted bugs. I suppose we could start looking at DurationField implementation for 1.2? This ticket has been open long enough.

Reassigning to myself.

06/16/09 20:41:29 changed by Adys

  • attachment durationfield.patch added.

DurationField patch, with year/month support removed, bugfixes and rebased against last svn


Add/Change #2443 (Add IntervalField to database models)




Change Properties
Action