django admin - adiciona campos de formulário personalizados que não fazem parte do modelo


106

Tenho um modelo cadastrado no site de administração. Um de seus campos é uma expressão de string longa. Gostaria de adicionar campos de formulário personalizados à página adicionar / atualizar deste modelo no administrador que, com base nos valores desses campos, construirei a expressão de string longa e a salvarei no campo de modelo relevante.

Como eu posso fazer isso?

ATUALIZAÇÃO: Basicamente, o que estou fazendo é construir uma expressão matemática ou string a partir de símbolos, o usuário escolhe os símbolos (esses são os campos personalizados que não fazem parte do modelo) e quando ele clica em salvar, crio uma representação de expressão string a partir do lista de símbolos e armazená-la no banco de dados. Não quero que os símbolos façam parte do modelo e do DB, apenas a expressão final.

Respostas:


156

Em seu admin.py ou em um forms.py separado, você pode adicionar uma classe ModelForm e então declarar seus campos extras dentro dela como faria normalmente. Também dei um exemplo de como você pode usar esses valores em 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

Para que os campos extras apareçam no administrador:

  1. edite seu admin.py e defina a propriedade do formulário para se referir ao formulário que você criou acima
  2. inclua seus novos campos em sua declaração de campos ou conjuntos de campos

Como isso:

class YourModelAdmin(admin.ModelAdmin):

    form = YourModelForm

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

ATUALIZAÇÃO: No django 1.8 você precisa adicionar fields = '__all__'à metaclasse de YourModelForm.


2
Sim, eu realmente quero saber o motivo
Vishnu

13
Você deve adicionar fields = '__all__'em sua Metaclasse, do contrário django reclama:Creating a ModelForm without either the 'fields' attribute or the 'exclude' attribute is deprecated
IsaacKleiner 01 de

1
@sthzg, porque não está correto. Isso me dá o erro:YourModelAdmin.list_display[0], 'extra_field' is not a callable or an attribute of 'YourModelAdmin' or found in the model 'YourModel'.
Cerin

7
Se, por algum motivo, você obtiver um AttributeError: Unable to lookup "extra_field"..., tente adicionar um labelà extra_fielddefinição. Parece que django tenta "adivinhar" o rótulo dele, olhando para Modele ModelAdminpara tal definição de atributo.
Alxs de

1
Isso funcionou perfeitamente se extra_field for um CharField (). Porém, se for um campo escondido [no Django 1.11], um erro é gerado Unknown field(s) (extra_field) specified for YourModel. Check fields/fieldsets/exclude attributes of class YourModelAdmin.. A solução para isso éextra_field = forms.CharField(widget=forms.HiddenInput())
kakoma

35

É possível fazer no admin, mas não há uma maneira muito direta de fazer isso. Além disso, gostaria de aconselhar a manter a maior parte da lógica de negócios em seus modelos, para que você não dependa do Django Admin.

Talvez seja mais fácil (e talvez até melhor) se você tiver os dois campos separados em seu modelo. Em seguida, adicione um método em seu modelo que os combine.

Por exemplo:

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)

Em seguida, no administrador, você pode adicionar o combined_fields()como um campo somente leitura:

class MyModelAdmin(models.ModelAdmin):

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

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

Se você deseja armazenar o combined_fieldsno banco de dados, você também pode salvá-lo ao salvar o modelo:

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

4
Obrigado pela resposta, mas não é isso que procuro. Não quero que os campos personalizados sejam salvos no banco de dados, apenas a string calculada. Basicamente, o que estou fazendo é construir uma expressão matemática ou string a partir de símbolos, o usuário escolhe os símbolos (esses são os campos personalizados que não fazem parte do modelo) e quando ele clica em salvar, crio uma representação de expressão string a partir da lista de símbolos e armazená-lo no banco de dados.
michalv82

@ michalv82 Você também pode salvá-lo no banco de dados no save()método do modelo , verifique as atualizações da minha resposta.
gitaarik

1
obrigado novamente, mas o problema é que eu não quero armazenar os campos que combinam o campo final (ou seja, os símbolos), eu só quero que a string final seja salva
michalv82

É um problema salvar os 2 campos? Talvez possa ser útil, se você quiser saber como o campo combinado foi gerado.
gitaarik

6
obrigado novamente, mas não são 2 campos, provavelmente serão mais. Novamente, eu NÃO quero armazená-los no banco de dados, então esta solução não pode funcionar para mim.
michalv82

5

Django 2.1.1 A resposta primária me deixou no meio do caminho para responder minha pergunta. Isso não me ajudou a salvar o resultado em um campo do meu modelo real. No meu caso, eu queria um campo de texto no qual um usuário pudesse inserir dados, então, quando um salvamento ocorresse, os dados seriam processados ​​e o resultado colocado em um campo no modelo e salvo. Enquanto a resposta original mostrava como obter o valor do campo extra, ela não mostrava como salvá-lo de volta no modelo pelo menos no Django 2.1.1

Isso pega o valor de um campo personalizado não acoplado, processa e salva no meu campo de descrição real:

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

você sempre pode criar um novo modelo de administrador e fazer o que você precisa em seu admin_view (substitua o URL de adição de administrador em seu admin_view):

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

3

Se você absolutamente deseja armazenar apenas o campo combinado no modelo e não os dois campos separados, pode fazer algo assim:

Eu nunca fiz algo assim, então não tenho certeza de como vai funcionar.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.