django admin: aggiungi campi modulo personalizzati che non fanno parte del modello


106

Ho un modello registrato nel sito di amministrazione. Uno dei suoi campi è un'espressione stringa lunga. Vorrei aggiungere campi modulo personalizzati alla pagina di aggiunta / aggiornamento di questo modello nell'amministratore che, in base ai valori di questi campi, costruirò l'espressione di stringa lunga e la salverò nel campo del modello pertinente.

Come lo posso fare?

AGGIORNAMENTO: Fondamentalmente quello che sto facendo è costruire un'espressione matematica o stringa da simboli, l'utente sceglie i simboli (questi sono i campi personalizzati che non fanno parte del modello) e quando fa clic su salva creo una rappresentazione di espressione stringa dal elenco dei simboli e memorizzarlo nel DB. Non voglio che i simboli facciano parte del modello e del DB, solo l'espressione finale.

Risposte:


156

O nel tuo admin.py o in un forms.py separato puoi aggiungere una classe ModelForm e quindi dichiarare i tuoi campi extra all'interno di quella come faresti normalmente. Ho anche fornito un esempio di come potresti utilizzare questi valori in form.save ():

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

Per visualizzare i campi aggiuntivi nell'amministratore, basta:

  1. modifica il tuo admin.py e imposta la proprietà del modulo in modo che faccia riferimento al modulo che hai creato sopra
  2. includere i nuovi campi nella dichiarazione dei campi o dei set di campi

Come questo:

class YourModelAdmin(admin.ModelAdmin):

    form = YourModelForm

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

AGGIORNAMENTO: In django 1.8 è necessario aggiungere fields = '__all__'alla metaclasse di YourModelForm.


2
Sì, voglio davvero sapere il motivo
Vishnu

13
Dovresti aggiungere fields = '__all__'nella tua Metaclasse, altrimenti django si lamenta:Creating a ModelForm without either the 'fields' attribute or the 'exclude' attribute is deprecated
IsaacKleiner

1
@sthzg, perché non è corretto. Mi dà l'errore:YourModelAdmin.list_display[0], 'extra_field' is not a callable or an attribute of 'YourModelAdmin' or found in the model 'YourModel'.
Cerin

7
Se per qualche motivo ottieni un AttributeError: Unable to lookup "extra_field"..., prova ad aggiungere un labelalla extra_fielddefinizione. Sembra che django cerchi di "indovinare" l'etichetta per esso, guardando Modele la ModelAdmindefinizione di tale attributo.
alxs

1
Funzionava magnificamente se extra_field è un CharField (). Se è un hiddenField però [in Django 1.11], viene generato un errore Unknown field(s) (extra_field) specified for YourModel. Check fields/fieldsets/exclude attributes of class YourModelAdmin.. La soluzione alternativa èextra_field = forms.CharField(widget=forms.HiddenInput())
kakoma

35

È possibile farlo nell'amministratore, ma non c'è un modo molto semplice per farlo. Inoltre, vorrei consigliare di mantenere la maggior parte della logica di business nei tuoi modelli, in modo da non dipendere dall'amministratore di Django.

Forse sarebbe più facile (e forse anche meglio) se avessi i due campi separati sul tuo modello. Quindi aggiungi un metodo sul tuo modello che li combini.

Per esempio:

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)

Quindi nell'amministratore puoi aggiungere il combined_fields()campo di sola lettura:

class MyModelAdmin(models.ModelAdmin):

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

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

Se vuoi memorizzare il combined_fieldsnel database puoi anche salvarlo quando salvi il modello:

def save(self, *args, **kwargs):
    self.field3 = self.combined_fields()
    super(MyModel, self).save(*args, **kwargs)

4
Grazie per la risposta, ma non è quello che sto cercando. Non voglio che i campi personalizzati vengano salvati nel DB, solo la stringa calcolata. Fondamentalmente quello che sto facendo è costruire un'espressione matematica o stringa da simboli, l'utente sceglie i simboli (questi sono i campi personalizzati che non fanno parte del modello) e quando fa clic su salva creo una rappresentazione di espressione stringa dall'elenco di simboli e memorizzarli nel DB.
michalv82

@ michalv82 Puoi anche salvarlo nel database nel save()metodo del modello , controlla gli aggiornamenti della mia risposta.
gitaarik

1
grazie ancora, ma il problema è che non voglio memorizzare i campi che combinano il campo finale (cioè i simboli), voglio solo salvare la stringa finale
michalv82

È un problema salvare i 2 campi? Forse può tornare utile, se vuoi sapere come è stato generato il campo combinato.
gitaarik

6
grazie ancora ma non sono 2 campi, probabilmente saranno di più. Di nuovo, NON voglio memorizzarli in DB, quindi questa soluzione non può funzionare per me.
michalv82

5

Django 2.1.1 La risposta principale mi ha portato a metà strada per rispondere alla mia domanda. Non mi ha aiutato a salvare il risultato in un campo nel mio modello attuale. Nel mio caso volevo un campo di testo in cui un utente potesse inserire i dati, quindi quando si verificava un salvataggio i dati sarebbero stati elaborati e il risultato inserito in un campo nel modello e salvato. Mentre la risposta originale mostrava come ottenere il valore dal campo extra, non mostrava come salvarlo di nuovo sul modello almeno in Django 2.1.1

Questo prende il valore da un campo personalizzato non associato, lo elabora e lo salva nel mio campo di descrizione reale:

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__"

4

puoi sempre creare un nuovo modello di amministrazione e fare ciò di cui hai bisogno in admin_view (sovrascrivi l'URL di aggiunta dell'amministratore al tuo admin_view):

 url(r'^admin/mymodel/mymodel/add/$' , 'admin_views.add_my_special_model')

3

Se vuoi assolutamente memorizzare solo il campo combinato sul modello e non i due campi separati, potresti fare qualcosa del genere:

Non ho mai fatto qualcosa di simile, quindi non sono completamente sicuro di come andrà a finire.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.