En admin, me gustaría deshabilitar un campo al modificar un objeto, pero hacerlo obligatorio al agregar un nuevo objeto.
¿Cuál es la forma en que django hace esto?
fuente
En admin, me gustaría deshabilitar un campo al modificar un objeto, pero hacerlo obligatorio al agregar un nuevo objeto.
¿Cuál es la forma en que django hace esto?
Puede anular el get_readonly_fields
método del administrador :
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
Si desea establecer todos los campos como de solo lectura solo en la vista de cambio, anule los get_readonly_fields del administrador:
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
Y si desea ocultar los botones de guardar en la vista de cambio :
Cambiar la vista
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)
Cambie los permisos si el usuario está intentando editar:
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
Esta solución ha sido probada sobre Django 1.11
FYI: en caso de que alguien más se encuentre con los mismos dos problemas que encontré:
Aún debe declarar cualquier readonly_fields permanentemente en el cuerpo de la clase, ya que se accederá al atributo de la clase readonly_fields desde la validación (vea django.contrib.admin.validation: validate_base (), line.213 appx)
Esto no funcionará con Inlines ya que el obj pasado a get_readonly_fields () es el obj padre (tengo dos soluciones bastante hacky y de baja seguridad usando css o js)
Una variación basada en la excelente sugerencia anterior de Bernhard Vallant, que también conserva cualquier posible personalización proporcionada por la clase base (si corresponde):
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
La situación con los formularios en línea todavía no se ha solucionado para Django 2.2.x, pero la solución de John es bastante inteligente.
Código ligeramente ajustado a mi situación:
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)
# ...
Puede hacer esto anulando el método formfield_for_foreignkey del 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)
Tengo un problema similar. Lo resolví con "add_fieldsets" y "restricted_fieldsets" en ModelAdmin.
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', )}),
)
Consulte, por ejemplo: http://code.djangoproject.com/svn/django/trunk/django/contrib/auth/admin.py
Pero esto no protege su modelo de cambios posteriores de "add_objX". Si desea esto también, creo que debe seguir el camino sobre la función "guardar" de la clase Modelo y verificar los cambios allí.
Ver: www.djangoproject.com/documentation/models/save_delete_hooks/
Greez, Nick