django admin macht ein Feld schreibgeschützt, wenn obj geändert wird, aber erforderlich, wenn neues obj hinzugefügt wird

88

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?

frnhr
quelle

Antworten:

172

Sie können die get_readonly_fieldsMethode 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
Bernhard Vallant
quelle
21
Kleinere / größere Einschränkung: Dies funktioniert nicht für Inlines. Die dynamische Schaltfläche "Ein weiteres X hinzufügen" zeigt das schreibgeschützte Feld als "(Keine)" an, nicht wie erwartet als Formularfeld.
Cerin
15

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 :

  1. Ä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)
  2. Ä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

DVicenteR
quelle
Perfekt. Genau das brauchte ich!
Wogsland
3

Zu Ihrer Information: Falls jemand anderes auf dieselben zwei Probleme stößt, auf die ich gestoßen bin:

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

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

Tim Diggins
quelle
2
2. Punkt - das liegt an einem Fehler im Administrator: # 15602 Es scheint, dass er nicht so schnell behoben wird (letzte Aktivität vor 2 Jahren). Es sieht also so aus, als wären wir den CSS / JS-Lösungen überlassen.
Freitag,
2

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
Mario Orlandi
quelle
1

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)
    # ...
jlapoutre
quelle
0

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)
David Miller
quelle
0

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

Nick Ma.
quelle