Ответ 1
Осторожно - "obj" не является встроенным объектом, а родителем. Это, возможно, ошибка - см. например этот Django-билет
Я пытаюсь использовать get_readonly_fields в классе TabularInline в Django:
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: запретить редактирование полей?
Однако, когда он помещается в класс TabularInline, новые формы объектов не отображаются должным образом. Цель состоит в том, чтобы сделать определенные поля только для чтения, в то же время позволяя вводить данные в новые объекты. Любые идеи для обходного пути или другой стратегии?
Осторожно - "obj" не является встроенным объектом, а родителем. Это, возможно, ошибка - см. например этот Django-билет
Это по-прежнему нелегко выполнить из-за того, что obj является экземпляром родительской модели, а не экземпляром, отображаемым встроенным.
То, что я сделал для этого, заключалось в том, чтобы все поля в встроенной форме были доступны только для чтения и предоставили ссылку Add/Edit для 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)
И затем в строке inline у меня будет что-то вроде этого
class ItemInline(ChangeFormLinkMixin, admin.StackedInline):
model = Item
extra = 5
readonly_fields = ['field1',...,'fieldN','change_form_link']
Затем в ChangeForm я смогу контролировать изменения так, как я хочу (у меня есть несколько состояний, каждый из которых связан с набором редактируемых полей).
В качестве обходного пути к этой проблеме я связал форму и виджет со своим Inline:
admin.py:
...
class MasterCouponFileInline(admin.TabularInline):
model = MasterCouponFile
form = MasterCouponFileForm
extra = 0
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))
Как добавили другие, это ошибка дизайна в django, как показано в этот билет Django (спасибо Дэнни W). get_readonly_fields
возвращает родительский объект, который здесь не нужен.
Поскольку мы не можем сделать это только для чтения, вот мое решение для проверки его не может быть задано формой, используя набор форм и чистый метод:
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')
Вы на правильном пути. Обновите 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