Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Django中的Tablerinline类中获取\u只读\u字段?_Django_Django Admin - Fatal编程技术网

在Django中的Tablerinline类中获取\u只读\u字段?

在Django中的Tablerinline类中获取\u只读\u字段?,django,django-admin,Django,Django Admin,我试图在Django中的Tablerinline类中使用get_readonly_字段: class ItemInline(admin.TabularInline): model = Item extra = 5 def get_readonly_fields(self, request, obj=None): if obj: return ['name'] return self.readonly_fields

我试图在Django中的Tablerinline类中使用get_readonly_字段:

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问题:


但是,如果将其放入Tablerinline类中,则新的对象形式无法正确渲染。目标是使某些字段成为只读字段,同时仍允许在新对象中输入数据。有什么解决方法或不同策略的想法吗?

你的思路是正确的。使用要设置为只读的字段的元组更新self.readonly_字段

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

小心——“obj”不是内联对象,它是父对象。这可以说是一个bug—例如,请参见

,由于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)

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

作为解决此问题的方法,我将一个表单和一个小部件关联到我的内联:

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,
        }
....

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

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)
...

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))
较旧的Django版本:

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,
        }
....

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

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)
...

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))
。。。
来自django导入表单
从django.forms.util导入flatatt
从django.utils.encoding导入强制_文本
类DisablePopulatedText(forms.TextInput):
定义初始化(self、obj、attrs=None):
self.object=obj
super(禁用填充文本,self)。\uuuu init\uuuu(属性)
def render(自身、名称、值、属性=无):
如果值为“无”:
值=“”
final\u attrs=self.build\u attrs(attrs,type=self.input\u type,name=name)
如果值!='':
#仅当值为非空时才添加“value”属性。
最终属性['value']=强制文本(自身格式\u值(值))
如果“\u_前缀\u_”不在名称和值中:
返回格式为html(“”,flatatt(最终属性))
其他:
返回格式为html(“”,flatatt(最终属性))

正如其他人所添加的,这是django中的一个设计缺陷,如中所示(感谢Danny W)
get\u readonly\u fields
返回父对象,这不是我们想要的

由于我们不能将其设置为只读,因此我的解决方案是使用formset和clean方法验证它不能由表单设置:

class ItemInline(admin.TabularInline):
    model = Item
    formset = ItemInlineFormset

class ItemInlineFormset(forms.models.BaseInlineFormSet):
    def clean(self):
        super(ItemInlineFormset, self).clean()
        for form in self.forms:
            if form.instance.some_condition:
                form.add_error('some_condition', 'Nope')

“不正确渲染”的确切含义是什么?新对象行没有任何表单字段,现有对象重复使用“名称”列。存在相同的问题。。似乎传递的对象是主对象的对象,而不是内联对象。这不是问题所在。他遇到的问题是obj保存的是父对象,而不是内联对象。最终,他无法使现有行中的字段为只读,但允许在插入时对字段进行编辑。我仍然认为这是一个丑陋的黑客行为,因为用户可以在子行的更改表单中更改父项,但再次,管理员站点被假装为“受信任”用户使用这个答案对我帮助很大,而且可能是完成这个任务的最好方法。