Mar*_*cin 9 python django django-forms django-admin
我有一个模型,Director
有两个DateFields和两个子类(下面的代码).我正在尝试为每个Director创建一个管理页面,它显示相应的子类实例,而不是Director
实例; 这部分大部分都很简单(我为每个子类创建一个内联,给主模型管理器一个表格,所有字段都被排除在外,并且主要的ModelAdmin请求来自内联的请求表单集,这些表单集有相应的实例 - 代码;有一个未解决的问题用这种方法,我在下面注意到,但不是这个问题的焦点).
我遇到的问题是我想按下显示给用户的值,其中一个显示在只读字段中,其中一个不显示.处理是我想将魔术值(date(1,1,1)
)更改为字符串"On incorporation"
.
readonly字段中的日期不会以非常友好的格式呈现,我希望减少对javascript的不必要依赖,所以我更喜欢服务器端解决方案.
下面的代码显示了我想要的表单,除了日期值根本没有按摩,保存时,即使没有错误,也有一个虚假的"请更正下面的错误"消息,并保存所有字段正确.
我的问题是:我如何拦截要在页面上呈现的值,包括只读字段和表单字段,并更改它们以显示我选择的字符串?
模型(到目前为止):
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
Run Code Online (Sandbox Code Playgroud)
管理员代码:
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))
Run Code Online (Sandbox Code Playgroud)
我意识到DirectorsCorporateInline
和之间存在着不对称DirectorsIndividualInline
; 那是因为我正在测试一个带有DirectorsIndividual
实例的实例.上面的代码是指模型中未显示的模型字段,因为它们对日期问题不重要; 应该可以在不改变那些字段的情况下使它们对于虚假错误问题变得无关紧要(尽管我意识到它对这个问题没什么帮助,但我想把这个问题主要集中在一个问题上).EnhancedAdmin
是一个ModelAdmin
子类,有一些小的改动,不应该是重要的.额外的代码可以在合理的请求中显示,但我不想与不相关的代码混淆.
为了完整性:我在python 2.7.2上使用django 1.3.1.
定义类的成员函数,Director
根据需要呈现readonly_field.
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
Run Code Online (Sandbox Code Playgroud)
然后只需添加'date_of_appointment_str'
到readonly_fields
管理员列表中.
编辑:我应该补充一点,这是一个快速的解决方案.一个更强大的解决方案是子类models.DateField
化为一个MyCustomDateField
类似于a的行为,DateField
除了当值为date(1,1,1)
"On Incorporation"时或当用户保存"On incorporation"时,它将值保存为date(1,1,1)
.这将确保您可以在此字段类型显示的任何位置重复使用此功能.但是,如果它只出现在一个地方; 这可能是矫枉过正的.
你需要类似的东西(这是未经测试的;你可能需要另外改变你的形式DateField
和/或其他东西;例如,如果你使用django-south,你将不得不添加自定义内省规则).
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)
Run Code Online (Sandbox Code Playgroud)
最简单的方法是通过在中定义自定义回调来实现ModelAdmin
.假设该字段被称为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'
Run Code Online (Sandbox Code Playgroud)
注意:如果settings.USE_L10N
是True
,这将在查看器的本地时间显示日期时间,这可能是您想要的.如果你想保持USE_L10N
的False
话可以覆盖这些行为,像这样:return localize(obj.my_datetime, use_l10n=True)
.
归档时间: |
|
查看次数: |
7201 次 |
最近记录: |