admin django membuat sebuah field read-only saat memodifikasi obj tetapi diperlukan saat menambahkan obj baru

91

Di admin saya ingin menonaktifkan bidang saat memodifikasi objek, tetapi membuatnya diperlukan saat menambahkan objek baru.

Apa cara django melakukan yang satu ini?

frnhr
sumber

Jawaban:

178

Anda dapat mengganti metode admin get_readonly_fields:

class MyModelAdmin(admin.ModelAdmin):

    def get_readonly_fields(self, request, obj=None):
        if obj: # editing an existing object
            return self.readonly_fields + ('field1', 'field2')
        return self.readonly_fields
Bernhard Vallant
sumber
21
Peringatan kecil / besar: Ini tidak berfungsi untuk sebaris. Tombol dinamis "tambahkan X lain" menunjukkan bidang hanya baca sebagai "(Tidak Ada)", bukan bidang formulir seperti yang Anda harapkan.
Cerin
17

Jika Anda ingin menyetel semua bidang sebagai hanya baca di tampilan ubah, timpa get_readonly_fields admin:

def get_readonly_fields(self, request, obj=None):
    if obj: # editing an existing object
        # All model fields as read_only
        return self.readonly_fields + tuple([item.name for item in obj._meta.fields])
    return self.readonly_fields

Dan jika Anda ingin menyembunyikan tombol simpan pada tampilan perubahan :

  1. Ubah tampilan

    def change_view(self, request, object_id, form_url='', extra_context=None):
        ''' customize edit form '''
        extra_context = extra_context or {}
        extra_context['show_save_and_continue'] = False
        extra_context['show_save'] = False
        extra_context['show_save_and_add_another'] = False # this not works if has_add_permision is True
        return super(TransferAdmin, self).change_view(request, object_id, extra_context=extra_context)
    
  2. Ubah izin jika pengguna mencoba mengedit:

    def has_add_permission(self, request, obj=None):
       # Not too much elegant but works to hide show_save_and_add_another button
        if '/change/' in str(request):
            return False
        return True
    

    Solusi ini telah diuji melalui Django 1.11

DVicenteR
sumber
Sempurna. Inilah yang saya butuhkan!
wogsland
3

FYI: jika orang lain mengalami dua masalah yang sama yang saya temui:

  1. Anda tetap harus mendeklarasikan readonly_fields secara permanen di badan kelas, karena atribut kelas readonly_fields akan diakses dari validasi (lihat django.contrib.admin.validation: validate_base (), line.213 appx)

  2. Ini tidak akan berfungsi dengan Inlines karena obj yang diteruskan ke get_readonly_fields () adalah objek induk (saya punya dua solusi yang agak hacky dan keamanan rendah menggunakan css atau js)

Tim Diggins
sumber
2
2. point - itu karena bug di admin: # 15602 Sepertinya itu tidak akan diperbaiki secepat itu (aktivitas terakhir 2 tahun yang lalu), jadi sepertinya kita diserahkan pada solusi CSS / JS.
frnhr
2

Variasi berdasarkan saran luar biasa sebelumnya dari Bernhard Vallant, yang juga mempertahankan kemungkinan penyesuaian yang diberikan oleh kelas dasar (jika ada):

class MyModelAdmin(BaseModelAdmin):

    def get_readonly_fields(self, request, obj=None):
        readonly_fields = super(MyModelAdmin, self).get_readonly_fields(request, obj)
        if obj: # editing an existing object
            return readonly_fields + ['field1', ..]
        return readonly_fields
Mario Orlandi
sumber
2

Situasi dengan formulir sebaris masih belum diperbaiki untuk Django 2.2.x tetapi solusi dari John sebenarnya cukup cerdas.

Kode sedikit disesuaikan dengan situasi saya:

class NoteListInline(admin.TabularInline):
""" Notes list, readonly """
    model = Note
    verbose_name = _('Note')
    verbose_name_plural = _('Notes')
    extra = 0
    fields = ('note', 'created_at')
    readonly_fields = ('note', 'created_at')

    def has_add_permission(self, request, obj=None):
    """ Only add notes through AddInline """
    return False

class NoteAddInline(admin.StackedInline):
    """ Notes edit field """
    model = Note
    verbose_name = _('Note')
    verbose_name_plural = _('Notes')
    extra = 1
    fields = ('note',)
    can_delete = False

    def get_queryset(self, request):
        queryset = super().get_queryset(request)
        return queryset.none()  # no existing records will appear

@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
    # ...
    inlines = (NoteListInline, NoteAddInline)
    # ...
jlapoutre
sumber
0

Anda dapat melakukan ini dengan mengganti metode formfield_for_foreignkey dari ModelAdmin:

from django import forms
from django.contrib import admin

from yourproject.yourapp.models import YourModel

class YourModelAdmin(admin.ModelAdmin):

    class Meta:
        model = YourModel

    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
        # Name of your field here
        if db_field.name == 'add_only':
            if request:
                add_opts = (self._meta.app_label, self._meta.module_name)
                add = u'/admin/%s/%s/add/' % add_opts
                if request.META['PATH_INFO'] == add:
                    field = db_field.formfield(**kwargs)
                else:
                    kwargs['widget'] = forms.HiddenInput()
                    field = db_field.formfield(**kwargs)
            return field
        return admin.ModelAdmin(self, db_field, request, **kwargs)
David Miller
sumber
0

Punya masalah serupa. Saya menyelesaikannya dengan "add_fieldsets" dan "limited_fieldsets" di ModelAdmin.

from django.contrib import admin  
class MyAdmin(admin.ModelAdmin):
 declared_fieldsets = None
 restricted_fieldsets = (
    (None, {'fields': ('mod_obj1', 'mod_obj2')}),
    ( 'Text', {'fields': ('mod_obj3', 'mod_obj4',)}),
 )

 add_fieldsets = (
            (None, {
             'classes': ('wide',),
             'fields': ('add_obj1', 'add_obj2', )}),
             )

Silakan lihat misalnya: http://code.djangoproject.com/svn/django/trunk/django/contrib/auth/admin.py

Tapi ini tidak melindungi model Anda dari perubahan "add_objX" nanti. Jika Anda menginginkan ini juga, saya pikir Anda harus melewati fungsi "simpan" kelas Model dan memeriksa perubahan di sana.

Lihat: www.djangoproject.com/documentation/models/save_delete_hooks/

Astaga, Nick

Nick Ma.
sumber