2012-11-23 16 views
12

में निर्भर इनलाइनों का सत्यापन मैं Django 1.4 का उपयोग कर रहा हूं और मैं सत्यापन नियम सेट करना चाहता हूं जो अलग-अलग इनलाइनों के मानों की तुलना करें।django व्यवस्थापक

मैं तीन सरल कक्षाओं

models.py में है:

class Shopping(models.Model): 
    shop_name = models.CharField(max_length=200) 

class Item(models.Model): 
    item_name = models.CharField(max_length=200) 
    cost = models.IntegerField() 
    item_shop = models.ForeignKey(Shopping) 

class Buyer(models.Model): 
    buyer_name = models.CharField(max_length=200) 
    amount = models.IntegerField() 
    buyer_shop = models.ForeignKey(Shopping) 

admin.py में:

class ItemInline(admin.TabularInline): 
    model = Item 

class BuyerInline(admin.TabularInline): 
    model = Buyer 

class ShoppingAdmin(admin.ModelAdmin): 
    inlines = (ItemInline, BuyerInline) 

इसलिए उदाहरण के लिए यह संभव है पर Rhum की एक बोतल खरीदने के लिए 8 $ और वोदका में से एक $ 8 पर। माइक 15 डॉलर का भुगतान करता है और टॉम 3 डॉलर का भुगतान करता है।

लक्ष्य उपयोगकर्ता को उन रकम के साथ एक उदाहरण सहेजने से रोकने के लिए है जो मेल नहीं खाते हैं: भुगतान किया गया है आइटम की लागत (यानी 10 + 8 = 15 + 3) के योग के समान होना चाहिए।

मैंने कोशिश की:

  • Shopping.clean विधि में ValidationError उठा। लेकिन इनलाइनों को अभी तक साफ नहीं किया गया है, इसलिए रकम सही नहीं हैं
  • शॉपिंगएडमिन.save_related विधि में प्रमाणीकरण त्रुटि को बढ़ाएं। लेकिन यहां सत्यापन प्रमाणीकरण एक अच्छा त्रुटि संदेश के साथ परिवर्तन पृष्ठ पर पुनर्निर्देशित करने के बजाय एक बहुत ही उपयोगकर्ता असभ्य त्रुटि पृष्ठ देता है।

क्या इस समस्या का कोई समाधान है? क्लाइंट-साइड (जावास्क्रिप्ट/AJAX) प्रमाणीकरण सबसे सरल है?

+0

हैलो, क्या आप इसके लिए कुछ लेकर आए हैं? मुझे एक ही समस्या का सामना करना पड़ता है। एकमात्र समाधान जो मैं सोच सकता हूं वह इनलाइन मॉडल की क्लीन विधि है, लेकिन यह एक बड़ा डीबी ओवरहेड उत्पन्न करेगा। – ppetrid

+0

मुझे लगता है कि एक समाधान django व्यवस्थापक के व्यवहार को संपादित करना है। Django/contrib/admin/options.py, add_view विधि पंक्ति 924 – Rems

उत्तर

23

आप जो चाहते हैं उसे प्राप्त करने के लिए आप अपने इनलाइन फॉर्मेट को ओवरराइड कर सकते हैं। फ़ॉर्मेट की क्लीन विधि में आपके पास 'इंस्टेंस' सदस्य के माध्यम से आपके शॉपिंग इंस्टेंस तक पहुंच है। इसलिए आप गणना मॉडल को अस्थायी रूप से संग्रहीत करने और अपने स्वरूपों को संवाद करने के लिए उपयोग कर सकते हैं। models.py में:

class Shopping(models.Model): 
    shop_name = models.CharField(max_length=200) 

    def __init__(self, *args, **kwargs) 
     super(Shopping, self).__init__(*args, **kwargs) 
     self.__total__ = None 
admin.py में

:

from django.forms.models import BaseInlineFormSet 
class ItemInlineFormSet(BaseInlineFormSet): 
    def clean(self): 
     super(ItemInlineFormSet, self).clean() 
     total = 0 
     for form in self.forms: 
     if not form.is_valid(): 
      return #other errors exist, so don't bother 
     if form.cleaned_data and not form.cleaned_data.get('DELETE'): 
      total += form.cleaned_data['cost'] 
     self.instance.__total__ = total 


class BuyerInlineFormSet(BaseInlineFormSet): 
    def clean(self): 
     super(BuyerInlineFormSet, self).clean() 
     total = 0 
     for form in self.forms: 
     if not form.is_valid(): 
      return #other errors exist, so don't bother 
     if form.cleaned_data and not form.cleaned_data.get('DELETE'): 
      total += form.cleaned_data['cost'] 

     #compare only if Item inline forms were clean as well 
     if self.instance.__total__ is not None and self.instance.__total__ != total: 
     raise ValidationError('Oops!') 

class ItemInline(admin.TabularInline): 
    model = Item 
    formset = ItemInlineFormSet 

class BuyerInline(admin.TabularInline): 
    model = Buyer 
    formset = BuyerInlineFormSet 

यह केवल स्वच्छ जिस तरह से आप यह (मेरी जानकारी के अनुसार) कर सकते हैं और सब कुछ रखा गया है जहां यह होना चाहिए ।

संपादित करें: * अगर form.cleaned_data * चेक जोड़ा गया है तो फॉर्म में खाली इनलाइन भी शामिल हैं। कृपया मुझे बताएं कि यह आपके लिए कैसे काम करता है!

EDIT2: हटाए जाने वाले फॉर्मों के लिए चेक को जोड़ा गया, जैसा टिप्पणियों में सही ढंग से बताया गया है। इन रूपों को गणना में भाग नहीं लेना चाहिए।

+0

बहुत बढ़िया! यह शर्म की बात है कि मैं आपका जवाब वोट नहीं दे सकता; मेरे पास पर्याप्त प्रतिष्ठा नहीं है। संपादित करें: एनवीएम कुछ प्रतिष्ठा अंक जादूगर रूप से दिखाई दिए – Rems

+1

इसे हटाए गए पंक्तियों के साथ उपेक्षा करना चाहिए: '' form.cleaned_data.get ('DELETE'): ' –

+1

जारी रखें यह एक सुंदर रणनीति है, धन्यवाद। मुझे कोई समस्या है, हालांकि, क्योंकि जब कोई इनलाइन नहीं है डी, त्रुटि संदेश नहीं होते हैं। मेरे कोड में, मेरे पास केवल एक इनलाइन फॉर्मेट परिभाषित है क्योंकि मैं इसे मुख्य मॉडल में किसी क्षेत्र में तुलना कर रहा हूं (इसलिए ऊपर दिए गए उदाहरण में, 'क्रेताइनलाइनफॉर्मसेट' में, मैं तुलना का उपयोग करूंगा 'अगर self.instance.amount! = कुल : उठाओ ... '। जब मैं 'शॉपिंग' उदाहरण को राशि> 0 के साथ सहेजता हूं और बिना किसी 'खरीदार' को जोड़ता हूं, तो यह मुझे बताता है कि यह फॉर्म मान्य है, भले ही यह नहीं है (क्योंकि कोई खरीदार राशि नहीं है 0) – jenniwren

-2

ठीक है मेरे पास एक समाधान है। इसमें django व्यवस्थापक का कोड संपादित करना शामिल है।

Django/योगदान/व्यवस्थापक/options.py में, add_view (लाइन 924) और change_view में (लाइन 1012) विधियों, इस हिस्से स्थान:

 [...] 
     if all_valid(formsets) and form_validated: 
      self.save_model(request, new_object, form, True) 
     [...] 

और साथ बदलें

 if not hasattr(self, 'clean_formsets') or self.clean_formsets(form, formsets): 
      if all_valid(formsets) and form_validated: 
       self.save_model(request, new_object, form, True) 
अपने ModelAdmin में अब

, आप यह और अधिक एक उचित समाधान टी की तुलना में एक हैक है इस

class ShoppingAdmin(admin.ModelAdmin): 
    inlines = (ItemInline, BuyerInline) 
    def clean_formsets(self, form, formsets): 
     items_total = 0 
     buyers_total = 0 
     for formset in formsets: 
      if formset.is_valid(): 
       if issubclass(formset.model, Item): 
        items_total += formset.cleaned_data[0]['cost'] 
       if issubclass(formset.model, Buyer): 
        buyers_total += formset.cleaned_data[0]['amount'] 

     if items_total != buyers_total: 
      # This is the most ugly part :(
      if not form._errors.has_key(forms.forms.NON_FIELD_ERRORS): 
       form._errors[forms.forms.NON_FIELD_ERRORS] = [] 
      form._errors[forms.forms.NON_FIELD_ERRORS].append('The totals don\'t match!') 
      return False 
     return True 

की तरह कुछ कर सकते हैं hough। कोई सुधार सुझाव? क्या किसी को लगता है कि यह django पर एक सुविधा अनुरोध होना चाहिए?

+0

देखें यह वास्तव में एक हैक है क्योंकि हमें ValidationError को बढ़ाने के बजाय सूची में त्रुटियों को मैन्युअल रूप से जोड़ना होगा। लेकिन यह अभी भी काम करता है! मुझे लगता है कि यह मूल रूप से फॉर्मेट सत्यापन का मामला है। उस अर्थ में शायद कोई कस्टम फॉर्मसेट क्लास बना सकता है, एक उचित साफ विधि लागू कर सकता है और इनलाइन में डिफ़ॉल्ट स्वरूप के बजाय उस वर्ग का उपयोग कर सकता है। बस एक विचार .. – ppetrid

+0

क्या आप मैन्युअल रूप से एक फॉर्मसेट बनाने का सुझाव देते हैं? इसलिए मूल रूप से कोई और इनलाइन नहीं है, आपको हाथ से संबंधित बचत को संभालना होगा, कोई "कोई अन्य बटन जोड़ें" आदि नहीं है ... आप इनलाइनों की सभी शक्तियों को खो देते हैं :( – Rems

+0

क्षमा करें, शायद मैं स्पष्ट नहीं था , मैं इनलाइन फॉर्मेट्स को ओवरराइड करने का सुझाव देता हूं। मैंने एक अलग जवाब पोस्ट करने के बाद समाप्त किया क्योंकि मैं अपने स्वयं के एक प्रोजेक्ट के लिए समाधान के साथ आया था। – ppetrid

संबंधित मुद्दे