एक Django के रूप में, मैं एक क्षेत्र को आसानी से (या अक्षम) कैसे बना सकता हूं ताकि इसे संपादित नहीं किया जा सके?


430

एक Django के रूप में, मैं केवल पढ़ने के लिए (या अक्षम) फ़ील्ड कैसे बनाऊँ?

जब फॉर्म का उपयोग नई प्रविष्टि बनाने के लिए किया जा रहा है, तो सभी फ़ील्ड सक्षम होनी चाहिए - लेकिन जब रिकॉर्ड अपडेट मोड में होता है तो कुछ फ़ील्ड को केवल-पढ़ने के लिए आवश्यक है।

उदाहरण के लिए, एक नया Itemमॉडल बनाते समय , सभी क्षेत्रों को संपादन योग्य होना चाहिए, लेकिन रिकॉर्ड को अपडेट करते समय, क्या skuफ़ील्ड को अक्षम करने का कोई तरीका है ताकि यह दिखाई दे, लेकिन संपादित नहीं किया जा सकता है?

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

क्या कक्षा ItemFormका पुन: उपयोग किया जा सकता है? ItemFormया Itemमॉडल वर्ग में किन बदलावों की आवश्यकता होगी ? क्या मुझे ItemUpdateFormआइटम को अपडेट करने के लिए एक और वर्ग, " " लिखना होगा ?

def update_item_view(request):
    if request.method == 'POST':
        form = ItemUpdateForm(request.POST)
        # Validate and save
    else:
        form = ItemUpdateForm()

SO प्रश्न भी देखें: Django में केवल-फॉर्म फ़ील्ड को एक बुरा विचार क्यों कहा जाता है? @ stackoverflow.com/questions/2902024 , स्वीकृत उत्तर (डैनियल नब द्वारा) दुर्भावनापूर्ण POST हैक का ख्याल रखता है।
X10

जवाबों:


421

जैसा कि इस उत्तर में बताया गया है , Django 1.9 ने Field.disabled विशेषता को जोड़ा :

अक्षम बूलियन तर्क, जब True पर सेट होता है, अक्षम HTML विशेषता का उपयोग करते हुए एक फॉर्म फ़ील्ड अक्षम करता है ताकि यह उपयोगकर्ताओं द्वारा संपादन योग्य न हो। यहां तक ​​कि अगर कोई उपयोगकर्ता सर्वर पर सबमिट किए गए फ़ील्ड के मान के साथ छेड़छाड़ करता है, तो इसे फॉर्म के प्रारंभिक डेटा से मूल्य के पक्ष में अनदेखा किया जाएगा।

Django 1.8 और पहले के साथ, विजेट पर प्रविष्टि को निष्क्रिय करने और दुर्भावनापूर्ण POST हैक को रोकने के लिए आपको readonlyफॉर्म फ़ील्ड पर विशेषता सेट करने के अलावा इनपुट को साफ़ करना होगा :

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']

या, if instance and instance.pkएक और शर्त के साथ बदलें जो यह दर्शाता है कि आप संपादन कर रहे हैं। आप disabledइसके बजाय, इनपुट फ़ील्ड पर विशेषता भी सेट कर सकते हैं readonly

clean_skuसमारोह सुनिश्चित करेंगे कि readonlyमूल्य एक से ओवरराइड नहीं होगा POST

अन्यथा, कोई अंतर्निहित Django प्रपत्र फ़ील्ड नहीं है जो बाध्य इनपुट डेटा को अस्वीकार करते हुए एक मूल्य प्रदान करेगा। यदि यह आपकी इच्छा है, तो आपको इसके बजाय एक अलग ModelFormक्षेत्र बनाना चाहिए जो कि अप्रयुक्त क्षेत्र को बाहर करता है, और बस उन्हें अपने टेम्पलेट के अंदर प्रिंट करें।


3
डैनियल, एक उत्तर पोस्ट करने के लिए धन्यवाद। यह मेरे लिए स्पष्ट नहीं है कि इस कोड का उपयोग कैसे करें? क्या यह कोड नए और साथ ही अपडेट मोड के लिए काम नहीं करेगा? क्या आप नए और अद्यतन रूपों के लिए इसका उपयोग करने के बारे में उदाहरण देने के लिए अपने उत्तर को संपादित कर सकते हैं? धन्यवाद।
एक्स 10

8
डैनियल के उदाहरण की कुंजी .id क्षेत्र का परीक्षण कर रही है। नई बनाई गई वस्तुओं में id == कोई नहीं होगा। वैसे, इस मुद्दे के बारे में सबसे पुराने खुले Django टिकटों में से एक है। Code.djangoproject.com/ticket/342 देखें ।
पीटर रॉवेल

2
@moadeep clean_descriptionफॉर्म क्लास में एक विधि जोड़ें ।
डैनियल नाब

3
linux (ubuntu 15) / chrome v45 पर, आसानी से पॉइंटर को "डिसएबल हैंड" में बदल देता है लेकिन फील्ड तब क्लिक करने योग्य होता है। विकलांगों के साथ यह उम्मीद के
मुताबिक

7
इस उत्तर को अद्यतन करने की आवश्यकता है। disabledDjango 1.9 में एक नया फ़ील्ड तर्क जोड़ा गया है। यदि Field.disabledसेट किया गया है True, तो उसके लिए POST मान Fieldको अनदेखा किया जाता है। इसलिए यदि आप 1.9 का उपयोग कर रहे हैं, तो ओवरराइड करने की कोई आवश्यकता नहीं है clean, बस सेट करें disabled = Trueइस उत्तर की जाँच करें
नरेन्द्र-चौधरी

174

Django 1.9 ने Field.disabled विशेषता जोड़ी: https://docs.djangoproject.com/en/stable/ref/forms/fields/#disabled

अक्षम बूलियन तर्क, जब True पर सेट होता है, अक्षम HTML विशेषता का उपयोग करते हुए एक फॉर्म फ़ील्ड अक्षम करता है ताकि यह उपयोगकर्ताओं द्वारा संपादन योग्य न हो। यहां तक ​​कि अगर कोई उपयोगकर्ता सर्वर पर सबमिट किए गए फ़ील्ड के मान के साथ छेड़छाड़ करता है, तो इसे फॉर्म के प्रारंभिक डेटा से मूल्य के पक्ष में अनदेखा किया जाएगा।


1.8 एलटीएस के लिए कुछ भी नहीं?
dnit13

9
किसी भी विचार कैसे हम एक UpdateView पर इसका उपयोग कर सकते हैं? जैसा कि यह मॉडल से खेतों को उत्पन्न करता है ...
18 सितंबर को bcsanches

6
सही उत्तर। मेरा समाधान वर्ग MyChangeForm (फ़ॉर्म। ModelForm): __init __ (self, * args, ** kwargs) को हराया: super (MyChangeForm, self) .__ init __ (* args, ** kwargs) self.fields ['my_field'] को निष्क्रिय कर दिया गया। सच
विजय कटम

8
यह एक समस्याग्रस्त उत्तर है - सेटिंग के disabled=Trueकारण उपयोगकर्ता सत्यापन त्रुटियों के साथ उपयोगकर्ता को वापस भेज दिया जाएगा।
बेन

1
भयानक होगा यदि आप एक उदाहरण शामिल कर सकते हैं
जियोसाइक्लिक

95

readonlyएक विजेट पर सेटिंग केवल ब्राउज़र में इनपुट को रीड-ओनली बनाती है। एक clean_skuरिटर्न जोड़ने से instance.skuयह सुनिश्चित होता है कि फ़ील्ड मान फॉर्म स्तर पर नहीं बदलेगा।

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

इस तरह से आप मॉडल (अनमॉडिफाइड सेव) का उपयोग कर सकते हैं और क्षेत्र की आवश्यक त्रुटि प्राप्त करने से बच सकते हैं।


15
+1 अधिक जटिल सेव () ओवरराइड से बचने का यह एक शानदार तरीका है। हालाँकि, आप वापसी से पहले एक उदाहरण जाँच करना चाहते हैं (newline-less कमेंट मोड में): "if self.instance: return self.instance.sku; बाकी: self.fields ['sku']]
डैनियल नाब

2
अंतिम पंक्ति के लिए, return self.cleaned_data['sku']अच्छा या बेहतर होगा? डॉक्स उपयोग करने का सुझाव करने लगते हैं cleaned_data: "इस विधि का वापसी मान में मौजूदा मूल्य की जगह cleaned_dataहै, इसलिए इसे से फ़ील्ड का मान होना चाहिए cleaned_dataया (भले ही इस विधि इसे बदल नहीं किया था) एक नया साफ मूल्य।"
पियानोजम्स

67

awalker के जवाब से मुझे बहुत मदद मिली!

मैंने Django 1.3 के साथ काम करने के लिए उसका उदाहरण बदल दिया है, get_readonly_fields का उपयोग करके

आमतौर पर आपको इस तरह से कुछ घोषित करना चाहिए app/admin.py:

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

मैंने इस तरह से अनुकूलित किया है:

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

और यह ठीक काम करता है। अब यदि आप एक आइटम जोड़ते हैं, तो urlफ़ील्ड रीड-राइट है, लेकिन बदलने पर यह केवल रीड-ओनली हो जाता है।


55

किसी ForeignKeyक्षेत्र के लिए यह काम करने के लिए , कुछ बदलाव करने की आवश्यकता है। सबसे पहले, SELECT HTMLटैग में आसानी से विशेषता नहीं है। हमें disabled="disabled"इसके बजाय उपयोग करने की आवश्यकता है । हालाँकि, तब ब्राउज़र उस फ़ील्ड के लिए कोई भी प्रपत्र डेटा वापस नहीं भेजता है। इसलिए हमें उस फ़ील्ड को सेट करने की आवश्यकता नहीं है ताकि फ़ील्ड सही तरीके से मान्य हो। इसके बाद हमें उस मूल्य को वापस रीसेट करने की आवश्यकता है जो पहले हुआ करता था इसलिए यह रिक्त पर सेट नहीं है।

तो विदेशी कुंजी के लिए आपको कुछ करने की आवश्यकता होगी:

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)

इस तरह से ब्राउज़र उपयोगकर्ता को क्षेत्र को बदलने नहीं देगा, और हमेशा POSTवैसा ही रहेगा जैसा वह खाली छोड़ दिया गया था। हम तब cleanक्षेत्र के मान को निर्धारित करने की विधि को ओवरराइड करते हैं जो मूल रूप से उदाहरण के लिए था।


मैंने इसे फॉर्म के रूप में उपयोग करने की कोशिश की TabularInline, लेकिन विफल रहा क्योंकि उदाहरणों और सभी के attrsबीच साझा किए गए थे , लेकिन widgetपहली पंक्ति, नए जोड़े सहित, केवल पढ़ने के लिए प्रदान की गई।
ढिल्ल

एक महान (अद्यतन) समाधान! दुर्भाग्य से यह और बाकी मुद्दे हैं जब सभी "अक्षम" मानों को खाली करने के रूप में फार्म त्रुटियां हैं।
माइकल थॉम्पसन

28

Django 1.2+ के लिए, आप फ़ील्ड को इस तरह से ओवरराइड कर सकते हैं:

sku = forms.CharField(widget = forms.TextInput(attrs={'readonly':'readonly'}))

6
यह फ़ील्ड को ऐड टाइम पर संपादित करने की अनुमति नहीं देता है, जो कि मूल प्रश्न के बारे में है।
मैट एस।

यह वह उत्तर है जिसकी मुझे तलाश है। Field disabledमैं वह नहीं करता जो मैं चाहता हूं क्योंकि यह क्षेत्र को निष्क्रिय करता है, लेकिन यह लेबल को भी हटा देता है / अदृश्य बना देता है।
शिवबुध

18

मैंने एक मिक्स इन क्लास बनाया, जो आपको पढ़ने में सक्षम होने के लिए विरासत में मिल सकता है।

(डैनियल के और 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')

11

मैंने अभी-अभी रीड-ओनली फ़ील्ड के लिए सबसे सरल संभव विजेट बनाया है - मैं वास्तव में यह नहीं देखता कि रूपों में यह पहले से क्यों नहीं है:

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

फार्म में:

my_read_only = CharField(widget=ReadOnlyWidget())

बहुत सरल - और मुझे सिर्फ आउटपुट मिलता है। केवल पढ़ने के मूल्यों के एक समूह के साथ एक फॉर्मेट में काम करना। बेशक - आप थोड़ा अधिक चतुर भी हो सकते हैं और इसे एट्रस के साथ एक डिव दे सकते हैं ताकि आप इसमें कक्षाएं जोड़ सकें।


2
सेक्सी लग रहा है, लेकिन विदेशी कुंजी को कैसे संभालना है?
औरिलबस 14

unicode(value)शायद बदले में वापसी करें । यूनिकोड डण्डर को समझदार मानने पर, आपको वह मिल जाएगा।
डैनी स्टेपल

विदेशी कुंजियों के लिए, आपको "मॉडल" विशेषता जोड़ने और "गेट (मान)" का उपयोग करने की आवश्यकता होगी। चेक मेरी सार
shadi

10

मैं एक ऐसी ही समस्या के लिए भाग गया। ऐसा लगता है कि मैं अपने ModelAdmin वर्ग में "get_readonly_fields" पद्धति को परिभाषित करके इसे हल करने में सक्षम था।

कुछ इस तरह:

# In the admin.py file

class ItemAdmin(admin.ModelAdmin):

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

अच्छी बात यह है कि objजब आप एक नया आइटम जोड़ रहे हैं तो कोई भी नहीं होगा, या यह तब होगा जब आप किसी मौजूदा आइटम को बदल रहे हों।

get_readonly_display यहां प्रलेखित है: http://docs.djangoproject.com/en/1.2/ref/contrib/admin/#modeladmin-methods


6

एक सरल विकल्प के form.instance.fieldNameबजाय सिर्फ टेम्पलेट में टाइप करना है form.fieldName


और कैसे के बारे में verbos_nameया labelमैदान के? मैं django टेम्पलेट में `लेबल कैसे दिखा सकता हूं? @alzclarke
व्हेल 52Hz

6

मैं इसे 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

यह केवल फ्रंट से ब्लॉक होगा। कोई भी बायपास कर सकता है। यदि आप संवेदनशील डेटा पर कर रहे हैं तो यह एक सुरक्षा समस्या बना देगा
सारथ एक

यह सुरक्षित है; यह Django> = 1.10 docs.djangoproject.com/en/1.10/ref/forms/fields/…
timdiels

5

हम्फ्रे के पद के लिए एक उपयोगी जोड़ के रूप में , मुझे django-प्रत्यावर्तन के साथ कुछ मुद्दे थे, क्योंकि यह अभी भी अक्षम फ़ील्ड को 'बदल' के रूप में पंजीकृत करता है। निम्न कोड समस्या को हल करता है।

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)

5

जैसा कि मैं अभी तक टिप्पणी नहीं कर सकता ( मुहुच का समाधान ), मैं एक अलग उत्तर के रूप में प्रतिक्रिया दूंगा। यह एक पूर्ण कोड उदाहरण है, जो मेरे लिए काम करता है:

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

5

फिर भी, मैं एक और समाधान की पेशकश करने जा रहा हूं :) मैं हम्फ्रे के कोड का उपयोग कर रहा था , इसलिए यह उस पर आधारित है।

हालाँकि, मैं इस क्षेत्र में मुद्दों के साथ भाग गया ModelChoiceField। सब कुछ पहले अनुरोध पर काम करेगा। हालाँकि, यदि फॉर्मेट में एक नया आइटम जोड़ने की कोशिश की गई और सत्यापन विफल हो गया, तो कुछ "मौजूदा" रूपों के साथ गलत हो रहा था, जहां SELECTEDविकल्प को डिफ़ॉल्ट पर रीसेट किया जा रहा था।---------

वैसे भी, मैं यह पता नहीं लगा सका कि इसे कैसे ठीक किया जाए। इसलिए इसके बजाय, (और मुझे लगता है कि यह वास्तव में फॉर्म में क्लीनर है), मैंने खेतों को बनाया HiddenInputField()। इसका मतलब यह है कि आपको टेम्पलेट में थोड़ा और काम करना होगा।

तो मेरे लिए यह तय था कि फॉर्म को सरल बनाया जाए:

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()

और फिर टेम्पलेट में, आपको फॉर्मेट के कुछ मैनुअल लूपिंग करने की आवश्यकता होगी

तो, इस मामले में आप कुछ इस तरह से करेंगे:

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

इसने मेरे लिए थोड़ा बेहतर काम किया और कम फॉर्म हेरफेर के साथ।


4

मैं उसी समस्या में जा रहा था इसलिए मैंने एक मिक्सिन बनाया जो मेरे उपयोग के मामलों के लिए काम करता है।

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

उपयोग, केवल परिभाषित करें कि किन लोगों को केवल पढ़ा जाना चाहिए:

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

मुझे लगता है कि यह मेरे द्वारा सुझाए गए मिश्रण के मुकाबले थोड़ा अधिक पठनीय है। शायद और भी अधिक कुशल, क्योंकि वे सफाई मान्यताओं की त्रुटियों को नहीं बढ़ाते हैं ...
क्रिश्चोफेई

मुझे एक त्रुटि मिलती है:'collections.OrderedDict' object has no attribute 'iteritems'
जियोसाइडिक

4

यदि आपकी जरूरत है केवल पढ़ने के लिए फ़ील्ड। आप नीचे दिए गए किसी भी तरीके का उपयोग कर सकते हैं

विधि 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

विधि 2

वंशानुक्रम विधि

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',)

3

एक सामान्यीकृत उदाहरण के साथ दो और (समान) दृष्टिकोण:

1) पहला दृष्टिकोण - सेव () विधि में फील्ड को हटाना, उदाहरण के लिए (परीक्षण नहीं किया गया;)):

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) दूसरा तरीका - साफ विधि में प्रारंभिक मूल्य पर फ़ील्ड रीसेट करें:

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

दूसरे दृष्टिकोण के आधार पर मैंने इसे इस तरह सामान्यीकृत किया:

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)

3

व्यवस्थापक संस्करण के लिए, मुझे लगता है कि यह एक अधिक कॉम्पैक्ट तरीका है यदि आपके पास एक से अधिक फ़ील्ड हैं:

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

3

यमीकैप के जवाब के आधार पर , मुझे एक बेहतर और बहुत सरल समाधान मिला, जो ModelMultipleChoiceFieldखेतों को भी संभालता है।

फ़ील्ड को हटाने से form.cleaned_dataफ़ील्ड्स को सहेजने से रोकता है:

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()

उपयोग:

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

2

यहाँ एक और अधिक शामिल संस्करण है, जो क्रिस्टोफ 31 के उत्तर पर आधारित है । यह "पठनीय" विशेषता पर निर्भर नहीं करता है। यह इसकी समस्याएँ बनाता है, जैसे चुनिंदा बक्से अभी भी परिवर्तनशील हैं और डेटापैकर अभी भी पॉप अप कर रहे हैं, चले जाओ।

इसके बजाय, यह प्रपत्र फ़ील्ड विजेट को एक पढ़ने योग्य विजेट में लपेटता है, इस प्रकार फ़ॉर्म को अभी भी मान्य बनाता है। मूल विजेट की सामग्री को <span class="hidden"></span>टैग के अंदर प्रदर्शित किया जाता है । यदि विजेट में एक render_readonly()विधि है जो इसे दृश्य पाठ के रूप में उपयोग करता है, अन्यथा यह मूल विजेट के HTML को पार्स करता है और सर्वश्रेष्ठ प्रतिनिधित्व का अनुमान लगाने की कोशिश करता है।

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)

1

क्या यह सबसे सरल तरीका है?

एक दृश्य कोड में कुछ इस तरह से:

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)

यह बढ़िया काम करता है!


1

Django 1.9+ के लिए
आप फ़ील्ड अक्षम करने के लिए फ़ील्ड्स अक्षम तर्क का उपयोग कर सकते हैं। उदा। फॉर्म-थ्रू फ़ाइल से निम्नलिखित कोड स्निपेट में, मैंने कर्मचारी_कोड फ़ील्ड को अक्षम कर दिया है

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

संदर्भ https://docs.djangoproject.com/en/2.0/ref/forms/fields/#disabled


1

यदि आप Django ver < 1.9( विशेषता 1.9जोड़ चुके Field.disabledहैं) के साथ काम कर रहे हैं, तो आप निम्न डेकोरेटर को अपनी फॉर्म __init__विधि में जोड़ने का प्रयास कर सकते हैं :

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):
        ...

मुख्य विचार यह है कि यदि फ़ील्ड है readonlyतो आपको छोड़कर किसी अन्य मूल्य की आवश्यकता नहीं है initial

पुनश्च: सेट करने के लिए मत भूलना yuor_form_field.widget.attrs['readonly'] = True


0

यदि आप Django व्यवस्थापक का उपयोग कर रहे हैं, तो यहां सबसे सरल समाधान है।

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',)

0

मुझे लगता है कि आपका सबसे अच्छा विकल्प सिर्फ आपके टेम्प्लेट में मौजूद विशेषता को अपने टेम्पलेट में शामिल करना होगा <span>या <p>इसके बजाय इसे फॉर्म में शामिल करना होगा यदि यह आसानी से हो।

प्रपत्र डेटा एकत्र करने के लिए हैं, इसे प्रदर्शित करने के लिए नहीं। कहा जा रहा है, एक readonlyविजेट में प्रदर्शित करने और POST डेटा को साफ़ करने के विकल्प ठीक समाधान हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.