Django admin:如何格式化只读字段?

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.

dr *_*bob 6

定义类的成员函数,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)


sed*_*nym 6

最简单的方法是通过在中定义自定义回调来实现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_L10NTrue,这将在查看器的本地时间显示日期时间,这可能是您想要的.如果你想保持USE_L10NFalse话可以覆盖这些行为,像这样:return localize(obj.my_datetime, use_l10n=True).