Django管理员:如何格式化readonly字段? [英] Django admin: how to format readonly fields?
问题描述
我有一个模型, Director
与两个DateFields和两个子类(下面的代码)。我正在为每个Director创建一个管理页面,该页面显示相应的子类实例,而不是 Director
instance;这个部分大部分是容易的(我为每个子类创建一个内联,给主模型管理器一个所有字段被排除的形式,并且只有主要的ModelAdmin只有从内联请求的formets有相应的实例 - 代码;有一个未解决的问题使用这种方法,我在下面注意到,但不是这个问题的重点)。
I have a model, Director
with two DateFields, and two subclasses (code below). I am trying to create an admin page for each Director which shows the corresponding subclass instance, and not the Director
instance; this part is mostly easy (I create an inline for each subclass, give the main ModelAdmin a form with all fields excluded, and have the main ModelAdmin only request formsets from the inlines which have a corresponding instance - the code; there is an unresolved issue with this approach, which I note below, but is not the focus of this question).
我遇到的问题是我想按摩显示的值其中一个显示在只读字段中,其中一个不是。处理是我想将一个魔术值( date(1,1,1)
)更改为字符串在公司上
。
The problem I have is that I want to massage the values displayed to the user, one of which is shown in a readonly field, one of which is not. The processing is that I want to change a magic value (date(1,1,1)
) to the string "On incorporation"
.
只读字段中的日期不会以非常友好的解析格式呈现,我想减少对JavaScript的不必要依赖,所以我更喜欢服务器端解决方案。
Dates in readonly fields aren't rendered in a format very friendly to parsing, and I would like to reduce unnecessary dependence on javascript, so I would very much prefer a server-side solution.
下面的代码显示了我想要的表单,除了日期值根本没有按摩,保存时,即使没有错误,还有一个虚假的请更正错误消息,所有字段都保存正确。
The code below displays the forms as I want them, except that date values are not massaged at all, and when saving, there is a spurious "Please correct the error below" message, even though there are no errors, and all fields are saved correctly.
我的问题是:如何截取在页面上显示的值,在只读字段和表单字段中,并更改它们以显示我选择的字符串?
模型(至于材料):
class Director(models.Model, Specializable):
date_of_appointment = models.DateField()
date_ceased_to_act = models.DateField(blank=True,null=True)
class DirectorsIndividual(Director):
pass
class DirectorsCorporate(Director):
pass
管理代码:
class DirectorAdmin(EnhancedAdmin):
fields = ()
## def formfield_for_dbfield(self, db_field, **kwargs):
## return None
def queryset(self, request):
""" Directors for all companies which are incorporated by the current user's organisation """
individual = Individual.for_user(request.user)
return Director.objects.filter(company__incorporation_ticket__ordered_by__in = Organisation.all_organisations_for_which_individual_authorised_to_incorporate(individual))
class form(forms.ModelForm):
# have this return no html - that way only inlines are shown
class Meta:
fields = ()
pass
def is_valid(self):
self._errors = {}
return True
class DirectorsIndividualInline(admin.StackedInline):
model = DirectorsIndividual
fk_name = 'director_ptr'
extra = 0
readonly_fields = ('deferred_on','company','date_of_appointment',)
can_delete = False
def get_readonly_fields(self, request, obj=None):
if obj and obj.company and not obj.company.is_submitted(): return self.readonly_fields # allow editing of fields listed in else
else:
return itertools.chain(self.readonly_fields, ('individual', 'is_secretary'))
def has_delete_permission(self, request, obj=None):
return obj and ((obj.company and not obj.company.is_submitted()) or not obj.company)
class form(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(forms.ModelForm, self).__init__(*args, **kwargs)
self.fields['surrogate_for'].required = False
self.fields['representative_for'].required = False
if self.instance:
obj = self.instance
for field in (f for f in type(obj)._meta.fields if type(f) == fields.DateField):
val = field.value_from_object(obj)
assert (type(val) in (datetime.date, type(None),))
# assert field.name != 'date_of_appointment'
if val == inc_consts.EARLIEST_DATE:
self.initial[field.name] = "On incorporation"
def is_valid(self):
self._errors = {}
return True
class DirectorsCorporateInline(admin.StackedInline):
model = DirectorsCorporate
fk_name = 'director_ptr'
extra = 0
can_delete = False
class form(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(forms.ModelForm, self).__init__(*args, **kwargs)
if True:
for k in self.fields:
self.fields[k].required = False
def is_valid(self):
self._errors = {}
return True
inlines = (DirectorsIndividualInline,DirectorsCorporateInline)
def get_inlines(self, request, obj=None):
return (inline for inline in (self.inline_instances)
if inline.model.objects.filter(**{(inline.fk_name or self.model._meta.object_name.lower()) : obj }))
def get_formsets(self, request, obj=None):
""" only return formset for inlines for which there exists an object """
return (inline.get_formset(request, obj) for inline in self.get_inlines(request, obj))
我意识到, DirectorsCorporateInline
和 DirectorsIndividualInline
;那是因为我正在使用 DirectorsIndividual
实例来测试一个实例。上述代码是指模型中未显示的模型字段,因为它们对日期问题不重要;应该可以使它们对于错误的错误问题不重要,而不会改变这些字段(虽然我意识到这对于这个问题没有什么帮助,但我想将这个问题主要集中在一个问题上)。 EnhancedAdmin
是一个 ModelAdmin
子类,有一些小的更改,不应该是后果。额外的代码可以按照合理的请求显示,但我不想混淆不相关的代码。
I realise that there is an asymmetry between DirectorsCorporateInline
and DirectorsIndividualInline
; that is because I am testing on an instance with a DirectorsIndividual
instance. The code above refers to model fields not shown in the models, because they are not material to the dates issue; it should be possible to render them immaterial for the spurious error issue without altering those fields (although I realise it is less helpful for that issue, I want to keep this question mostly focused on one issue). EnhancedAdmin
is a ModelAdmin
subclass with some minor alterations which shouldn't be of consequence. Extra code can be shown on reasoned request, but I don't want to confuse with irrelevant code.
为了完整性:我在python 2.7上使用django 1.3.1。 2。
For completeness: I am using django 1.3.1 on python 2.7.2.
推荐答案
定义您的 Director
类的成员函数你想要的readonly_field。
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
然后只需将'date_of_appointment_str'
添加到您的列表 readonly_fields
在管理员中。
and then just add 'date_of_appointment_str'
to your list of readonly_fields
in the admin.
编辑:我应该补充说,这是一个快速的解决方案。一个更强大的解决方案是将 models.DateField
子类化为 MyCustomDateField
,其行为类似于 DateField
除了当值为 date(1,1,1)
时,它呈现为On Incorpor或用户保存On Incorpor它将值保存为 date(1,1,1)
。这将确保您可以在此字段类型显示的任何地方重复使用此功能。但是,如果它只显示在一个地方;这可能是过度的。
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.
您需要类似的东西(这是未经测试的;您可能需要另外更改您的表单 DateField
和/或其他东西;例如,如果您使用django-south,您将不得不添加自定义内省规则。)
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)
这篇关于Django管理员:如何格式化readonly字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!