Reputation: 3241
Assuming a the following model
# models.py
class Person(models.Model):
name = models.CharField(max_length=40)
birthdate = models.DateField()
age = models.CharField(max_length=3) # dont mind the type, this is just an example :)
The desired behavior would be to have the age
field hidden while the user creates a new Person
object, and evaluate its value when the user submits the form. Also, the age
field should be visible when views the object instance in admin.
To achieve this, I created a ModelAdmin
with a custom ModelForm
as follows
# admin.py
from django.contrib import admin
from django import forms
from .models import Person
from dateutil.relativedelta import relativedelta
import datetime
class PersonForm(forms.ModelForm):
def clean(self):
cleaned_data = super(PersonForm, self).clean()
birthdate = cleaned_data.get('birthdate')
cleaned_data['age'] = relativedelta(datetime.date.today(), birthdate).years
return cleaned_data
class PersonAdmin(admin.ModelAdmin):
form = PersonForm
exclude = ('age',)
admin.site.register(Person, PersonAdmin)
However, after submitting the form, the age
field does not get populated, possibly because it is excluded. If I remove the exclude
then it works, but the form displays the age
field which is not the desired behavior.
Upvotes: 2
Views: 751
Reputation: 47364
Instead of custom form you can use ModelAdmin
's save_model
method:
class PersonAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
birthdate = form.cleaned_data.get('birthdate')
age = relativedelta(datetime.date.today(), birthdate).years
obj.age = age
super().save_model(request, obj, form, change)
def get_exclude(self, request, obj=None):
if not obj:
return ('age',)
return self.exclude
Also you probably can use readonly_fields
:
class PersonAdmin(admin.ModelAdmin):
readonly_fields = ('age',)
def save_model(self, request, obj, form, change):
birthdate = form.cleaned_data.get('birthdate')
age = relativedelta(datetime.date.today(), birthdate).years
obj.age = age
super().save_model(request, obj, form, change)
Upvotes: 2
Reputation: 3588
Or you can hide the input from form.
# admin.py
from django.contrib import admin
from django import forms
from .models import Person
from dateutil.relativedelta import relativedelta
import datetime
class PersonForm(forms.ModelForm):
age = forms.CharField(widget=forms.HiddenInput, required=False)
def clean(self):
cleaned_data = super(PersonForm, self).clean()
birthdate = cleaned_data.get('birthdate')
cleaned_data['age'] = relativedelta(datetime.date.today(), birthdate).years
return cleaned_data
You can use that form only when object is none (create case).
class PersonAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
if not obj:
kwargs['form'] = PersonForm
return super().get_form(request, obj, **kwargs)
admin.site.register(Person, PersonAdmin)
Upvotes: 1