django admin - Fügen Sie benutzerdefinierte Formularfelder hinzu, die nicht Teil des Modells sind

104

Ich habe ein Modell auf der Admin-Site registriert. Eines seiner Felder ist ein langer Zeichenfolgenausdruck. Ich möchte der Seite zum Hinzufügen / Aktualisieren dieses Modells im Administrator benutzerdefinierte Formularfelder hinzufügen, die auf der Grundlage dieser Feldwerte den Ausdruck für lange Zeichenfolgen erstellen und im entsprechenden Modellfeld speichern.

Wie kann ich das machen?

UPDATE: Grundsätzlich erstelle ich einen mathematischen oder Zeichenfolgenausdruck aus Symbolen. Der Benutzer wählt Symbole aus (dies sind die benutzerdefinierten Felder, die nicht Teil des Modells sind). Wenn er auf Speichern klickt, erstelle ich eine Zeichenfolgenausdrucksdarstellung aus dem Liste der Symbole und speichern Sie es in der DB. Ich möchte nicht, dass die Symbole Teil des Modells und der Datenbank sind, sondern nur der endgültige Ausdruck.

michalv82
quelle

Antworten:

154

Entweder in Ihrer admin.py oder in einer separaten forms.py können Sie eine ModelForm-Klasse hinzufügen und dann Ihre zusätzlichen Felder darin deklarieren, wie Sie es normalerweise tun würden. Ich habe auch ein Beispiel gegeben, wie Sie diese Werte in form.save () verwenden können:

from django import forms
from yourapp.models import YourModel


class YourModelForm(forms.ModelForm):

    extra_field = forms.CharField()

    def save(self, commit=True):
        extra_field = self.cleaned_data.get('extra_field', None)
        # ...do something with extra_field here...
        return super(YourModelForm, self).save(commit=commit)

    class Meta:
        model = YourModel

Damit die zusätzlichen Felder im Administrator angezeigt werden, gehen Sie wie folgt vor:

  1. Bearbeiten Sie Ihre Datei admin.py und legen Sie die Formulareigenschaft so fest, dass sie auf das oben erstellte Formular verweist
  2. Fügen Sie Ihre neuen Felder in Ihre Felder- oder Feldsatzdeklaration ein

So was:

class YourModelAdmin(admin.ModelAdmin):

    form = YourModelForm

    fieldsets = (
        (None, {
            'fields': ('name', 'description', 'extra_field',),
        }),
    )

UPDATE: In Django 1.8 müssen Sie fields = '__all__'der Metaklasse von YourModelForm hinzufügen.

Vishnu
quelle
2
Ja, ich möchte wirklich den Grund wissen
Vishnu
13
Sie sollten fields = '__all__'in Ihrer MetaKlasse hinzufügen , sonst beschwert sich Django:Creating a ModelForm without either the 'fields' attribute or the 'exclude' attribute is deprecated
IsaacKleiner
1
@sthzg, weil es nicht korrekt ist. Es gibt mir den Fehler:YourModelAdmin.list_display[0], 'extra_field' is not a callable or an attribute of 'YourModelAdmin' or found in the model 'YourModel'.
Cerin
7
Wenn Sie aus irgendeinem Grund eine erhalten AttributeError: Unable to lookup "extra_field"..., versuchen Sie label, der extra_fieldDefinition eine hinzuzufügen . Es scheint, dass Django versucht, die Bezeichnung dafür zu "erraten", indem er sich die Modelund die ModelAdminfür eine solche Attributdefinition ansieht .
Alxs
1
Dies hat wunderbar funktioniert, wenn extra_field ein CharField () ist. Wenn es sich jedoch um ein hiddenField handelt [In Django 1.11], wird ein Fehler generiert Unknown field(s) (extra_field) specified for YourModel. Check fields/fieldsets/exclude attributes of class YourModelAdmin.. Die Problemumgehung dafür istextra_field = forms.CharField(widget=forms.HiddenInput())
Kakoma
35

Es ist möglich, dies im Administrator zu tun, aber es gibt keinen sehr einfachen Weg dorthin. Außerdem möchte ich Ihnen raten, die meisten Geschäftslogiken in Ihren Modellen beizubehalten, damit Sie nicht vom Django-Administrator abhängig sind.

Vielleicht wäre es einfacher (und vielleicht sogar noch besser), wenn Sie die beiden getrennten Felder in Ihrem Modell hätten. Fügen Sie dann Ihrem Modell eine Methode hinzu, die sie kombiniert.

Beispielsweise:

class MyModel(models.model):

    field1 = models.CharField(max_length=10)
    field2 = models.CharField(max_length=10)

    def combined_fields(self):
        return '{} {}'.format(self.field1, self.field2)

Dann können Sie im Admin das combined_fields()Feld als schreibgeschütztes Feld hinzufügen :

class MyModelAdmin(models.ModelAdmin):

    list_display = ('field1', 'field2', 'combined_fields')
    readonly_fields = ('combined_fields',)

    def combined_fields(self, obj):
        return obj.combined_fields()

Wenn Sie das combined_fieldsin der Datenbank speichern möchten, können Sie es auch speichern, wenn Sie das Modell speichern:

def save(self, *args, **kwargs):
    self.field3 = self.combined_fields()
    super(MyModel, self).save(*args, **kwargs)
Gitaarik
quelle
4
Danke für die Antwort, aber das ist nicht das, wonach ich suche. Ich möchte nicht, dass die benutzerdefinierten Felder in der Datenbank gespeichert werden, sondern nur die berechnete Zeichenfolge. Grundsätzlich erstelle ich einen mathematischen Ausdruck oder einen Zeichenfolgenausdruck aus Symbolen, der Benutzer wählt Symbole aus (dies sind die benutzerdefinierten Felder, die nicht Teil des Modells sind), und wenn er auf Speichern klickt, erstelle ich eine Darstellung des Zeichenfolgenausdrucks aus der Liste von Symbole und speichern Sie es in der DB.
michalv82
@ michalv82 Sie können es auch in der save()Methode des Modells in der Datenbank speichern und die Aktualisierungen meiner Antwort überprüfen.
Gitaarik
1
Nochmals vielen
Ist es ein Problem, die 2 Felder zu speichern? Vielleicht kann es nützlich sein, wenn Sie wissen möchten, wie das kombinierte Feld generiert wurde.
Gitaarik
6
Nochmals vielen Dank, aber es sind nicht 2 Felder, es wird wahrscheinlich mehr sein. Auch hier möchte ich sie NICHT in der Datenbank speichern, daher kann diese Lösung für mich nicht funktionieren.
michalv82
5

Django 2.1.1 Die primäre Antwort brachte mich auf halbem Weg zur Beantwortung meiner Frage. Es hat mir nicht geholfen, das Ergebnis in einem Feld in meinem tatsächlichen Modell zu speichern. In meinem Fall wollte ich ein Textfeld, in das ein Benutzer Daten eingeben kann. Wenn dann ein Speichern erfolgt, werden die Daten verarbeitet und das Ergebnis in ein Feld im Modell eingegeben und gespeichert. Während die ursprüngliche Antwort zeigte, wie der Wert aus dem zusätzlichen Feld abgerufen werden kann, zeigte sie zumindest in Django 2.1.1 nicht, wie er wieder im Modell gespeichert werden kann

Dadurch wird der Wert aus einem ungebundenen benutzerdefinierten Feld übernommen, verarbeitet und in meinem realen Beschreibungsfeld gespeichert:

class WidgetForm(forms.ModelForm):
    extra_field = forms.CharField(required=False)

    def processData(self, input):
        # example of error handling
        if False:
            raise forms.ValidationError('Processing failed!')

        return input + " has been processed"

    def save(self, commit=True):
        extra_field = self.cleaned_data.get('extra_field', None)

        # self.description = "my result" note that this does not work

        # Get the form instance so I can write to its fields
        instance = super(WidgetForm, self).save(commit=commit)

        # this writes the processed data to the description field
        instance.description = self.processData(extra_field)

        if commit:
            instance.save()

        return instance

    class Meta:
        model = Widget
        fields = "__all__"
MangoLassi
quelle
4

Sie können jederzeit eine neue Admin-Vorlage erstellen und in Ihrer admin_view das tun, was Sie benötigen (überschreiben Sie die Admin-Add-URL zu Ihrer admin_view):

 url(r'^admin/mymodel/mymodel/add/$' , 'admin_views.add_my_special_model')
Eyal Ch
quelle
3

Wenn Sie unbedingt nur das kombinierte Feld im Modell und nicht die beiden separaten Felder speichern möchten, können Sie Folgendes tun:

Ich habe so etwas noch nie gemacht, daher bin ich mir nicht ganz sicher, wie es funktionieren wird.

Gitaarik
quelle