7đź‘Ť
Define a member function of your Director
class that renders the readonly_field as you want.
class Director(models.Model, Specializable):
date_of_appointment = models.DateField()
date_ceased_to_act = models.DateField(blank=True,null=True)
def date_of_appointment_str(self):
if self.date_of_appointment == datetime.date(1,1,1):
return "On incorporation"
else:
return "%s" % (self.date_of_appointment) # format as you wish
and then just add 'date_of_appointment_str'
to your list of readonly_fields
in the admin.
EDIT: I should add that this is one quick solution. A more robust solution is to subclass models.DateField
into a MyCustomDateField
that acts like a DateField
except that when the value is date(1,1,1)
it renders as “On incorporation” or when a user saves “On incorporation” it saves the value as date(1,1,1)
. This would ensure that you can reuse this functionality everywhere this field type shows up. However, if it only shows up in one place; this may be overkill.
You’d need something like (this is untested; you may need to additionally alter your the forms DateField
and/or other things; e.g., if you use django-south you’ll have to add custom introspection rules).
class MyCustomDateField(models.DateField):
date_111_str = 'On incorporation'
def value_to_string(self, obj):
val = self._get_val_from_obj(obj)
if val is None:
data = ''
elif val.year == val.day == val.month == 1:
data = date_111_str
else:
data = datetime_safe.new_date(val).strftime("%Y-%m-%d")
return data
def get_prep_value(self, value):
if value == date_111_str:
value = datetime.date(1,1,1)
return super(MyCustomDateField,self).get_prep_value(self, value)
9đź‘Ť
The easiest way is to do it by defining a custom callback in the ModelAdmin
. Let’s say the field is called my_datetime
:
from django.contrib import admin
from django.utils.formats import localize
class MyModelAdmin(admin.ModelAdmin):
readonly_fields = ('my_datetime_localized',)
def my_datetime_localized(self, obj):
return localize(obj.my_datetime)
my_datetime_localized.short_description = 'Date / time'
Note: if settings.USE_L10N
is True
, this will display the datetime in the local time of the viewer, which is probably what you want. If you want to keep USE_L10N
as False
then you can override its behaviour like so: return localize(obj.my_datetime, use_l10n=True)
.
- Is Django Asyc Views an ajax replacement?
- Celery: clean way of revoking the entire chain from within a task
- Django translate model choices
- Passing an argument to a custom save() method
2đź‘Ť
As @drjimbob (and carljm on #django) suggested, the solution is to create a member function or property on the model, e.g.:
class Director(models.Model, Specializable):
date_of_appointment = models.DateField()
date_ceased_to_act = models.DateField(blank=True,null=True)
#def date_formatter and def _date_format_factory omitted
date_of_appointment_formatted = lambda self: self.date_formatter(getattr(self, 'date_of_appointment'))
date_ceased_to_act_formatted = _date_format_factory(None, 'date_ceased_to_act') #for some reason, I get a 'classmethod/staticmethod object is not callable' error if I decorate _date_format_factory
date_of_appointment_formatted.short_description = u'Date of appointment'
Note the date_of_appointment_formatted.short_description
– the ModelAdmin
will use the short_description
as the label for a readonly_field
.
To get the properties working with model fields, a custom form is needed:
class DirectorInlineForm(EnhancedModelForm):
from django.utils import formats
date_ceased_to_act_formatted = forms.DateField(required = False, widget = admin.widgets.AdminDateWidget,
label = u'Date officer\'s appointment terminated',
input_formats = formats.get_format('DATE_INPUT_FORMATS') + (Director.on_incorporation,))
class Meta:
model = Director # Note that model declaration is necessary for this to work with additional fields declared
def __init__(self, *args, **kwargs):
super(DirectorInlineForm, self).__init__(*args, **kwargs)
# set initial values from model of declared fields
if self.instance:
self.initial['date_ceased_to_act_formatted'] = self.instance.date_ceased_to_act_formatted
def save(self, commit = True):
# save logic for magic formatted fields
if self._raw_value('date_ceased_to_act_formatted') == Director.on_incorporation:
sval = Director.on_incorporation
else: sval = self.cleaned_data['date_ceased_to_act_formatted']
self.instance.date_ceased_to_act_formatted = sval
return super(forms.ModelForm, self).save(commit)
The ModelForm
needs a custom field to display the property; a custom __init__
to set the initial value for the field from the property, and a custom save, to set the model property from the form field.
In my example, the save also has to be aware of the magic value, because of how DateField
handles the magic value. You could push that code into a custom field instead.
- Django get display name choices
- Dynamic pagination using Generic ListView
- Django/Webpack – How to serve generated webpack bundles with webpack dev server
- Why can I connect to http://127.0.0.1:8000/ but not to http://192.168.1.6/
0đź‘Ť
I would massage field values with javascript. You can override the admin templates, and attach your javascript code into the {% block extrahead %}
block ( some more info from the django book). Put your magic massage function example into .ready()
(if you use jQuery).
I hope this will work for you, because I would like to do something similar, but haven’t implemented yet. 🙂