Django 2.1 - Form and field validation

प्रपत्र और फ़ील्ड सत्यापन




django

प्रपत्र और फ़ील्ड सत्यापन

प्रपत्र सत्यापन तब होता है जब डेटा साफ किया जाता है। यदि आप इस प्रक्रिया को अनुकूलित करना चाहते हैं, तो परिवर्तन करने के लिए विभिन्न स्थान हैं, प्रत्येक एक अलग उद्देश्य की सेवा कर रहा है। फॉर्म प्रसंस्करण के दौरान तीन प्रकार की सफाई विधियां चलती हैं। जब आप किसी प्रपत्र पर is_valid() विधि को कॉल करते हैं, तो इन्हें सामान्य रूप से निष्पादित किया जाता है। ऐसी अन्य चीजें भी हैं जो सफाई और सत्यापन को ट्रिगर कर सकती हैं ( errors विशेषता तक पहुंच या full_clean() सीधे कॉल कर full_clean() हैं), लेकिन आम तौर पर उन्हें ज़रूरत नहीं होगी।

सामान्य तौर पर, कोई भी सफाई विधि ValidationError को बढ़ा सकती है यदि डेटा में कोई समस्या है तो वह प्रसंस्करण के लिए प्रासंगिक जानकारी को पार कर रहा है। ValidationError को बढ़ाने के सर्वोत्तम अभ्यास के लिए नीचे देखें । यदि कोई ValidationError नहीं उठाया गया है, तो विधि को पाइथन ऑब्जेक्ट के रूप में साफ (सामान्यीकृत) डेटा को वापस करना चाहिए।

अधिकांश सत्यापन validators - सरल सहायकों का उपयोग करके किया जा सकता है जिन्हें आसानी से पुन: उपयोग किया जा सकता है। Validators सरल कार्य (या callables) हैं जो एकल तर्क लेते हैं और अमान्य इनपुट पर ValidationError बढ़ाते हैं। क्षेत्र के to_python और validate विधियों को to_python जाने के बाद सत्यापनकर्ता चलाए जाते हैं।

किसी प्रपत्र की मान्यता को कई चरणों में विभाजित किया जाता है, जिसे अनुकूलित या ओवरराइड किया जा सकता है:

  • Field पर to_python() विधि प्रत्येक सत्यापन में पहला चरण है। यह एक सही डेटाटाइप के लिए मूल्य को बढ़ाता है और यदि संभव नहीं है तो ValidationError बढ़ाता है। यह विधि विजेट से कच्चे मान को स्वीकार करती है और परिवर्तित मान लौटाती है। उदाहरण के लिए, एक FloatField डेटा को पायथन float में बदल देगा या एक FloatField देगा।
  • Field पर validate() विधि फ़ील्ड-विशिष्ट सत्यापन को संभालती है जो सत्यापनकर्ता के लिए उपयुक्त नहीं है। ऐसा मान लेता है जो किसी सही डेटाटाइप के लिए ज़ब्त किया गया है और किसी भी त्रुटि पर ValidationError को बढ़ाता है। यह विधि कुछ भी नहीं लौटाती है और मान को परिवर्तित नहीं करना चाहिए। आपको सत्यापन तर्क को संभालने के लिए इसे ओवरराइड करना चाहिए जिसे आप सत्यापनकर्ता में नहीं डाल सकते हैं या नहीं रखना चाहते हैं।
  • एक Field पर run_validators() विधि क्षेत्र के सभी सत्यापनकर्ताओं को चलाती है और सभी त्रुटियों को एक एकल run_validators() । आपको इस विधि को ओवरराइड करने की आवश्यकता नहीं है।
  • Field उपवर्ग पर clean() विधि सही क्रम में to_python() , validate() और validate() और run_validators() चलाने के लिए जिम्मेदार है। यदि, किसी भी समय, कोई भी विधि ValidationError बढ़ाती है, तो सत्यापन रुक जाता है और उस त्रुटि को उठाया जाता है। यह विधि क्लीन डेटा को लौटाती है, जिसे बाद में फॉर्म के cleaned_data शब्दकोश में डाला जाता है।
  • clean_<fieldname>() विधि को फॉर्म उपवर्ग पर कहा जाता है - जहां <fieldname> को फॉर्म फील्ड विशेषता के नाम से बदल दिया जाता है। यह विधि कोई भी सफाई करती है जो उस विशेष विशेषता के लिए विशिष्ट है, जो उस क्षेत्र के प्रकार से असंबंधित है। यह विधि किसी भी पैरामीटर से पारित नहीं होती है। आपको self.cleaned_data में फ़ील्ड के मान को देखना होगा और याद रखना होगा कि यह इस बिंदु पर एक पायथन ऑब्जेक्ट होगा, न कि फॉर्म में सबमिट किया गया मूल स्ट्रिंग (यह cleaned_data में cleaned_data क्योंकि सामान्य रूप से clean() , ऊपर, पहले ही एक बार डेटा साफ कर चुका है)।

    उदाहरण के लिए, यदि आप यह प्रमाणित करना चाहते हैं कि CharField नामक CharField की सामग्री अद्वितीय थी, तो ऐसा करने के लिए clean_serialnumber() सही जगह होगा। आपको एक विशिष्ट फ़ील्ड की आवश्यकता नहीं है (यह सिर्फ एक CharField ), लेकिन आप सत्यापन का एक CharField -विशिष्ट टुकड़ा चाहते हैं और संभवतः, डेटा को साफ / सामान्य कर सकते हैं।

    इस पद्धति का रिटर्न मान, मौजूदा मान को cleaned_data में बदल देता है, इसलिए इसे cleaned_data से फ़ील्ड का मान होना चाहिए (भले ही यह विधि इसे बदल न गई हो) या एक नया साफ़ किया गया मान।

  • प्रपत्र उपवर्ग की clean() विधि सत्यापन कर सकती है जिसके लिए कई प्रपत्र फ़ील्ड तक पहुँच की आवश्यकता होती है। यह वह जगह है जहाँ आप चेक में डाल सकते हैं जैसे "यदि फ़ील्ड A की आपूर्ति की जाती है, तो फ़ील्ड B में एक वैध ईमेल पता होना चाहिए"। यह विधि पूरी तरह से एक अलग शब्दकोश लौटा सकती है यदि यह चाहे, तो इसे cleaned_data रूप में उपयोग किया जाएगा।

    चूँकि फ़ील्ड सत्यापन विधियाँ clean() कहलाती हैं, इसलिए आपके पास फॉर्म की errors विशेषता भी होती है, जिसमें व्यक्तिगत क्षेत्रों की सफाई द्वारा उठाए गए सभी त्रुटियां होती हैं।

    ध्यान दें कि आपके Form.clean() ओवरराइड द्वारा उठाए गए किसी भी त्रुटि विशेष रूप से किसी भी क्षेत्र से जुड़ी नहीं होगी। वे एक विशेष "फ़ील्ड" (जिसे __all__ कहा जाता है) में जाते हैं, जिसे आप non_field_errors() विधि के माध्यम से एक्सेस कर सकते हैं यदि आपको आवश्यकता है। यदि आप प्रपत्र में त्रुटियों को किसी विशिष्ट फ़ील्ड में संलग्न करना चाहते हैं, तो आपको add_error() को कॉल add_error()

    यह भी ध्यान दें कि किसी ModelForm उपवर्ग की clean() पद्धति को ओवरराइड करते समय विशेष विचार हैं। (अधिक जानकारी के लिए ModelForm प्रलेखन देखें)

इन विधियों को ऊपर दिए गए क्रम में चलाया जाता है, एक समय में एक क्षेत्र। यही है, प्रपत्र में प्रत्येक फ़ील्ड के लिए (क्रम में उन्हें परिभाषा के रूप में घोषित किया गया है), Field.clean() विधि (या इसका ओवरराइड) चलाया जाता है, फिर clean_<fieldname>() । अंत में, एक बार उन दो तरीकों को हर क्षेत्र के लिए चलाए जाने के बाद, Form.clean() विधि, या इसके ओवरराइड को निष्पादित किया जाता है, चाहे पिछले तरीकों ने त्रुटियां Form.clean() हों या नहीं।

इनमें से प्रत्येक विधि के उदाहरण नीचे दिए गए हैं।

जैसा कि उल्लेख किया गया है, इनमें से कोई भी विधि ValidationError कर सकती है। किसी भी क्षेत्र के लिए, यदि Field.clean() विधि एक ValidationError उठाती है, तो किसी भी क्षेत्र-विशिष्ट सफाई विधि को नहीं कहा जाता है। हालांकि, सभी शेष क्षेत्रों के लिए सफाई के तरीकों को अभी भी निष्पादित किया जाता है।

वल्र्डाइजेशन बढ़ाना

त्रुटि संदेशों को लचीला और ओवरराइड करने में आसान बनाने के लिए, निम्नलिखित दिशानिर्देशों पर विचार करें:

  • कंस्ट्रक्टर को वर्णनात्मक त्रुटि code प्रदान करें:

    # Good
    ValidationError(_('Invalid value'), code='invalid')
    
    # Bad
    ValidationError(_('Invalid value'))
    
  • संदेश में वैरिएबल का उपयोग न करें; प्लेसहोल्डर का उपयोग करें और कंस्ट्रक्टर के params तर्क:

    # Good
    ValidationError(
        _('Invalid value: %(value)s'),
        params={'value': '42'},
    )
    
    # Bad
    ValidationError(_('Invalid value: %s') % value)
    
  • पोजिशनिंग फॉर्मेटिंग के बजाय मैपिंग कीज़ का उपयोग करें। यह किसी भी क्रम में चर डालने में सक्षम बनाता है या संदेश को फिर से लिखने पर उन्हें पूरी तरह से छोड़ देता है:

    # Good
    ValidationError(
        _('Invalid value: %(value)s'),
        params={'value': '42'},
    )
    
    # Bad
    ValidationError(
        _('Invalid value: %s'),
        params=('42',),
    )
    
  • अनुवाद सक्षम करने के लिए संदेश को लपेटें:

    # Good
    ValidationError(_('Invalid value'))
    
    # Bad
    ValidationError('Invalid value')
    

यह सब एक साथ डालें:

raise ValidationError(
    _('Invalid value: %(value)s'),
    code='invalid',
    params={'value': '42'},
)

यदि आप पुन: प्रयोज्य प्रपत्र, फ़ील्ड, और मॉडल फ़ील्ड लिखते हैं, तो इन दिशानिर्देशों का पालन करना विशेष रूप से आवश्यक है।

अनुशंसित नहीं है, अगर आप सत्यापन श्रृंखला के अंत में हैं (अर्थात आपका फॉर्म clean() विधि) और आप जानते हैं कि आपको अपने त्रुटि संदेश को ओवरराइड करने की आवश्यकता नहीं होगी, तो आप अभी भी कम क्रिया के लिए विकल्प चुन सकते हैं:

ValidationError(_('Invalid value: %s') % value)

Form.errors.as_data() और Form.errors.as_json() विधियों को पूरी तरह से चित्रित ValidationError s (एक code नाम और एक params शब्दकोश के साथ) से बहुत लाभ होता है।

कई त्रुटियां उठाना

यदि आप एक सफाई विधि के दौरान कई त्रुटियों का पता लगाते हैं और उन सभी को फॉर्म सबमिट करने वाले को संकेत देना चाहते हैं, तो त्रुटियों की सूची ValidationError कंस्ट्रक्टर को पास करना संभव है।

ऊपर के रूप में, यह code एस और params साथ ValidationError उदाहरणों की सूची को पारित करने की सिफारिश की गई है, लेकिन तार की एक सूची भी काम करेगी:

# Good
raise ValidationError([
    ValidationError(_('Error 1'), code='error1'),
    ValidationError(_('Error 2'), code='error2'),
])

# Bad
raise ValidationError([
    _('Error 1'),
    _('Error 2'),
])

व्यवहार में मान्यता का उपयोग करना

पिछले अनुभागों ने बताया कि कैसे रूपों के लिए सत्यापन सामान्य रूप से काम करता है। चूंकि कभी-कभी उपयोग में प्रत्येक सुविधा को देखकर चीजों को जगह देना आसान हो सकता है, यहां छोटे उदाहरणों की एक श्रृंखला है जो पिछली विशेषताओं में से प्रत्येक का उपयोग करते हैं।

सत्यापनकर्ताओं का उपयोग करना

Django का फॉर्म (और मॉडल) फ़ील्ड साधारण उपयोगिता कार्यों और मान्यताओं के रूप में ज्ञात वर्गों के उपयोग का समर्थन करता है। एक सत्यापनकर्ता केवल एक कॉल करने योग्य वस्तु या फ़ंक्शन है जो एक मूल्य लेता है और बस कुछ भी नहीं लौटाता है यदि मूल्य वैध है या नहीं तो ValidationError । इन्हें फ़ील्ड के कंस्ट्रक्टर में पास किया जा सकता है, क्षेत्र के validators तर्क के माध्यम से, या डिफ़ॉल्ट_वलिडेटर्स विशेषता के साथ Field वर्ग पर ही परिभाषित किया जा सकता है।

साधारण सत्यापनकर्ताओं का उपयोग क्षेत्र के अंदर मूल्यों को मान्य करने के लिए किया जा सकता है, आइए एक नजर डालते हैं Django के SlugField :

from django.core import validators
from django.forms import CharField

class SlugField(CharField):
    default_validators = [validators.validate_slug]

जैसा कि आप देख सकते हैं, SlugField केवल एक अनुकूलित सत्यापनकर्ता के साथ एक CharField जो पुष्टि करता है कि कुछ वर्ण नियमों के पाठ सबमिट किए गए हैं। यह क्षेत्र की परिभाषा पर भी किया जा सकता है:

slug = forms.SlugField()

के बराबर है:

slug = forms.CharField(validators=[validators.validate_slug])

एक ईमेल या एक नियमित अभिव्यक्ति के खिलाफ मान्य जैसे सामान्य मामलों को Django में उपलब्ध मौजूदा सत्यापनकर्ता कक्षाओं का उपयोग करके नियंत्रित किया जा सकता है। उदाहरण के लिए, validators.validate_slug एक RegexValidator का एक उदाहरण है, RegexValidator पहला तर्क पैटर्न है: ^[-a-zA-Z0-9_]+$ मान्यताओं को लिखने के लिए अनुभाग देखें कि क्या पहले से ही उपलब्ध है और एक सत्यापनकर्ता कैसे लिखें के उदाहरण के लिए।

फॉर्म फील्ड डिफ़ॉल्ट सफाई

चलिए सबसे पहले एक कस्टम फॉर्म फील्ड बनाते हैं जो अपने इनपुट को मान्य करता है एक स्ट्रिंग है जिसमें अल्पविराम से अलग किए गए ईमेल पते होते हैं। पूरा वर्ग इस तरह दिखता है:

from django import forms
from django.core.validators import validate_email

class MultiEmailField(forms.Field):
    def to_python(self, value):
        """Normalize data to a list of strings."""
        # Return an empty list if no input was given.
        if not value:
            return []
        return value.split(',')

    def validate(self, value):
        """Check if value consists only of valid emails."""
        # Use the parent's handling of required fields, etc.
        super().validate(value)
        for email in value:
            validate_email(email)

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

आइए यह दिखाने के लिए कि आप इस क्षेत्र का उपयोग कैसे करेंगे, एक सरल संपर्क बनाएँ:

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    message = forms.CharField()
    sender = forms.EmailField()
    recipients = MultiEmailField()
    cc_myself = forms.BooleanField(required=False)

बस किसी भी अन्य प्रपत्र फ़ील्ड की तरह MultiEmailField उपयोग करें। जब is_valid() विधि को फॉर्म पर कॉल किया जाता है, तो सफाई प्रक्रिया के हिस्से के रूप में MultiEmailField.clean() विधि को चलाया जाएगा और यह, बदले में, कस्टम to_python() और validate() विधियों को कॉल करेगा।

एक विशिष्ट फ़ील्ड विशेषता की सफाई

पिछले उदाहरण से जारी रखते हुए, मान लीजिए कि हमारे ContactForm , हम यह सुनिश्चित करना चाहते हैं कि recipients फ़ील्ड में हमेशा पता "[email protected]" । यह वह मान्यता है जो हमारे फॉर्म के लिए विशिष्ट है, इसलिए हम इसे सामान्य MultiEmailField क्लास में नहीं डालना चाहते हैं। इसके बजाय, हम एक सफाई विधि लिखते हैं जो recipients फ़ील्ड पर काम करती है, जैसे:

from django import forms

class ContactForm(forms.Form):
    # Everything as before.
    ...

    def clean_recipients(self):
        data = self.cleaned_data['recipients']
        if "[email protected]" not in data:
            raise forms.ValidationError("You have forgotten about Fred!")

        # Always return a value to use as the new cleaned data, even if
        # this method didn't change it.
        return data

खेतों की सफाई और सत्यापन जो एक दूसरे पर निर्भर करते हैं

मान लीजिए कि हम अपने संपर्क फ़ॉर्म में एक और आवश्यकता जोड़ते हैं: यदि cc_myself फ़ील्ड True , तो subject "help" शब्द होना चाहिए। हम एक समय में एक से अधिक फ़ील्ड पर सत्यापन कर रहे हैं, इसलिए फ़ॉर्म की Form.clean() विधि ऐसा करने के लिए एक अच्छा स्थान है। ध्यान दें कि हम यहां फॉर्म पर clean() विधि के बारे में बात कर रहे हैं, जबकि पहले हम एक क्षेत्र पर एक clean() विधि लिख रहे थे। यह महत्वपूर्ण है कि फील्ड और फॉर्म के अंतर को स्पष्ट रखें जब काम करना है तो चीजों को मान्य करना है। फ़ील्ड एकल डेटा बिंदु हैं, प्रपत्र फ़ील्ड का संग्रह हैं।

जब तक फॉर्म की clean() विधि कहा जाता है, तब तक सभी व्यक्तिगत फ़ील्ड क्लीन तरीके (पिछले दो खंड) self.cleaned_data होंगे, इसलिए self.cleaned_data को अब तक बचे हुए किसी भी डेटा के साथ पॉपुलेट किया जाएगा। इसलिए आपको इस तथ्य की अनुमति देने के लिए याद रखना चाहिए कि आप जिन क्षेत्रों को मान्य करना चाहते हैं, वे प्रारंभिक व्यक्तिगत फ़ील्ड चेक से बच नहीं सकते हैं।

इस चरण से किसी भी त्रुटि की रिपोर्ट करने के दो तरीके हैं। संभवतः सबसे सामान्य तरीका है फॉर्म के शीर्ष पर त्रुटि प्रदर्शित करना। ऐसी त्रुटि बनाने के लिए, आप clean() विधि से ValidationError बढ़ा सकते हैं। उदाहरण के लिए:

from django import forms

class ContactForm(forms.Form):
    # Everything as before.
    ...

    def clean(self):
        cleaned_data = super().clean()
        cc_myself = cleaned_data.get("cc_myself")
        subject = cleaned_data.get("subject")

        if cc_myself and subject:
            # Only do something if both fields are valid so far.
            if "help" not in subject:
                raise forms.ValidationError(
                    "Did not send for 'help' in the subject despite "
                    "CC'ing yourself."
                )

इस कोड में, यदि सत्यापन त्रुटि को उठाया जाता है, तो फॉर्म समस्या के वर्णन के लिए फॉर्म के शीर्ष पर (सामान्य रूप से) एक त्रुटि संदेश प्रदर्शित करेगा।

उदाहरण कोड में super().clean() के लिए कॉल सुनिश्चित करता है कि माता-पिता वर्गों में किसी भी सत्यापन तर्क को बनाए रखा गया है। यदि आपका फ़ॉर्म एक और इनहेरिट करता है, जो अपनी clean() विधि में एक cleaned_data शब्दकोश नहीं लौटाता है (ऐसा करना वैकल्पिक है), तो super() के परिणाम के लिए cleaned_data को असाइन न करें super() कॉल करें और इसके बजाय self.cleaned_data

def clean(self):
    super().clean()
    cc_myself = self.cleaned_data.get("cc_myself")
    ...

सत्यापन त्रुटियों की रिपोर्ट करने के लिए दूसरा दृष्टिकोण फ़ील्ड में से एक को त्रुटि संदेश प्रदान करना शामिल हो सकता है। इस मामले में, आइए फॉर्म विषय में "विषय" और "cc_myself" दोनों पंक्तियों में एक त्रुटि संदेश दें। अभ्यास में ऐसा करते समय सावधान रहें, क्योंकि इससे फॉर्म आउटपुट भ्रमित हो सकता है। हम यह दिखा रहे हैं कि यहां क्या संभव है और इसे आपके और आपके डिजाइनरों के लिए छोड़ना है जो आपकी विशेष स्थिति में प्रभावी ढंग से काम करता है। हमारा नया कोड (पिछले नमूने की जगह) इस तरह दिखता है:

from django import forms

class ContactForm(forms.Form):
    # Everything as before.
    ...

    def clean(self):
        cleaned_data = super().clean()
        cc_myself = cleaned_data.get("cc_myself")
        subject = cleaned_data.get("subject")

        if cc_myself and subject and "help" not in subject:
            msg = "Must put 'help' in subject when cc'ing yourself."
            self.add_error('cc_myself', msg)
            self.add_error('subject', msg)

add_error() का दूसरा तर्क एक साधारण स्ट्रिंग, या अधिमानतः ValidationError का एक उदाहरण हो सकता है। अधिक जानकारी के लिए मान्यकरण उठाएँ देखें। ध्यान दें कि add_error() स्वचालित रूप से फ़ील्ड को cleaned_data से निकाल देता है।