Django中的TabularInline类中的get_readonly_fields?

时间:2011-07-17 23:05:53

标签: django django-admin

我正在尝试在Django的TabularInline类中使用get_readonly_fields:

class ItemInline(admin.TabularInline):
    model = Item
    extra = 5

    def get_readonly_fields(self, request, obj=None):
        if obj:
            return ['name']

        return self.readonly_fields

此代码取自另一个StackOverflow问题: Django admin site: prevent fields from being edited?

但是,当它放在TabularInline类中时,新的对象表单无法正确呈现。目标是使某些字段只读,同时仍然允许在新对象中输入数据。有关变通方法或不同策略的任何想法吗?

5 个答案:

答案 0 :(得分:19)

小心 - “obj”不是内联对象,它是父对象。这可以说是一个错误 - 例如this Django ticket

答案 1 :(得分:4)

作为此问题的解决方法,我已将表单和Widget与我的内联相关联:

admin.py:

...

class MasterCouponFileInline(admin.TabularInline):
    model = MasterCouponFile
    form = MasterCouponFileForm
    extra = 0
Django 2.0中的

forms.py

from django import forms
from . import models
from feedback.widgets import DisablePopulatedText


class FeedbackCommentForm(forms.ModelForm):
    class Meta:
        model = models.MasterCouponFile
        fields = ('Comment', ....)
        widgets = {
            'Comment':  DisablePopulatedText,
        }

在widgets.py中

from django import forms

class DisablePopulatedText(forms.TextInput):
    def render(self, name, value, attrs=None, renderer=None):
        """Render the widget as an HTML string."""
        if value is not None:
            # Just return the value, as normal read_only fields do
            # Add Hidden Input otherwise the old fields are still required
            HiddenInput = forms.HiddenInput()
            return format_html("{}\n"+HiddenInput.render(name, value), self.format_value(value))
        else:
            return super().render(name, value, attrs, renderer)

较旧的Django版本:

forms.py

....

class MasterCouponFileForm(forms.ModelForm):
    class Meta:
        model = MasterCouponFile       

    def __init__(self, *args, **kwargs):
        super(MasterCouponFileForm, self).__init__(*args, **kwargs)
        self.fields['range'].widget = DisablePopulatedText(self.instance)
        self.fields['quantity'].widget = DisablePopulatedText(self.instance)

在widgets.py中

...

from django import forms
from django.forms.util import flatatt
from django.utils.encoding import force_text

class DisablePopulatedText(forms.TextInput):
    def __init__(self, obj, attrs=None):
        self.object = obj
        super(DisablePopulatedText, self).__init__(attrs)
    def render(self, name, value, attrs=None):
        if value is None:
            value = ''
        final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
        if value != '':
            # Only add the 'value' attribute if a value is non-empty.
            final_attrs['value'] = force_text(self._format_value(value))
        if "__prefix__" not in name and not value:
            return format_html('<input{0} disabled />', flatatt(final_attrs))
        else:
            return format_html('<input{0} />', flatatt(final_attrs))

答案 2 :(得分:2)

由于obj是父模型实例而不是内联显示的实例,因此目前仍然不容易这样做。

我为解决这个问题所做的是使所有字段以内联形式只读,并为内联模型的ChangeForm提供添加/编辑链接。

喜欢这个

class ChangeFormLinkMixin(object):
    def change_form_link(self, instance):
        url = reverse('admin:%s_%s_change' % (instance._meta.app_label,
            instance._meta.module_name), args=(instance.id,))
        # Id == None implies and empty inline object
        url = url.replace('None', 'add')
        command = _('Add') if url.find('add') > -1 else _('Edit')
        return format_html(u'<a href="{}">%s</a>' % command, url)

然后在内联中我会有类似的东西

class ItemInline(ChangeFormLinkMixin, admin.StackedInline):
    model = Item
    extra = 5
    readonly_fields = ['field1',...,'fieldN','change_form_link']

然后在ChangeForm中,我将能够以我想要的方式控制更改(我有几个状态,每个状态都有一组可编辑的字段关联)。

答案 3 :(得分:1)

正如其他人所说,这是django中的一个设计缺陷,如this Django ticket所示(感谢Danny W)。 java.time返回父对象,这不是我们想要的。

由于我们无法将其设为只读,因此我的解决方案是使用formset和clean方法无法通过表单进行验证:

get_readonly_fields

答案 4 :(得分:-2)

你走在正确的轨道上。使用您想要设置为readonly的字段的元组更新self.readonly_fields。

class ItemInline(admin.TabularInline):
    model = Item
    extra = 5

    def get_readonly_fields(self, request, obj=None):
        # add a tuple of readonly fields
        self.readonly_fields += ('field_a', 'field_b')
        return self.readonly_fields