Dalam bentuk Django, bagaimana cara membuat bidang dibaca hanya (atau dinonaktifkan) sehingga tidak dapat diedit?

431

Dalam bentuk Django, bagaimana cara membuat bidang hanya baca (atau dinonaktifkan)?

Ketika formulir sedang digunakan untuk membuat entri baru, semua bidang harus diaktifkan - tetapi ketika catatan dalam mode pembaruan, beberapa bidang harus hanya-baca.

Misalnya, saat membuat Itemmodel baru , semua bidang harus dapat diedit, tetapi saat memperbarui catatan, apakah ada cara untuk menonaktifkan skubidang sehingga terlihat, tetapi tidak dapat diedit?

class Item(models.Model):
    sku = models.CharField(max_length=50)
    description = models.CharField(max_length=200)
    added_by = models.ForeignKey(User)


class ItemForm(ModelForm):
    class Meta:
        model = Item
        exclude = ('added_by')

def new_item_view(request):
    if request.method == 'POST':
        form = ItemForm(request.POST)
        # Validate and save
    else:
            form = ItemForm()
    # Render the view

Bisakah kelas ItemFormdigunakan kembali? Perubahan apa yang akan diperlukan di kelas model ItemFormatau Item? Apakah saya perlu menulis kelas lain, " ItemUpdateForm", untuk memperbarui item?

def update_item_view(request):
    if request.method == 'POST':
        form = ItemUpdateForm(request.POST)
        # Validate and save
    else:
        form = ItemUpdateForm()
X10
sumber
Lihat juga pertanyaan SO: Mengapa bidang formulir hanya baca di Django merupakan ide yang buruk? @ stackoverflow.com/questions/2902024 , Jawaban yang diterima (oleh Daniel Naab) menangani peretasan POST yang berbahaya.
X10

Jawaban:

422

Seperti yang ditunjukkan dalam jawaban ini , Django 1.9 menambahkan atribut Field.disabled :

Argumen boolean yang dinonaktifkan, ketika disetel ke True, menonaktifkan bidang formulir menggunakan atribut HTML yang dinonaktifkan sehingga tidak dapat diedit oleh pengguna. Bahkan jika seorang pengguna mengutak-atik nilai bidang yang dikirimkan ke server, itu akan diabaikan demi nilai dari data awal formulir.

Dengan Django 1.8 dan yang lebih lama, untuk menonaktifkan entri pada widget dan mencegah peretasan POST berbahaya, Anda harus menggosok input selain mengatur readonlyatribut pada bidang formulir:

class ItemForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(ItemForm, self).__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if instance and instance.pk:
            self.fields['sku'].widget.attrs['readonly'] = True

    def clean_sku(self):
        instance = getattr(self, 'instance', None)
        if instance and instance.pk:
            return instance.sku
        else:
            return self.cleaned_data['sku']

Atau, ganti if instance and instance.pkdengan kondisi lain yang menunjukkan Anda sedang mengedit. Anda juga bisa mengatur atribut disabledpada kolom input, bukan readonly.

The clean_skufungsi akan memastikan bahwa readonlynilai tidak akan ditimpa oleh POST.

Jika tidak, tidak ada bidang formulir Django built-in yang akan memberikan nilai sambil menolak data input terikat. Jika ini yang Anda inginkan, Anda harus membuat yang terpisah ModelFormyang mengecualikan bidang yang tidak dapat diedit, dan cukup mencetaknya di dalam templat Anda.

Daniel Naab
sumber
3
Daniel, Terima kasih telah mengirim jawaban. Tidak jelas bagi saya bagaimana menggunakan kode ini? tidakkah kode ini berfungsi sama untuk mode pembaruan yang baru juga? Bisakah Anda mengedit jawaban Anda untuk memberikan contoh cara menggunakannya untuk formulir baru dan memperbarui? Terima kasih.
X10
8
Kunci untuk contoh Daniel adalah menguji bidang .id. Objek yang baru dibuat akan memiliki id == Tidak Ada. Ngomong-ngomong, salah satu tiket Django terbuka tertua adalah tentang masalah ini. Lihat code.djangoproject.com/ticket/342 .
Peter Rowell
2
@moadeep menambahkan clean_descriptionmetode ke kelas bentuk.
Daniel Naab
3
di linux (ubuntu 15) / chrome v45, readonly mengubah pointer ke "tangan yang dinonaktifkan" tetapi bidang tersebut kemudian dapat diklik. dengan cacat berfungsi seperti yang diharapkan
simone cittadini
7
Jawaban ini perlu diperbarui. Argumen lapangan baru disabledditambahkan dalam Django 1.9. Jika Field.disableddiatur ke True, maka nilai POST untuk itu Fielddiabaikan. Jadi, jika Anda menggunakan 1.9, tidak perlu menimpa clean, cukup atur disabled = True. Periksa jawaban ini .
narendra-choudhary
174

Django 1.9 menambahkan atribut Field.disabled: https://docs.djangoproject.com/en/stable/ref/forms/fields/#disabled

Argumen boolean yang dinonaktifkan, ketika disetel ke True, menonaktifkan bidang formulir menggunakan atribut HTML yang dinonaktifkan sehingga tidak dapat diedit oleh pengguna. Bahkan jika seorang pengguna mengutak-atik nilai bidang yang dikirimkan ke server, itu akan diabaikan demi nilai dari data awal formulir.

Mike Mahmud
sumber
Tidak ada untuk 1,8 LTS?
dnit13
9
ada ide bagaimana kita bisa menggunakan ini di UpdateView? Saat itu menghasilkan bidang dari model ...
bcsanches
6
Jawaban yang benar. Kelas solusi saya MyChangeForm (forms.ModelForm): def __init __ (self, * args, ** kwargs): super (MyChangeForm, self) .__ init __ (* args, ** kwargs) self.fields ['my_field']. Dinonaktifkan = Benar
Vijay Katam
8
Ini adalah pengaturan jawaban yang bermasalah disabled=Trueakan menyebabkan model menjadi meludah ke pengguna dengan kesalahan validasi.
Ben
1
Akan luar biasa jika Anda bisa memasukkan contoh
geoidesic
95

Pengaturan readonlypada widget hanya membuat input di browser hanya-baca. Menambahkan clean_skupengembalian yang instance.skumemastikan nilai bidang tidak akan berubah pada level formulir.

def clean_sku(self):
    if self.instance: 
        return self.instance.sku
    else: 
        return self.fields['sku']

Dengan cara ini Anda dapat menggunakan model (simpan yang tidak dimodifikasi) dan menghindari kesalahan yang diperlukan bidang.

muhuk
sumber
15
+1 Ini adalah cara yang bagus untuk menghindari lebih banyak save () override. Namun, Anda ingin melakukan pemeriksaan contoh sebelum pengembalian (dalam mode komentar tanpa baris baru): "jika self.instance: return self.instance.sku; selain itu: return self.fields ['sku']"
Daniel Naab
2
Untuk baris terakhir, apakah return self.cleaned_data['sku']akan sebaik atau lebih baik? The docs tampaknya menyarankan menggunakan cleaned_data: "Nilai kembali dari metode ini menggantikan nilai yang ada di cleaned_data, sehingga harus menjadi nilai bidang ini dari cleaned_data(bahkan jika metode ini tidak mengubahnya) atau nilai dibersihkan baru."
pianoJames
67

jawaban awalker banyak membantu saya!

Saya telah mengubah contohnya untuk bekerja dengan Django 1.3, menggunakan get_readonly_fields .

Biasanya Anda harus mendeklarasikan sesuatu seperti ini di app/admin.py:

class ItemAdmin(admin.ModelAdmin):
    ...
    readonly_fields = ('url',)

Saya telah beradaptasi dengan cara ini:

# In the admin.py file
class ItemAdmin(admin.ModelAdmin):
    ...
    def get_readonly_fields(self, request, obj=None):
        if obj:
            return ['url']
        else:
            return []

Dan itu bekerja dengan baik. Sekarang jika Anda menambahkan Item, urlbidang ini baca-tulis, tetapi saat diubah itu menjadi hanya-baca.

chirale
sumber
56

Untuk membuat ini berfungsi untuk ForeignKeybidang, beberapa perubahan perlu dilakukan. Pertama, SELECT HTMLtag tidak memiliki atribut readonly. Kita harus menggunakan disabled="disabled"sebagai gantinya. Namun, maka browser tidak mengirim data formulir apa pun untuk bidang itu. Jadi kita perlu mengatur bidang itu agar tidak diperlukan agar bidang tersebut divalidasi dengan benar. Kami kemudian perlu mengatur ulang nilai kembali ke tempat semula sehingga tidak disetel ke kosong.

Jadi untuk kunci asing Anda perlu melakukan sesuatu seperti:

class ItemForm(ModelForm):

    def __init__(self, *args, **kwargs):
        super(ItemForm, self).__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if instance and instance.id:
            self.fields['sku'].required = False
            self.fields['sku'].widget.attrs['disabled'] = 'disabled'

    def clean_sku(self):
        # As shown in the above answer.
        instance = getattr(self, 'instance', None)
        if instance:
            return instance.sku
        else:
            return self.cleaned_data.get('sku', None)

Dengan cara ini browser tidak akan membiarkan pengguna mengubah bidang, dan akan selalu POSTseperti itu dibiarkan kosong. Kami kemudian mengganti cleanmetode untuk menetapkan nilai bidang menjadi apa yang awalnya dalam contoh.

Humphrey
sumber
Saya mencoba menggunakannya sebagai formulir TabularInline, tetapi gagal karena attrsdibagikan di antara widgetinstance dan semua kecuali baris pertama, termasuk yang baru ditambahkan, hanya dibaca yang dibuat.
bukit pasir
Solusi (pembaruan) yang hebat! Sayangnya ini dan sisanya memiliki masalah ketika ada kesalahan bentuk karena semua nilai "dinonaktifkan" dikosongkan.
Michael Thompson
28

Untuk Django 1.2+, Anda dapat mengganti bidang seperti:

sku = forms.CharField(widget = forms.TextInput(attrs={'readonly':'readonly'}))
StefanNch
sumber
6
Ini tidak memungkinkan bidang untuk diedit pada waktu tambahan juga, yang merupakan pertanyaan asli.
Matt S.
Ini jawaban yang saya cari. Field disabledtidak melakukan apa yang saya inginkan karena menonaktifkan bidang, tetapi juga menghilangkan label / membuatnya tidak terlihat.
sivabudh
18

Saya membuat kelas MixIn yang dapat Anda warisi untuk dapat menambahkan bidang read_only iterable yang akan menonaktifkan dan mengamankan bidang pada pengeditan non-pertama:

(Berdasarkan jawaban Daniel dan Muhuk)

from django import forms
from django.db.models.manager import Manager

# I used this instead of lambda expression after scope problems
def _get_cleaner(form, field):
    def clean_field():
         value = getattr(form.instance, field, None)
         if issubclass(type(value), Manager):
             value = value.all()
         return value
    return clean_field

class ROFormMixin(forms.BaseForm):
    def __init__(self, *args, **kwargs):
        super(ROFormMixin, self).__init__(*args, **kwargs)
        if hasattr(self, "read_only"):
            if self.instance and self.instance.pk:
                for field in self.read_only:
                    self.fields[field].widget.attrs['readonly'] = "readonly"
                    setattr(self, "clean_" + field, _get_cleaner(self, field))

# Basic usage
class TestForm(AModelForm, ROFormMixin):
    read_only = ('sku', 'an_other_field')
christophe31
sumber
11

Saya baru saja membuat widget sesederhana mungkin untuk bidang hanya baca - Saya tidak benar-benar mengerti mengapa formulir belum memiliki ini:

class ReadOnlyWidget(widgets.Widget):
    """Some of these values are read only - just a bit of text..."""
    def render(self, _, value, attrs=None):
        return value

Dalam bentuk:

my_read_only = CharField(widget=ReadOnlyWidget())

Sangat sederhana - dan hanya membuat saya output. Berguna dalam format dengan banyak nilai hanya baca. Tentu saja - Anda juga bisa menjadi sedikit lebih pintar dan memberikan div dengan attrs sehingga Anda dapat menambahkan kelas ke dalamnya.

Danny Staple
sumber
2
Terlihat seksi, tetapi bagaimana menangani kunci asing?
andilabs
Buat itu unicode(value)sebagai gantinya mungkin. Dengan anggapan unicode dunder masuk akal, Anda akan mendapatkannya.
Danny Staple
Untuk kunci asing, Anda harus menambahkan atribut "model" dan menggunakan "get (value)". Lihat intisari saya
shadi
10

Saya menemukan masalah yang sama. Sepertinya saya bisa menyelesaikannya dengan mendefinisikan metode "get_readonly_fields" di kelas ModelAdmin saya.

Sesuatu seperti ini:

# In the admin.py file

class ItemAdmin(admin.ModelAdmin):

    def get_readonly_display(self, request, obj=None):
        if obj:
            return ['sku']
        else:
            return []

Yang menyenangkan adalah itu obj akan ada ketika Anda menambahkan item baru, atau itu akan menjadi objek yang sedang diedit ketika Anda mengubah item yang sudah ada.

get_readonly_display didokumentasikan di sini: http://docs.djangoproject.com/en/1.2/ref/contrib/admin/#modeladmin-methods

tukang awang
sumber
6

Salah satu opsi sederhana adalah dengan mengetikkan form.instance.fieldNametemplat alih-alih form.fieldName.

alzclarke
sumber
Dan bagaimana dengan verbos_nameatau labellapangan? Bagaimana saya bisa menampilkan `label dalam template Django? @alzclarke
Paus 52Hz
6

Bagaimana saya melakukannya dengan Django 1.11:

class ItemForm(ModelForm):
    disabled_fields = ('added_by',)

    class Meta:
        model = Item
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super(ItemForm, self).__init__(*args, **kwargs)
        for field in self.disabled_fields:
            self.fields[field].disabled = True
Lucas B
sumber
ini hanya akan memblokir dari fronted. siapa pun dapat melewati. ini akan membuat masalah keamanan jika Anda melakukan pada data sensitif
Sarath Ak
Itu aman; itu juga memblokir di backend sejak Django> = 1.10 docs.djangoproject.com/en/1.10/ref/forms/fields/…
timdiels
5

Sebagai tambahan yang berguna untuk posting Humphrey , saya memiliki beberapa masalah dengan Django-reversion, karena masih mendaftarkan bidang yang dinonaktifkan sebagai 'diubah'. Kode berikut memperbaiki masalah.

class ItemForm(ModelForm):

    def __init__(self, *args, **kwargs):
        super(ItemForm, self).__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if instance and instance.id:
            self.fields['sku'].required = False
            self.fields['sku'].widget.attrs['disabled'] = 'disabled'

    def clean_sku(self):
        # As shown in the above answer.
        instance = getattr(self, 'instance', None)
        if instance:
            try:
                self.changed_data.remove('sku')
            except ValueError, e:
                pass
            return instance.sku
        else:
            return self.cleaned_data.get('sku', None)
Evan Brumley
sumber
5

Karena saya belum bisa berkomentar ( solusi muhuk ), saya akan menjawab sebagai jawaban terpisah. Ini adalah contoh kode lengkap, yang berfungsi untuk saya:

def clean_sku(self):
  if self.instance and self.instance.pk:
    return self.instance.sku
  else:
    return self.cleaned_data['sku']
Madis
sumber
5

Sekali lagi, saya akan menawarkan satu solusi lagi :) Saya menggunakan kode Humphrey , jadi ini didasarkan pada itu.

Namun, saya mengalami masalah dengan bidang yang menjadi a ModelChoiceField. Semuanya akan bekerja berdasarkan permintaan pertama. Namun, jika formset mencoba menambahkan item baru dan gagal validasi, ada yang salah dengan formulir "yang ada" di mana SELECTEDopsi sedang diatur ulang ke default ---------.

Lagi pula, saya tidak tahu bagaimana cara memperbaikinya. Jadi sebagai gantinya, (dan saya pikir ini sebenarnya lebih bersih dalam bentuk), saya membuat ladang HiddenInputField(). Ini berarti Anda harus melakukan sedikit lebih banyak pekerjaan di template.

Jadi perbaikan untuk saya adalah menyederhanakan Formulir:

class ItemForm(ModelForm):

    def __init__(self, *args, **kwargs):
        super(ItemForm, self).__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if instance and instance.id:
            self.fields['sku'].widget=HiddenInput()

Dan kemudian di templat, Anda harus melakukan beberapa perulangan manual dari formset .

Jadi, dalam hal ini Anda akan melakukan sesuatu seperti ini di templat:

<div>
    {{ form.instance.sku }} <!-- This prints the value -->
    {{ form }} <!-- Prints form normally, and makes the hidden input -->
</div>

Ini bekerja sedikit lebih baik untuk saya dan dengan manipulasi bentuk yang lebih sedikit.

JamesD
sumber
4

Saya mengalami masalah yang sama jadi saya membuat Mixin yang sepertinya berfungsi untuk kasus penggunaan saya.

class ReadOnlyFieldsMixin(object):
    readonly_fields =()

    def __init__(self, *args, **kwargs):
        super(ReadOnlyFieldsMixin, self).__init__(*args, **kwargs)
        for field in (field for name, field in self.fields.iteritems() if name in self.readonly_fields):
            field.widget.attrs['disabled'] = 'true'
            field.required = False

    def clean(self):
        cleaned_data = super(ReadOnlyFieldsMixin,self).clean()
        for field in self.readonly_fields:
           cleaned_data[field] = getattr(self.instance, field)

        return cleaned_data

Penggunaan, cukup tentukan yang mana yang harus dibaca saja:

class MyFormWithReadOnlyFields(ReadOnlyFieldsMixin, MyForm):
    readonly_fields = ('field1', 'field2', 'fieldx')
Michael
sumber
Saya kira itu sedikit lebih mudah dibaca daripada mixin saya sendiri yang saya sarankan di sini. Bahkan mungkin lebih efisien, karena pembersihan itu tidak meningkatkan kesalahan validasi ...
christophe31
Saya mendapat pesan kesalahan:'collections.OrderedDict' object has no attribute 'iteritems'
geoidesic
4

jika Anda membutuhkan beberapa bidang baca-saja. Anda dapat menggunakan salah satu metode yang diberikan di bawah ini

metode 1

class ItemForm(ModelForm):
    readonly = ('sku',)

    def __init__(self, *arg, **kwrg):
        super(ItemForm, self).__init__(*arg, **kwrg)
        for x in self.readonly:
            self.fields[x].widget.attrs['disabled'] = 'disabled'

    def clean(self):
        data = super(ItemForm, self).clean()
        for x in self.readonly:
            data[x] = getattr(self.instance, x)
        return data

metode 2

metode pewarisan

class AdvancedModelForm(ModelForm):


    def __init__(self, *arg, **kwrg):
        super(AdvancedModelForm, self).__init__(*arg, **kwrg)
        if hasattr(self, 'readonly'):
            for x in self.readonly:
                self.fields[x].widget.attrs['disabled'] = 'disabled'

    def clean(self):
        data = super(AdvancedModelForm, self).clean()
        if hasattr(self, 'readonly'):
            for x in self.readonly:
                data[x] = getattr(self.instance, x)
        return data


class ItemForm(AdvancedModelForm):
    readonly = ('sku',)
Sarath Ak
sumber
3

Dua lagi pendekatan (serupa) dengan satu contoh umum:

1) pendekatan pertama - menghapus bidang dalam metode save (), mis. (Tidak diuji;)):

def save(self, *args, **kwargs):
    for fname in self.readonly_fields:
        if fname in self.cleaned_data:
            del self.cleaned_data[fname]
    return super(<form-name>, self).save(*args,**kwargs)

2) pendekatan kedua - setel ulang bidang ke nilai awal dalam metode bersih:

def clean_<fieldname>(self):
    return self.initial[<fieldname>] # or getattr(self.instance, fieldname)

Berdasarkan pendekatan kedua saya menggeneralisasikannya seperti ini:

from functools                 import partial

class <Form-name>(...):

    def __init__(self, ...):
        ...
        super(<Form-name>, self).__init__(*args, **kwargs)
        ...
        for i, (fname, field) in enumerate(self.fields.iteritems()):
            if fname in self.readonly_fields:
                field.widget.attrs['readonly'] = "readonly"
                field.required = False
                # set clean method to reset value back
                clean_method_name = "clean_%s" % fname
                assert clean_method_name not in dir(self)
                setattr(self, clean_method_name, partial(self._clean_for_readonly_field, fname=fname))

    def _clean_for_readonly_field(self, fname):
        """ will reset value to initial - nothing will be changed 
            needs to be added dynamically - partial, see init_fields
        """
        return self.initial[fname] # or getattr(self.instance, fieldname)
Robert Lujo
sumber
3

Untuk versi Admin, saya pikir ini adalah cara yang lebih ringkas jika Anda memiliki lebih dari satu bidang:

def get_readonly_fields(self, request, obj=None):
    skips = ('sku', 'other_field')
    fields = super(ItemAdmin, self).get_readonly_fields(request, obj)

    if not obj:
        return [field for field in fields if not field in skips]
    return fields
Hassek
sumber
3

Berdasarkan jawaban Yamikep , saya menemukan solusi yang lebih baik dan sangat sederhana yang juga menangani ModelMultipleChoiceFieldbidang.

Menghapus bidang dari form.cleaned_datamencegah bidang disimpan:

class ReadOnlyFieldsMixin(object):
    readonly_fields = ()

    def __init__(self, *args, **kwargs):
        super(ReadOnlyFieldsMixin, self).__init__(*args, **kwargs)
        for field in (field for name, field in self.fields.iteritems() if
                      name in self.readonly_fields):
            field.widget.attrs['disabled'] = 'true'
            field.required = False

    def clean(self):
        for f in self.readonly_fields:
            self.cleaned_data.pop(f, None)
        return super(ReadOnlyFieldsMixin, self).clean()

Pemakaian:

class MyFormWithReadOnlyFields(ReadOnlyFieldsMixin, MyForm):
    readonly_fields = ('field1', 'field2', 'fieldx')
darklow
sumber
2

Ini adalah versi yang sedikit lebih terlibat, berdasarkan jawaban christophe31 . Itu tidak bergantung pada atribut "readonly". Ini membuat masalah, seperti kotak pilih masih dapat diubah dan datapickers masih muncul, pergi.

Alih-alih, itu membungkus widget bidang formulir di widget hanya baca, sehingga membuat formulir masih valid. Konten widget asli ditampilkan di dalam <span class="hidden"></span>tag. Jika widget memiliki render_readonly()metode yang digunakannya sebagai teks yang terlihat, jika tidak, ia mem-parsing HTML widget asli dan mencoba menebak representasi terbaik.

import django.forms.widgets as f
import xml.etree.ElementTree as etree
from django.utils.safestring import mark_safe

def make_readonly(form):
    """
    Makes all fields on the form readonly and prevents it from POST hacks.
    """

    def _get_cleaner(_form, field):
        def clean_field():
            return getattr(_form.instance, field, None)
        return clean_field

    for field_name in form.fields.keys():
        form.fields[field_name].widget = ReadOnlyWidget(
            initial_widget=form.fields[field_name].widget)
        setattr(form, "clean_" + field_name, 
                _get_cleaner(form, field_name))

    form.is_readonly = True

class ReadOnlyWidget(f.Select):
    """
    Renders the content of the initial widget in a hidden <span>. If the
    initial widget has a ``render_readonly()`` method it uses that as display
    text, otherwise it tries to guess by parsing the html of the initial widget.
    """

    def __init__(self, initial_widget, *args, **kwargs):
        self.initial_widget = initial_widget
        super(ReadOnlyWidget, self).__init__(*args, **kwargs)

    def render(self, *args, **kwargs):
        def guess_readonly_text(original_content):
            root = etree.fromstring("<span>%s</span>" % original_content)

            for element in root:
                if element.tag == 'input':
                    return element.get('value')

                if element.tag == 'select':
                    for option in element:
                        if option.get('selected'):
                            return option.text

                if element.tag == 'textarea':
                    return element.text

            return "N/A"

        original_content = self.initial_widget.render(*args, **kwargs)
        try:
            readonly_text = self.initial_widget.render_readonly(*args, **kwargs)
        except AttributeError:
            readonly_text = guess_readonly_text(original_content)

        return mark_safe("""<span class="hidden">%s</span>%s""" % (
            original_content, readonly_text))

# Usage example 1.
self.fields['my_field'].widget = ReadOnlyWidget(self.fields['my_field'].widget)

# Usage example 2.
form = MyForm()
make_readonly(form)
Rune Kaagaard
sumber
1

Apakah ini cara paling sederhana?

Tepat di kode tampilan, kira-kira seperti ini:

def resume_edit(request, r_id):
    .....    
    r = Resume.get.object(pk=r_id)
    resume = ResumeModelForm(instance=r)
    .....
    resume.fields['email'].widget.attrs['readonly'] = True 
    .....
    return render(request, 'resumes/resume.html', context)

Ini bekerja dengan baik!

fly_frog
sumber
1

Untuk django 1.9+
Anda bisa menggunakan argumen Fields disable untuk membuat field dinonaktifkan. misalnya Dalam cuplikan kode berikut dari file forms.py, saya telah membuat bidang employee_code dinonaktifkan

class EmployeeForm(forms.ModelForm):
    employee_code = forms.CharField(disabled=True)
    class Meta:
        model = Employee
        fields = ('employee_code', 'designation', 'salary')

Referensi https://docs.djangoproject.com/en/2.0/ref/forms/fields/#disabled

Ajinkya Bhosale
sumber
1

Jika Anda bekerja dengan Django ver < 1.9( atribut 1.9telah ditambahkan Field.disabled), Anda bisa mencoba menambahkan dekorator berikut ke __init__metode formulir Anda :

def bound_data_readonly(_, initial):
    return initial


def to_python_readonly(field):
    native_to_python = field.to_python

    def to_python_filed(_):
        return native_to_python(field.initial)

    return to_python_filed


def disable_read_only_fields(init_method):

    def init_wrapper(*args, **kwargs):
        self = args[0]
        init_method(*args, **kwargs)
        for field in self.fields.values():
            if field.widget.attrs.get('readonly', None):
                field.widget.attrs['disabled'] = True
                setattr(field, 'bound_data', bound_data_readonly)
                setattr(field, 'to_python', to_python_readonly(field))

    return init_wrapper


class YourForm(forms.ModelForm):

    @disable_read_only_fields
    def __init__(self, *args, **kwargs):
        ...

Gagasan utamanya adalah jika bidang adalah readonlyAnda tidak memerlukan nilai lain selain initial.

PS: Jangan lupa mengatur yuor_form_field.widget.attrs['readonly'] = True

Yaroslav Varkhol
sumber
0

Jika Anda menggunakan admin Django, berikut adalah solusi paling sederhana.

class ReadonlyFieldsMixin(object):
    def get_readonly_fields(self, request, obj=None):
        if obj:
            return super(ReadonlyFieldsMixin, self).get_readonly_fields(request, obj)
        else:
            return tuple()

class MyAdmin(ReadonlyFieldsMixin, ModelAdmin):
    readonly_fields = ('sku',)
utapyngo
sumber
0

Saya pikir pilihan terbaik Anda hanya akan menyertakan atribut readonly dalam template Anda yang diberikan dalam <span>atau <p>bukan memasukkannya ke dalam formulir jika hanya dibaca.

Formulir adalah untuk mengumpulkan data, bukan menampilkannya. Yang sedang berkata, opsi untuk menampilkan di readonlywidget dan menggosok data POST adalah solusi bagus.

austinheiman
sumber