Django 管理员在修改 obj 时将字段设置为只读,但在添加新 obj 时需要这样做

在管理中,我希望在修改对象时禁用一个字段,但是在添加新对象时必须禁用该字段。

姜戈会怎么做?

42209 次浏览

You can do this by overriding the formfield_for_foreignkey method of the ModelAdmin:

from django import forms
from django.contrib import admin


from yourproject.yourapp.models import YourModel


class YourModelAdmin(admin.ModelAdmin):


class Meta:
model = YourModel


def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
# Name of your field here
if db_field.name == 'add_only':
if request:
add_opts = (self._meta.app_label, self._meta.module_name)
add = u'/admin/%s/%s/add/' % add_opts
if request.META['PATH_INFO'] == add:
field = db_field.formfield(**kwargs)
else:
kwargs['widget'] = forms.HiddenInput()
field = db_field.formfield(**kwargs)
return field
return admin.ModelAdmin(self, db_field, request, **kwargs)

遇到了类似的问题,我在 ModelAdmin 中用“ add _ fieldsets”和“ limited _ fieldsets”解决了这个问题。

from django.contrib import admin
class MyAdmin(admin.ModelAdmin):
declared_fieldsets = None
restricted_fieldsets = (
(None, {'fields': ('mod_obj1', 'mod_obj2')}),
( 'Text', {'fields': ('mod_obj3', 'mod_obj4',)}),
)


add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('add_obj1', 'add_obj2', )}),
)

请参阅例如: http://code.djangoproject.com/svn/django/trunk/django/contrib/auth/admin.py

但是这并不能保护您的模型不受以后“ add _ objecX”更改的影响。 如果您也想这样做,我认为您必须重复 Model 类“ save”函数并检查那里的更改。

看: www.djangoproject.com/documentation/models/save_delete_hooks/

天啊,尼克

您可以覆盖管理员的 get_readonly_fields方法:

class MyModelAdmin(admin.ModelAdmin):


def get_readonly_fields(self, request, obj=None):
if obj: # editing an existing object
return self.readonly_fields + ('field1', 'field2')
return self.readonly_fields

顺便说一句,以防其他人遇到和我一样的问题:

  1. 您仍然应该在类的主体中声明任何永久的 readonly _ fields,因为 readonly _ fields 类属性将从验证中被访问(参见 django.Contrib.admin.valid: valid_ base () ,第213行 appx)

  2. 这不适用于内联,因为传递给 get _ readonly _ fields ()的 obj 是父 obj (我使用 css 或 js 提供了两个相当拙劣且安全性较低的解决方案)

如果只想在更改视图上显示 将所有字段设置为只读,那么覆盖管理员的 get _ readonly _ fields:

def get_readonly_fields(self, request, obj=None):
if obj: # editing an existing object
# All model fields as read_only
return self.readonly_fields + tuple([item.name for item in obj._meta.fields])
return self.readonly_fields

And 如果要在更改视图中隐藏保存按钮:

  1. 改变视角

    def change_view(self, request, object_id, form_url='', extra_context=None):
    ''' customize edit form '''
    extra_context = extra_context or {}
    extra_context['show_save_and_continue'] = False
    extra_context['show_save'] = False
    extra_context['show_save_and_add_another'] = False # this not works if has_add_permision is True
    return super(TransferAdmin, self).change_view(request, object_id, extra_context=extra_context)
    
  2. Change permissions if user is trying to edit:

    def has_add_permission(self, request, obj=None):
    # Not too much elegant but works to hide show_save_and_add_another button
    if '/change/' in str(request):
    return False
    return True
    

    此解决方案已在 < strong > Django 1.11上进行了测试

一个基于 Bernhard Vallant 之前的出色建议的变体,它还保留了基类(如果有的话)提供的任何可能的定制:

class MyModelAdmin(BaseModelAdmin):


def get_readonly_fields(self, request, obj=None):
readonly_fields = super(MyModelAdmin, self).get_readonly_fields(request, obj)
if obj: # editing an existing object
return readonly_fields + ['field1', ..]
return readonly_fields

对于 Django 2.2. x,内联表格的情况仍然没有得到修复,但是 约翰的解决方案实际上非常聪明。

代码稍微适应了我的情况:

class NoteListInline(admin.TabularInline):
""" Notes list, readonly """
model = Note
verbose_name = _('Note')
verbose_name_plural = _('Notes')
extra = 0
fields = ('note', 'created_at')
readonly_fields = ('note', 'created_at')


def has_add_permission(self, request, obj=None):
""" Only add notes through AddInline """
return False


class NoteAddInline(admin.StackedInline):
""" Notes edit field """
model = Note
verbose_name = _('Note')
verbose_name_plural = _('Notes')
extra = 1
fields = ('note',)
can_delete = False


def get_queryset(self, request):
queryset = super().get_queryset(request)
return queryset.none()  # no existing records will appear


@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
# ...
inlines = (NoteListInline, NoteAddInline)
# ...

Bernhard 和 Mario 的伟大解决方案的一个更加可插拔的解决方案,增加了对 createonly _ fields 模拟到 readonly _ fields 的支持:

class MyModelAdmin(admin.ModelAdmin):


# ModelAdmin configuration as usual goes here


createonly_fields = ['title', ]


def get_readonly_fields(self, request, obj=None):
readonly_fields = list(super(MyModelAdmin, self).get_readonly_fields(request, obj))
createonly_fields = list(getattr(self, 'createonly_fields', []))
            

if obj:  # editing an existing object
readonly_fields.extend(createonly_fields)
return readonly_fields