Django - नेस्टेड फॉर्मेट के साथ फॉर्म सेव न करें


14

मैं Django-Crispy-Forms लेआउट सुविधा का उपयोग करते हुए मुख्य फ़ॉर्म के साथ नेस्टेड फ़ॉर्मेट को सहेजने के लिए एक दृष्टिकोण को अनुकूलित करने का प्रयास कर रहा हूं लेकिन मैं इसे सहेजता हूं। मैं इस कोड उदाहरण परियोजना का पालन ​​कर रहा हूं, लेकिन डेटा सहेजने के लिए फॉर्मेट मान्य नहीं कर सका। अगर कोई मेरी गलती को इंगित कर सकता है तो मैं वास्तव में शुक्रगुजार रहूंगा। मुझे EmployeeForm के लिए एक ही दृश्य में तीन इंलाइन जोड़ने की आवश्यकता है। मैंने Django-Extra-Views की कोशिश की, लेकिन वह काम नहीं कर सका। सराहना करेंगे यदि आप एक ही दृश्य के लिए एक से अधिक इनलाइनों को जोड़ने की सलाह देते हैं जैसे कि लगभग 5. मैं इसे प्राप्त करने के लिए एक पेज बनाना चाहता हूं Employeeऔर इसकी तरह इनलाइन भी चाहता हूं Education, Experience, Others। नीचे कोड है:

मॉडल:

class Employee(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='employees',
                                null=True, blank=True)
    about = models.TextField()
    street = models.CharField(max_length=200)
    city = models.CharField(max_length=200)
    country = models.CharField(max_length=200)
    cell_phone = models.PositiveIntegerField()
    landline = models.PositiveIntegerField()

    def __str__(self):
        return '{} {}'.format(self.id, self.user)

    def get_absolute_url(self):
        return reverse('bars:create', kwargs={'pk':self.pk})

class Education(models.Model):
    employee = models.ForeignKey('Employee', on_delete=models.CASCADE, related_name='education')
    course_title = models.CharField(max_length=100, null=True, blank=True)
    institute_name = models.CharField(max_length=200, null=True, blank=True)
    start_year = models.DateTimeField(null=True, blank=True)
    end_year = models.DateTimeField(null=True, blank=True)

    def __str__(self):
        return '{} {}'.format(self.employee, self.course_title)

राय:

class EmployeeCreateView(CreateView):
    model = Employee
    template_name = 'bars/crt.html'
    form_class = EmployeeForm
    success_url = None

    def get_context_data(self, **kwargs):
        data = super(EmployeeCreateView, self).get_context_data(**kwargs)
        if self.request.POST:
            data['education'] = EducationFormset(self.request.POST)
        else:
            data['education'] = EducationFormset()
        print('This is context data {}'.format(data))
        return data


    def form_valid(self, form):
        context = self.get_context_data()
        education = context['education']
        print('This is Education {}'.format(education))
        with transaction.atomic():
            form.instance.employee.user = self.request.user
            self.object = form.save()
            if education.is_valid():
                education.save(commit=False)
                education.instance = self.object
                education.save()

        return super(EmployeeCreateView, self).form_valid(form)

    def get_success_url(self):
        return reverse_lazy('bars:detail', kwargs={'pk':self.object.pk})

फॉर्म:

class EducationForm(forms.ModelForm):
    class Meta:
        model = Education
        exclude = ()
EducationFormset =inlineformset_factory(
    Employee, Education, form=EducationForm,
    fields=['course_title', 'institute_name'], extra=1,can_delete=True
    )

class EmployeeForm(forms.ModelForm):

    class Meta:
        model = Employee
        exclude = ('user', 'role')

    def __init__(self, *args, **kwargs):
        super(EmployeeForm, self).__init__(*args, **kwargs)
        self.helper = FormHelper()
        self.helper.form_tag = True
        self.helper.form_class = 'form-horizontal'
        self.helper.label_class = 'col-md-3 create-label'
        self.helper.field_class = 'col-md-9'
        self.helper.layout = Layout(
            Div(
                Field('about'),
                Field('street'),
                Field('city'),
                Field('cell_phone'),
                Field('landline'),
                Fieldset('Add Education',
                    Formset('education')),
                HTML("<br>"),
                ButtonHolder(Submit('submit', 'save')),
                )
            )

उदाहरण के अनुसार कस्टम लेआउट ऑब्जेक्ट:

from crispy_forms.layout import LayoutObject, TEMPLATE_PACK
from django.shortcuts import render
from django.template.loader import render_to_string

class Formset(LayoutObject):
    template = "bars/formset.html"

    def __init__(self, formset_name_in_context, template=None):
        self.formset_name_in_context = formset_name_in_context
        self.fields = []
        if template:
            self.template = template

    def render(self, form, form_style, context, template_pack=TEMPLATE_PACK):
        formset = context[self.formset_name_in_context]
        return render_to_string(self.template, {'formset': formset})

Formset.html:

{% load static %}
{% load crispy_forms_tags %}
{% load staticfiles %}

<table>
{{ formset.management_form|crispy }}

    {% for form in formset.forms %}
            <tr class="{% cycle 'row1' 'row2' %} formset_row-{{ formset.prefix }}">
                {% for field in form.visible_fields %}
                <td>
                    {# Include the hidden fields in the form #}
                    {% if forloop.first %}
                        {% for hidden in form.hidden_fields %}
                            {{ hidden }}
                        {% endfor %}
                    {% endif %}
                    {{ field.errors.as_ul }}
                    {{ field|as_crispy_field }}
                </td>
                {% endfor %}
            </tr>
    {% endfor %}

</table>
<br>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js">
</script>
<script src="{% static 'js/jquery.formset.js' %}">
</script>
<script type="text/javascript">
    $('.formset_row-{{ formset.prefix }}').formset({
        addText: 'add another',
        deleteText: 'remove',
        prefix: '{{ formset.prefix }}',
    });
</script>

टर्मिनल और या अन्य में कोई त्रुटि नहीं है। मदद काफी सराहना की है।


वैकल्पिक समाधान के लिए फॉर्मेट को भी संभालना है: मैं इसे संबंधित फॉर्मेट के लिए schinckel.net/2019/05/23/form-and-formset
मैथ्यू Schinckel

जवाबों:


0

आप वर्तमान में अपने फ़ॉर्मेट को ठीक से संसाधित नहीं कर रहे हैं CreateViewform_validउस दृश्य में केवल माता-पिता के रूप को संभालना होगा, न कि स्वरूपों को। आपको जो करना चाहिए वह postविधि को ओवरराइड करता है, और वहां आपको फॉर्म और किसी भी फॉर्मेट दोनों को मान्य करने की आवश्यकता होती है:

def post(self, request, *args, **kwargs):
    form = self.get_form()
    # Add as many formsets here as you want
    education_formset = EducationFormset(request.POST)
    # Now validate both the form and any formsets
    if form.is_valid() and education_formset.is_valid():
        # Note - we are passing the education_formset to form_valid. If you had more formsets
        # you would pass these as well.
        return self.form_valid(form, education_formset)
    else:
        return self.form_invalid(form)

फिर आप form_validऐसा संशोधित करते हैं:

def form_valid(self, form, education_formset):
    with transaction.atomic():
        form.instance.employee.user = self.request.user
        self.object = form.save()
        # Now we process the education formset
        educations = education_formset.save(commit=False)
        for education in educations:
            education.instance = self.object
            education.save()
        # If you had more formsets, you would accept additional arguments and
        # process them as with the one above.
    # Don't call the super() method here - you will end up saving the form twice. Instead handle the redirect yourself.
    return HttpResponseRedirect(self.get_success_url())

वे जिस तरह से आप वर्तमान में उपयोग कर रहे हैं get_context_data()वह सही नहीं है - उस पद्धति को पूरी तरह से हटा दें। यह केवल एक टेम्पलेट प्रदान करने के लिए संदर्भ डेटा लाने के लिए इस्तेमाल किया जाना चाहिए। आपको इसे अपने form_valid()तरीके से नहीं बुलाना चाहिए । इसके बजाय आपको post()उपर्युक्त उल्लिखित विधि से फॉर्मेट को इस विधि में पास करने की आवश्यकता है ।

मैंने ऊपर दिए गए नमूना कोड में कुछ अतिरिक्त टिप्पणियां छोड़ी हैं, जो आपको यह जानने में मदद करेगा।


कृपया उत्तर देने से पहले स्थानीय रूप से एक उदाहरण दें। मैंने आपके टुकड़े की कोशिश की है, लेकिन काम नहीं कर रहा है।
शाज़िया नुसरत

1
@ShaziaNusrat क्षमा करें, मेरे पास यह प्रयास करने और काम करने का समय नहीं है कि आपके लिए क्या काम कर रहा है विशेष रूप से यदि आप यह नहीं कहते कि आपने क्या प्रयास किया है और क्या काम नहीं किया ("यह काम नहीं कर रहा है" कोई नहीं है जो काम नहीं किया उसका पर्याप्त विवरण)। मेरा मानना ​​है कि आपके वर्तमान कार्यान्वयन के साथ आपको जो बदलने की आवश्यकता है, उसे पहचानने में मेरी मदद करने के लिए मेरे उत्तर में पर्याप्त है। यदि नहीं, तो चलो आशा करते हैं कि कोई और आपको अधिक व्यापक उत्तर देने में सक्षम होगा।
सूर्योदयकालीन तिथि

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

0

हो सकता है कि आप पैकेज django-extra-viewsदेखना चाहते हों CreateWithInlinesView, दृश्य प्रदान करते हों , डायन आपको नेस्टेड इनलाइफ के साथ फॉर्म बनाने की अनुमति देती है जैसे कि Django- एडमिन इनलाइन।

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

views.py

class EducationInline(InlineFormSetFactory):
    model = Education
    fields = ['course_title', 'institute_name']


class EmployeeCreateView(CreateWithInlinesView):
    model = Employee
    inlines = [EducationInline,]
    fields = ['about', 'street', 'city', 'cell_phone', 'landline']
    template_name = 'bars/crt.html'

crt.html

<form method="post">
  ...
  {{ form }}
  <table>
  {% for formset in inlines %}
    {{ formset.management_form }}
      {% for inline_form in formset %}
        <tr class="{% cycle 'row1' 'row2' %} formset_row-{{ formset.prefix }}">
          {{ inline_form }}
        </tr>
      {% endfor %}
  {% endfor %}
  </table>
  ...
  <input type="submit" value="Submit" />
</form>

<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js">
</script>
<script src="{% static 'js/jquery.formset.js' %}">
</script>
<script type="text/javascript">
    {% for formset in inlines %}
      $('.formset_row-{{ formset.prefix }}').formset({
          addText: 'add another',
          deleteText: 'remove',
          prefix: '{{ formset.prefix }}',
      });
    {% endfor %}
</script>

दृश्य EmployeeCreateViewआपके लिए रूपों को संसाधित करेगा जैसा कि Django-admin में है। इस बिंदु से आप उस शैली को लागू कर सकते हैं जिसे आप प्रपत्रों के लिए चाहते हैं।

मैं आपको अधिक जानकारी के लिए प्रलेखन पर जाने की सलाह देता हूं

संपादित करें: मैंने जोड़ा management_form और जेएस बटन जोड़ने / हटाने के लिए।


मैंने कोशिश की कि पहले से ही है, लेकिन मुझे कई inlines के लिए बटन जोड़ने / हटाने नहीं देंगे। यह केवल जेएस बटन के साथ एक इनलाइन का समर्थन करता है। मैंने पहले ही कोशिश की है।
शाज़िया नुसरत

1
यह इसका समर्थन करता है, आपको management_formप्रत्येक के लिए जोड़ना होगाformset
जॉन

0

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

आपका केस फॉर्मेट्स की वजह से थोड़ा पेचीदा है और एक ही CreateView पर कई रूपों का उपयोग कर रहा है। इंटरनेट पर कई (या बहुत अच्छे नहीं) उदाहरण नहीं हैं। जब तक आप django कोड में खुदाई नहीं करते हैं कि इनलाइन फॉर्मेट्स कैसे काम कर रहे हैं, तो आपको परेशानी होगी।

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

सामान्य रूपों में यदि आप is_valid के बाद इसे chnage करते हैं, तो आपके पास अप्रत्याशित परिणाम होंगे। इनसेट नोटिफ़िकेशन करने के बाद भी सीधे इंस्टीट्यूशन को बदलने वाले फॉर्मेट्स के लिए, फॉर्मसेट में मौजूद फॉर्मेज़ को पहले से ही कुछ इंस्टेंस के साथ इनिशियलाइज़ कर दिया जाता है, और इसे बदलने के बाद मदद नहीं मिलेगी। अच्छी खबर यह है कि आप फॉर्मसेट के प्रारंभ होने के बाद उदाहरण की विशेषताओं को बदल सकते हैं, क्योंकि सभी प्रकार के इंस्टेंस गुण फॉर्मेट के प्रारंभ होने के बाद उसी ऑब्जेक्ट को इंगित करेंगे।

आपके पास दो विकल्प हैं:

इंस्टीट्यूशन सेट करने के बजाय यदि फॉर्मेट है, तो केवल इंस्टा .pk सेट करें। (यह सिर्फ एक अनुमान है कि मैंने कभी ऐसा नहीं किया है लेकिन मुझे लगता है कि इसे काम करना चाहिए। समस्या यह है कि यह हैक के रूप में दिखेगा)। एक फॉर्म बनाएँ जो एक ही बार में सभी फॉर्म / फॉर्मेट्स को इनिशियलाइज़ करेगा। जब यह is_valid () विधि कहलाता है तो सभी fomrs को मान्य किया जाना चाहिए। जब यह सहेजा जाता है () विधि कहा जाता है सभी रूपों को बचाया जाना चाहिए। फिर आपको अपने CreateView की form_class विशेषता को उस फॉर्म क्लास में सेट करना होगा। एकमात्र मुश्किल हिस्सा यह है कि आपके मुख्य फॉर्म को आरंभीकृत करने के बाद आपको अपने पहले फॉर्म के उदाहरण के साथ दूसरों (फॉर्मेट्स) को इनिशियलाइज़ करना होगा। साथ ही आपको टेम्प्लेट में उन तक पहुंचने के लिए अपने फ़ॉर्म की विशेषताओं के रूप में फ़ॉर्म / फ़ॉर्मेट सेट करना होगा। जब मैं किसी ऑब्जेक्ट को संबंधित सभी ऑब्जेक्ट्स के साथ बनाना चाहता हूं, तो मैं दूसरे दृष्टिकोण का उपयोग कर रहा हूं।

वैध होने पर is_valid () () के साथ वैधता के लिए जाँच किए गए कुछ डेटा (इस मामले में POST डेटा) के साथ आरम्भ में इसे सहेजा जा सकता है। आप प्रपत्र इंटरफ़ेस को संरक्षित करते हैं और यदि आपने अपना फ़ॉर्म सही ढंग से बनाया है तो आप इसका उपयोग न केवल बनाने के लिए कर सकते हैं बल्कि वस्तुओं को उनकी संबंधित वस्तुओं के साथ अद्यतन करने के लिए भी कर सकते हैं और विचार बहुत सरल होंगे।

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