532 Stimmen

Wie kann ich in einem Django-Formular ein Feld schreibgeschützt (oder deaktiviert) machen, damit es nicht bearbeitet werden kann?

Wie kann ich in einem Django-Formular ein Feld schreibgeschützt (oder deaktiviert) machen?

Wenn das Formular zum Erstellen eines neuen Eintrags verwendet wird, sollten alle Felder aktiviert sein - wenn sich der Datensatz jedoch im Aktualisierungsmodus befindet, müssen einige Felder schreibgeschützt sein.

Wenn Sie zum Beispiel eine neue Item Modell müssen alle Felder bearbeitbar sein, aber gibt es eine Möglichkeit, während der Aktualisierung des Datensatzes die sku Feld, so dass es zwar sichtbar ist, aber nicht bearbeitet werden kann?

class Item(models.Model):
    sku = models.CharField(max_length=50)
    description = models.CharField(max_length=200)
    added_by = models.ForeignKey(User)

class ItemForm(ModelForm):
    class Meta:
        model = Item
        exclude = ('added_by')

def new_item_view(request):
    if request.method == 'POST':
        form = ItemForm(request.POST)
        # Validate and save
    else:
            form = ItemForm()
    # Render the view

Kann Klasse ItemForm wiederverwendet werden? Welche Änderungen wären erforderlich in der ItemForm o Item Modellklasse? Müsste ich eine weitere Klasse schreiben, " ItemUpdateForm ", um den Artikel zu aktualisieren?

def update_item_view(request):
    if request.method == 'POST':
        form = ItemUpdateForm(request.POST)
        # Validate and save
    else:
        form = ItemUpdateForm()

1 Stimmen

Siehe auch SO Frage: Warum sind schreibgeschützte Formularfelder in Django eine schlechte Idee? @ stackoverflow.com/questions/2902024 Die akzeptierte Antwort (von Daniel Naab) kümmert sich um bösartige POST-Hacks.

0 Stimmen

497voto

Daniel Naab Punkte 21986

Wie bereits in diese Antwort fügte Django 1.9 die Feld.deaktiviert Attribut:

Das boolesche Argument disabled, wenn es auf True gesetzt ist, deaktiviert ein Formularfeld mit dem HTML-Attribut disabled, so dass es von den Benutzern nicht mehr bearbeitet werden kann. Selbst wenn ein Benutzer den an den Server übermittelten Wert des Feldes manipuliert, wird er zugunsten des Wertes aus den ursprünglichen Daten des Formulars ignoriert.

Bei Django 1.8 und früher müssen Sie, um die Eingabe im Widget zu deaktivieren und bösartige POST-Hacks zu verhindern, die Eingabe zusätzlich zum Setzen der readonly Attribut für das Formularfeld:

class ItemForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(ItemForm, self).__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if instance and instance.pk:
            self.fields['sku'].widget.attrs['readonly'] = True

    def clean_sku(self):
        instance = getattr(self, 'instance', None)
        if instance and instance.pk:
            return instance.sku
        else:
            return self.cleaned_data['sku']

Oder, ersetzen Sie if instance and instance.pk mit einer anderen Bedingung, die angibt, dass Sie gerade bearbeiten. Sie könnten auch das Attribut disabled auf das Eingabefeld, anstatt readonly .

El clean_sku Funktion wird sicherstellen, dass die readonly Wert wird nicht durch eine POST .

Andernfalls gibt es kein eingebautes Django-Formularfeld, das einen Wert wiedergibt, während es gebundene Eingabedaten zurückweist. Wenn dies Ihr Wunsch ist, sollten Sie stattdessen ein separates ModelForm die die nicht editierbaren Felder ausschließt, und drucken Sie sie einfach in Ihrer Vorlage aus.

3 Stimmen

Daniel, danke für deine Antwort. Es ist mir nicht klar, wie ich diesen Code verwenden soll? Würde dieser Code nicht auch für den neuen sowie den Update-Modus funktionieren? Kannst du deine Antwort bearbeiten, um Beispiele zu geben, wie man ihn für neue und aktualisierte Formulare verwendet? Vielen Dank!

9 Stimmen

Der Schlüssel zu Daniels Beispiel ist das Testen des Feldes .id. Neu erstellte Objekte haben id==None. Übrigens, eines der ältesten offenen Django-Tickets befasst sich mit diesem Problem. Siehe code.djangoproject.com/ticket/342 .

0 Stimmen

Aber was ist mit Fremdschlüsselfeldern, die nicht für Fremdschlüssel bestimmt sind?

198voto

MDB Punkte 2099

In Django 1.9 wurde das Attribut Field.disabled hinzugefügt: https://docs.djangoproject.com/en/stable/ref/forms/fields/#disabled

Das boolesche Argument disabled, wenn es auf True gesetzt ist, deaktiviert ein Formularfeld mit dem HTML-Attribut disabled, so dass es von Benutzern nicht bearbeitet werden kann. Selbst wenn ein Benutzer den an den Server übermittelten Wert des Feldes manipuliert, wird er zugunsten des Wertes aus den ursprünglichen Daten des Formulars ignoriert.

0 Stimmen

Nichts für 1.8 LTS?

9 Stimmen

Jede Idee, wie wir dies auf ein UpdateView verwenden können? Da es die Felder aus dem Modell generiert...

9 Stimmen

Richtige Antwort. Meine Lösung class MyChangeForm(forms.ModelForm): def __init__(self, *args, **kwargs): super(MyChangeForm, self).__init__(*args, **kwargs) self.fields['my_field'].disabled = True

100voto

muhuk Punkte 15179

Einstellung readonly für ein Widget macht die Eingabe im Browser nur schreibgeschützt. Hinzufügen einer clean_sku die zurückgibt instance.sku stellt sicher, dass sich der Feldwert auf Formularebene nicht ändert.

def clean_sku(self):
    if self.instance: 
        return self.instance.sku
    else: 
        return self.fields['sku']

Auf diese Weise können Sie die Modelle verwenden (unmodifiziertes Speichern) und vermeiden, dass der Fehler "Feld erforderlich" auftritt.

18 Stimmen

+1 Dies ist eine gute Möglichkeit, kompliziertere save()-Überschreibungen zu vermeiden. Sie sollten jedoch vor der Rückgabe eine Instanzprüfung durchführen (im Kommentar-Modus ohne Zeilenumbruch): "if self.instance: return self.instance.sku; else: return self.fields['sku']"

4 Stimmen

Für die letzte Zeile, würde return self.cleaned_data['sku'] genauso gut oder besser sein? Die documents scheinen zu empfehlen, dass man cleaned_data : "Der Rückgabewert dieser Methode ersetzt den bestehenden Wert in cleaned_data sein, also muss es sich um den Wert des Feldes aus cleaned_data (auch wenn diese Methode ihn nicht verändert hat) oder einen neuen bereinigten Wert."

86voto

chirale Punkte 1549

Antwort von awalker hat mir sehr geholfen!

Ich habe sein Beispiel so geändert, dass es mit Django 1.3 funktioniert, indem ich get_readonly_fields .

Normalerweise sollten Sie etwas wie dieses in app/admin.py :

class ItemAdmin(admin.ModelAdmin):
    ...
    readonly_fields = ('url',)

Ich habe mich auf diese Weise angepasst:

# In the admin.py file
class ItemAdmin(admin.ModelAdmin):
    ...
    def get_readonly_fields(self, request, obj=None):
        if obj:
            return ['url']
        else:
            return []

Und es funktioniert gut. Wenn Sie nun ein Element hinzufügen, wird die url ist schreibgeschützt, aber bei einer Änderung wird es schreibgeschützt.

0 Stimmen

Wie kann man dies tun, ohne auf dem Feld schreiben zu können?

0 Stimmen

Das erste Codeschnipsel deaktiviert das Schreiben auf das URL-Feld vollständig, das zweite Schnipsel deaktiviert das Schreiben auf das URL-Feld nur bei bestehenden Item-Instanzen. Sie können die Bedingung ändern, um ein anderes Verhalten zu erreichen, aber Sie können nicht beide verwenden, wenn ich die Frage richtig verstehe.

0 Stimmen

Ich habe versucht readonly_fields aber es funktionierte nicht, denn ich brauchte eine fields auch. Was ich stattdessen tat, war, die Werte in Variablen anzuzeigen, jetzt sind sie nur noch lesbar.

67voto

Humphrey Punkte 3890

Damit dies für eine ForeignKey müssen einige Änderungen vorgenommen werden. Erstens: Die SELECT HTML Tag hat nicht das Attribut readonly. Wir müssen disabled="disabled" stattdessen. Allerdings sendet der Browser dann keine Formulardaten für dieses Feld zurück. Also müssen wir das Feld auf nicht erforderlich setzen, damit das Feld korrekt validiert wird. Dann müssen wir den Wert wieder auf den ursprünglichen Wert zurücksetzen, damit er nicht auf leer gesetzt wird.

Für Fremdschlüssel müssen Sie also etwas tun wie:

class ItemForm(ModelForm):

    def __init__(self, *args, **kwargs):
        super(ItemForm, self).__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if instance and instance.id:
            self.fields['sku'].required = False
            self.fields['sku'].widget.attrs['disabled'] = 'disabled'

    def clean_sku(self):
        # As shown in the above answer.
        instance = getattr(self, 'instance', None)
        if instance:
            return instance.sku
        else:
            return self.cleaned_data.get('sku', None)

Auf diese Weise lässt der Browser nicht zu, dass der Benutzer das Feld ändert, und wird immer POST da es leer gelassen wurde. Wir überschreiben dann die clean Methode, um den Wert des Feldes so zu setzen, wie er ursprünglich in der Instanz war.

0 Stimmen

Ich habe versucht, es als Formular in TabularInline scheiterte aber, weil attrs wurden geteilt zwischen widget Instanzen, und alle außer der ersten Zeile, einschließlich der neu hinzugefügten, wurden schreibgeschützt.

0 Stimmen

Eine großartige (Update-)Lösung! Leider ist dies und der Rest haben Probleme, wenn es Form Fehler als alle "deaktiviert" Werte geleert werden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X