django admin - tambahkan bidang formulir kustom yang bukan bagian dari model

106

Saya memiliki model yang terdaftar di situs admin. Salah satu bidangnya adalah ekspresi string panjang. Saya ingin menambahkan bidang formulir kustom ke halaman tambah / perbarui model ini di admin yang berdasarkan nilai bidang ini saya akan membangun ekspresi string panjang dan menyimpannya di bidang model yang relevan.

Bagaimana saya bisa melakukan itu?

UPDATE: Pada dasarnya yang saya lakukan adalah membangun ekspresi matematika atau string dari simbol, pengguna memilih simbol (ini adalah bidang khusus yang bukan bagian dari model) dan ketika dia mengklik simpan maka saya membuat representasi ekspresi string dari daftar simbol dan menyimpannya di DB. Saya tidak ingin simbol menjadi bagian dari model dan DB, hanya ekspresi akhir.

michalv82.dll
sumber

Jawaban:

156

Baik di admin.py Anda atau di forms.py terpisah Anda dapat menambahkan kelas ModelForm dan kemudian mendeklarasikan bidang ekstra Anda di dalamnya seperti biasa. Saya juga memberikan contoh bagaimana Anda dapat menggunakan nilai-nilai ini di 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

Agar bidang ekstra muncul di admin hanya:

  1. edit admin.py Anda dan setel properti formulir untuk merujuk ke formulir yang Anda buat di atas
  2. sertakan bidang baru Anda di bidang atau deklarasi fieldets

Seperti ini:

class YourModelAdmin(admin.ModelAdmin):

    form = YourModelForm

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

UPDATE: Dalam django 1.8 Anda perlu menambahkan fields = '__all__'metaclass dari YourModelForm.

Wisnu
sumber
2
Ya, saya benar-benar ingin tahu alasannya
Wisnu
13
Anda harus menambahkan fields = '__all__'di Metakelas Anda , jika tidak django mengeluh:Creating a ModelForm without either the 'fields' attribute or the 'exclude' attribute is deprecated
IsaacKleiner
1
@sthzg, Karena itu tidak benar. Ini memberi saya kesalahan:YourModelAdmin.list_display[0], 'extra_field' is not a callable or an attribute of 'YourModelAdmin' or found in the model 'YourModel'.
Cerin
7
Jika karena alasan tertentu, Anda mendapatkan AttributeError: Unable to lookup "extra_field"..., coba tambahkan a labelke extra_fielddefinisi. Tampaknya django mencoba untuk "menebak" labelnya, dengan melihat pada Modeldan ModelAdminuntuk definisi atribut tersebut.
alxs
1
Ini bekerja dengan baik jika extra_field adalah CharField (). Jika ini adalah hiddenField meskipun [Dalam Django 1.11], kesalahan dibuat Unknown field(s) (extra_field) specified for YourModel. Check fields/fieldsets/exclude attributes of class YourModelAdmin.. Solusi untuk itu adalahextra_field = forms.CharField(widget=forms.HiddenInput())
kakoma
35

Itu mungkin dilakukan di admin, tetapi tidak ada cara yang sangat mudah untuk itu. Juga, saya ingin memberi nasihat untuk menyimpan kebanyakan logika bisnis dalam model Anda, sehingga Anda tidak akan bergantung pada Admin Django.

Mungkin akan lebih mudah (dan bahkan lebih baik) jika Anda memiliki dua bidang terpisah pada model Anda. Kemudian tambahkan metode pada model Anda yang menggabungkannya.

Sebagai contoh:

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)

Kemudian di admin Anda dapat menambahkan bidang combined_fields()sebagai hanya baca:

class MyModelAdmin(models.ModelAdmin):

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

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

Jika Anda ingin menyimpan combined_fieldsdalam database, Anda juga bisa menyimpannya saat menyimpan model:

def save(self, *args, **kwargs):
    self.field3 = self.combined_fields()
    super(MyModel, self).save(*args, **kwargs)
gitaarik
sumber
4
Terima kasih atas jawabannya, tetapi bukan ini yang saya cari. Saya tidak ingin bidang kustom disimpan di DB, hanya string yang dihitung. Pada dasarnya yang saya lakukan adalah membangun ekspresi matematika atau string dari simbol, pengguna memilih simbol (ini adalah bidang khusus yang bukan bagian dari model) dan ketika dia mengklik simpan maka saya membuat representasi ekspresi string dari daftar simbol dan menyimpannya di DB.
michalv82
@ michalv82 Anda juga dapat menyimpannya ke database dalam metode model save(), periksa pembaruan jawaban saya.
gitaarik
1
terima kasih sekali lagi, tetapi masalahnya adalah saya tidak ingin menyimpan bidang yang menggabungkan bidang terakhir (yaitu simbol), saya hanya ingin string terakhir disimpan
michalv82
Apakah masalah menyimpan 2 bidang? Mungkin itu bisa berguna, jika Anda ingin tahu bagaimana bidang gabungan dihasilkan.
gitaarik
6
terima kasih lagi tapi ini bukan 2 bidang, mungkin akan lebih. Sekali lagi, saya TIDAK ingin menyimpannya di DB, jadi solusi ini tidak dapat berfungsi untuk saya.
michalv82
5

Django 2.1.1 Jawaban utama membuat saya setengah jalan untuk menjawab pertanyaan saya. Itu tidak membantu saya menyimpan hasil ke bidang dalam model saya yang sebenarnya. Dalam kasus saya, saya menginginkan bidang teks yang dapat dimasukkan pengguna ke dalam data, kemudian ketika terjadi penyimpanan, data akan diproses dan hasilnya dimasukkan ke dalam bidang dalam model dan disimpan. Sementara jawaban asli menunjukkan bagaimana mendapatkan nilai dari bidang ekstra, itu tidak menunjukkan bagaimana menyimpannya kembali ke model setidaknya di Django 2.1.1

Ini mengambil nilai dari bidang khusus yang tidak terikat, memproses, dan menyimpannya ke dalam bidang deskripsi nyata saya:

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__"
Mango Lassi
sumber
4

Anda selalu dapat membuat template admin baru, dan melakukan apa yang Anda butuhkan di admin_view (menimpa admin menambahkan url ke admin_view Anda):

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

Jika Anda benar-benar hanya ingin menyimpan bidang gabungan pada model dan bukan dua bidang terpisah, Anda dapat melakukan sesuatu seperti ini:

Saya tidak pernah melakukan sesuatu seperti ini jadi saya tidak sepenuhnya yakin bagaimana itu akan berhasil.

gitaarik
sumber