I have a Django app that among other things wants the user to enter a date of birth or their age. Upon the entry of one, the other should be calculated and when the form is saved, the database should be updated with both values. Unfortunately it does not appear that the modified data is seen when the form is submitted. If the birthday is entered and the age calculated, clean() will indicate that age is None. When the age is entered and the birthday calculated everything seems to work correctly. I am assuming that it has something to do with changing the disabled property, but have been unable to find anything to support this. I have also seen similar results with other fields where I change the disabled flag.
Excerpt from models.py:
class Patient(models.Model):
birthday = models.DateField(_('Birthday'), blank=True)
age = models.IntegerField(_('Age'), blank=True)
Excerpt from forms.py
class PatientCreateForm(forms.ModelForm):
birthday = forms.DateField(widget=DateInput(attrs={'type': 'date'}), required=True)
age = forms.IntegerField(required=True)
class Meta:
model = Patient
fields = [
'birthday',
'age',
Excerpt from views.py
def createpatient(request):
if not request.user.is_authenticated:
logger.debug(f'no user is logged in')
login_url = reverse("login")
next_url = reverse("create-patient")
redirect_url = f'{login_url}?next={next_url}'
logger.debug(f'redirect to {redirect_url}')
return redirect(redirect_url)
# value of patient_id is calculated here
if request.method == 'POST':
form = PatientCreateForm(request.POST, initial={'patient_id': patient_id})
if form.is_valid():
form.save()
messages.success(request, _(f'Patient {form.cleaned_data.get('full_name')} has been created.'))
return redirect('patient-list')
else:
form = PatientCreateForm(initial={'patient_id': patient_id})
return render(request, 'patients/patient_form.html', {'form': form})
Excerpt from patient_form.html
{% extends 'patients/base.html' %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block content %}
<script src=".3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="/[email protected]/dist/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<script>
$(document).ready(function() {
// event handler to update age based on birthday
$("#id_birthday").on('focusout', function() {
if($("#id_birthday").val()) {
var birthday = new Date($("#id_birthday").val());
var today = new Date();
var age = Math.floor((today - birthday) / (365.25 * 24 * 60 * 60 * 1000));
$("#id_age").val(age);
$("#id_age").prop('disabled', true);
}
else {
$("#id_age").prop('disabled', false);
}
});
// event handler to set birthday based on age, only if no birthday entered
$("#id_age").on('focusout', function() {
var bd = $("#id_birthday").val();
if(!Date.parse(bd)) {
if($("#id_age").val()) {
var age = $("#id_age").val();
var birthyear = new Date().getFullYear() - age;
var birthday = new Date(birthyear, 0, 1);
$("#id_birthday").val(birthyear + "-01-01");
}
}
});
});
</script>
<div class="content-section">
<form method="POST">
{% csrf_token %}
{{ form.non_field_errors }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">{% trans "Patient Information" %}</legend>
<div class="form-row">
<div class="form-group col-md-6 mb-0">
{{ form.birthday|as_crispy_field }}
</div>
<div class="form-group col-md-6 mb-0">
{{ form.age|as_crispy_field }}
</div>
</div>
{{ form.patient_id|as_crispy_field }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">{% trans "Create" %}</button>
<a class="btn btn-outline-danger" onclick="window.history.back()">{% trans "Cancel" %}</a>
</div>
</form>
</div>
{% endblock content %}
What I expect in this particular case: If a birthday is entered, the age should be calculated and the user should not be able to enter an age. If the birthday is not entered, the user should be able to enter and age and the birthday should be calculated. The birthday field should not be disabled.
In the other cases I mentioned in passing I have a field that defaults to 0. If a value is entered (e.g. 1) to other fields should be enabled (i.e. disable = false) and values entered. Here the fields get enabled correctly, but any data entered in the fields is ignored.
I have a Django app that among other things wants the user to enter a date of birth or their age. Upon the entry of one, the other should be calculated and when the form is saved, the database should be updated with both values. Unfortunately it does not appear that the modified data is seen when the form is submitted. If the birthday is entered and the age calculated, clean() will indicate that age is None. When the age is entered and the birthday calculated everything seems to work correctly. I am assuming that it has something to do with changing the disabled property, but have been unable to find anything to support this. I have also seen similar results with other fields where I change the disabled flag.
Excerpt from models.py:
class Patient(models.Model):
birthday = models.DateField(_('Birthday'), blank=True)
age = models.IntegerField(_('Age'), blank=True)
Excerpt from forms.py
class PatientCreateForm(forms.ModelForm):
birthday = forms.DateField(widget=DateInput(attrs={'type': 'date'}), required=True)
age = forms.IntegerField(required=True)
class Meta:
model = Patient
fields = [
'birthday',
'age',
Excerpt from views.py
def createpatient(request):
if not request.user.is_authenticated:
logger.debug(f'no user is logged in')
login_url = reverse("login")
next_url = reverse("create-patient")
redirect_url = f'{login_url}?next={next_url}'
logger.debug(f'redirect to {redirect_url}')
return redirect(redirect_url)
# value of patient_id is calculated here
if request.method == 'POST':
form = PatientCreateForm(request.POST, initial={'patient_id': patient_id})
if form.is_valid():
form.save()
messages.success(request, _(f'Patient {form.cleaned_data.get('full_name')} has been created.'))
return redirect('patient-list')
else:
form = PatientCreateForm(initial={'patient_id': patient_id})
return render(request, 'patients/patient_form.html', {'form': form})
Excerpt from patient_form.html
{% extends 'patients/base.html' %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block content %}
<script src="https://code.jquery/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
<script>
$(document).ready(function() {
// event handler to update age based on birthday
$("#id_birthday").on('focusout', function() {
if($("#id_birthday").val()) {
var birthday = new Date($("#id_birthday").val());
var today = new Date();
var age = Math.floor((today - birthday) / (365.25 * 24 * 60 * 60 * 1000));
$("#id_age").val(age);
$("#id_age").prop('disabled', true);
}
else {
$("#id_age").prop('disabled', false);
}
});
// event handler to set birthday based on age, only if no birthday entered
$("#id_age").on('focusout', function() {
var bd = $("#id_birthday").val();
if(!Date.parse(bd)) {
if($("#id_age").val()) {
var age = $("#id_age").val();
var birthyear = new Date().getFullYear() - age;
var birthday = new Date(birthyear, 0, 1);
$("#id_birthday").val(birthyear + "-01-01");
}
}
});
});
</script>
<div class="content-section">
<form method="POST">
{% csrf_token %}
{{ form.non_field_errors }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">{% trans "Patient Information" %}</legend>
<div class="form-row">
<div class="form-group col-md-6 mb-0">
{{ form.birthday|as_crispy_field }}
</div>
<div class="form-group col-md-6 mb-0">
{{ form.age|as_crispy_field }}
</div>
</div>
{{ form.patient_id|as_crispy_field }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">{% trans "Create" %}</button>
<a class="btn btn-outline-danger" onclick="window.history.back()">{% trans "Cancel" %}</a>
</div>
</form>
</div>
{% endblock content %}
What I expect in this particular case: If a birthday is entered, the age should be calculated and the user should not be able to enter an age. If the birthday is not entered, the user should be able to enter and age and the birthday should be calculated. The birthday field should not be disabled.
In the other cases I mentioned in passing I have a field that defaults to 0. If a value is entered (e.g. 1) to other fields should be enabled (i.e. disable = false) and values entered. Here the fields get enabled correctly, but any data entered in the fields is ignored.
Share Improve this question asked Nov 19, 2024 at 16:19 Andy WalldorffAndy Walldorff 31 silver badge4 bronze badges1 Answer
Reset to default 1Can you try this? I'm not very good with JS, it may not work correctly.
- Replace
.prop('disabled', true)
with.prop('readonly', true)
for the age field. - Update the
clean
method of the form to handle either birthday or age. Remove therequired=True
in Form. If either field can calculate the other, you should not require both in the form declaration.