In admin möchte ich ein Feld beim Ändern eines Objekts deaktivieren, es jedoch beim Hinzufügen eines neuen Objekts erforderlich machen.
Was ist der Django-Weg, um diesen einen zu machen?
Antworten:
Sie können die get_readonly_fields
Methode des Administrators überschreiben :
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
Wenn Sie alle Felder nur in der Änderungsansicht als schreibgeschützt festlegen möchten , überschreiben Sie die get_readonly_fields des Administrators:
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
Und wenn Sie die Schaltflächen zum Speichern in der Änderungsansicht ausblenden möchten :
Ändern Sie die Ansicht
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)
Ändern Sie die Berechtigungen, wenn der Benutzer versucht, Folgendes zu bearbeiten:
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
Diese Lösung wurde über Django 1.11 getestet
Zu Ihrer Information: Falls jemand anderes auf dieselben zwei Probleme stößt, auf die ich gestoßen bin:
Sie sollten weiterhin permanent readonly_fields im Hauptteil der Klasse deklarieren, da auf das Klassenattribut readonly_fields über die Validierung zugegriffen wird (siehe django.contrib.admin.validation: validate_base (), Zeile 213 appx).
Dies funktioniert nicht mit Inlines, da das an get_readonly_fields () übergebene Objekt das übergeordnete Objekt ist (ich habe zwei ziemlich hackige und wenig sichere Lösungen mit CSS oder JS).
Eine Variation, die auf dem vorherigen ausgezeichneten Vorschlag von Bernhard Vallant basiert und auch mögliche Anpassungen durch die Basisklasse (falls vorhanden) beibehält:
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
Die Situation mit Inline-Formularen ist für Django 2.2.x noch nicht behoben, aber die Lösung von John ist eigentlich ziemlich klug.
Code leicht auf meine Situation abgestimmt:
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)
# ...
Sie können dies tun, indem Sie die Methode formfield_for_foreignkey des ModelAdmin überschreiben:
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)
Habe ein ähnliches Problem. Ich habe es mit "add_fieldsets" und "eingeschränkten_fieldsets" im ModelAdmin gelöst.
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', )}),
)
Siehe z. B.: Http://code.djangoproject.com/svn/django/trunk/django/contrib/auth/admin.py
Dies schützt Ihr Modell jedoch nicht vor späteren Änderungen von "add_objX". Wenn Sie dies auch möchten, müssen Sie meiner Meinung nach die Funktion "Speichern" der Modellklasse durchgehen und dort nach Änderungen suchen.
Siehe: www.djangoproject.com/documentation/models/save_delete_hooks/
Griechisch, Nick