I’m making a form that users can input duration of time into the fields
I set default values on these fields, and I set widget for these fields as NumberInput, because if I use HTML input type="time", the browser shows AM/PM without additional style or scripting, and this might lead users to confuse about entering duration.
So, I use NumberInput and additional clean_<field> to handle this
This don’t show default value on the input.
What I want to know is, which HTML input type or widget should I use to show users default value and make them not confused.
models.py
import datetime
from django.core.validators import RegexValidator
from django.db import models
from django.urls import reverse
# Create your models here.
...
class Branch(models.Model):
...
lesson_time = models.DurationField(
verbose_name="Lesson time",
default=datetime.timedelta(minutes=110),
)
break_time = models.DurationField(
verbose_name="Break time",
default=datetime.timedelta(minutes=10),
)
...
forms.py
import datetime
from django import forms
from django.forms import ModelForm
from branches.models import Branch, Hour
class BranchForm(ModelForm):
...
def clean_lesson_time(self):
time = self.data["lesson_time"]
return datetime.timedelta(minutes=int(time))
def clean_break_time(self):
time = self.data["break_time"]
return datetime.timedelta(minutes=int(time))
...
class Meta:
model = Branch
fields = (
...
"lesson_time",
"break_time",
)
...
"lesson_time": forms.NumberInput(
attrs={
"aria-describedby": "minute-unit",
"class": "form-control",
}
),
"break_time": forms.NumberInput(
attrs={
"aria-describedby": "minute-unit",
"class": "form-control",
}
),
}
This is not django-related but I would have a DurationField set as a number input in the background, i.e, hidden via CSS. Or even as a hidden input.
The (most granular) unit of this field you can choose in an arbitrary way: could be seconds, hours, days or milliseconds.
Then I would enhance the lot with Javascript, i.e., purely client-side. Ideas:
split hidden field in several fields that can be interacted with. “Virtual fields” such as one for days, one for hours, etc. Similar to the deconstruct or to_python methods server-side.
do conversions client-side in real time. For example, if user enters 1 days and you hidden field is in seconds, add 246060 to the hidden field.
In that way you would ensure a simple number to reach your server as, say, a number of seconds.
OR
you could split you values in form.py, with optional fields taking care of each “unit”: days, hours, minutes, seconds, etc. Then transform in some clean methods server-side.
Up to you, there are probably a hundred different ways of doing that.
In other words, this appears to be something that is not under control of the HTML on the page.
(Note: Also read the text on the referenced page for more detailed information.)
I’d have to second @plopidou here - I think the only reasonable way to produce a platform and settings-independent widget for a duration would be to craft it yourself using a text input widget and some JavaScript.
may I ask - what duration format exactly do you want to work with? days/minutes/hours/seconds or just minutes? if this was just minutes (for input and output), you could really lower the complexity of everything
I don’t think I understand what you’re asking here.
What you see on a form on a page does not need to have any relationship to the data as it is stored in the model.
A DurationField in a model is stored in the database as an interval on PostgreSQL and Oracle and as a bigint everywhere else.
In the model objects themselves, it’s stored as a timedelta object, which is not a datetime field. And, unfortunately, there is no HTML representation of a “duration”.
No, I think you’ve understood what I’m asking.
Because of my lack of knowledge about Django, I’m a bit confused and I just wanted to make it clear.
What I’m trying to make is if user just enter ‘110’, system recognize it as 110 minutes and store as timedelta(minutes=110).
First, all of your choices are going to involve some amount of writing code to do this. That format is not going to be directly handled by Django.
You have a couple different options
Create a custom field with a custom to_python method.
Translate the text field in JavaScript to a parse_duration compatible format before submitting the form.
Create a separate field for the Form entry and perform the conversion in either Form.clean or ModelForm.save.
… and I’m sure there are other options as well. You can add code pretty much anywhere along the line here to perform your conversion before the data gets saved to the database.
If it helps, we did this before with an IntegerField and javascript to output minutes from days/hours/minutes (it outputs the minutes directly into the form field)
the JS code goes in static/js/script.js
function add_number() {
const day_number = parseInt((document.getElementById("DayConversion").value)* 1440 || 0) ; // 0 or get day value multiplied by minutes per day
const hour_number = parseInt(document.getElementById("HourConversion").value * 60 || 0); // 0 or get hour value multiplied by minutes per hour
const minute_number = parseInt(document.getElementById("MinuteConversion").value) || 0; // 0 or get minutes value
document.getElementById("id_downtime").value = day_number + hour_number + minute_number; // total value of minutes -> html element
}
and as for the widget for the form field (downtime), it was just NumberInput "downtime" : forms.NumberInput(attrs={'class':'form-control', 'placeholder':'Total Downtime'}),