customized prefix not retrieved in my POST request

Hello,

I’am using a formset to allow updating objects (business). A business may be related to one market (linked to a customer). Within each object, I can open a bootstrap modal to allow the creation of a new market. Therefore, my view construct a dictionnary of “market” forms (1 form with a customize prefix which includes the business id: pobusiness.id). This way, each modal has its own id. Same process for allowing the creation of “framework_agreement” (customized id: fabusiness.id)

Unfortunately, my POST request does not include any key containing the customized prefix. I get the values in a list associated to key named with the fiedname (for instance: “number”, when I was expecting “fa1” for business.id of 1.

Any idea of what is going on?

Here is the code I have so far (I removed some pieces which are not relevant):

views.py:

class CompanyUpdateView(UpdateView):
    template_name = 'salesforecast/company_update_form.html'
    model = Company
    fields = '__all__'
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        company = get_object_or_404(Company, pk=self.kwargs['pk']) 
        context['company_form'] = CompanyForm(instance = company)
        BusinessFormSet = inlineformset_factory(Company,Business,fk_name='customer', can_delete=False,extra=1, form = BusinessForm)
        business_formset = BusinessFormSet(instance=company)
        context['business_formset'] = business_formset
        # On construit un formulaire de saisie d'accord-cadre par affaire    
        businesses = Business.objects.filter(customer=company)
        framework_agreement_forms_dic = {}
        for business in businesses:
            prefix = f'fa{business.id}'
            framework_agreement_forms_dic[business.id] = Framework_agreementForm(auto_id=f'id_{prefix}_%s', instance=business)
        context['framework_agreement_forms'] = framework_agreement_forms_dic   
        # On construit un formulaire de saisie de marché à bons de commande par affaire    
        businesses = Business.objects.filter(customer=company)
        po_market_forms_dic = {}
        for business in businesses:
            # Si un accord_cadre est associé à l'affaire en cours, on initialise le champ accord_cadre à ce dernier
            initial_data = {}
            if business.framework_agreement:
                initial_data['framework_agreement'] = business.framework_agreement
            prefix = f'po{business.id}'
            po_market_forms_dic[business.id] = PO_marketForm(auto_id=f'id_{prefix}_%s', \
                instance=business, initial = initial_data)
        context['po_market_forms'] = po_market_forms_dic   
        return context
    
    def post(self, request, *args, **kwargs):
        pk = self.kwargs['pk'] 
        company = get_object_or_404(Company, pk=pk)      
        customer_form = CompanyForm(instance=company, data=request.POST)
        BusinessFormSet = inlineformset_factory(Company, Business, fk_name='customer',can_delete=False, extra=1, form = BusinessForm)
        business_formset = BusinessFormSet(instance=company, data= request.POST)      
        messages.warning(request, request.POST)
        if 'update' in request.POST:
            if customer_form.has_changed():
                if customer_form.is_valid():
                    customer_form.save()
                    messages.success(request, 'Les données Client ont été mises à jour.')
                else:
                    messages.warning(request, 'les données Client sont incorrectes.'+ str(customer_form.errors))
            if business_formset.has_changed():
                if business_formset.is_valid():
                    # A insérer: la sauvegarde des données dans l'historique
                    business_formset.save()
                    # On recharge la page avec les données mises à jour (pour faire apparaître une nouvelle ligne de saisie)
                    business_formset = BusinessFormSet(instance=company)
                    messages.success(request, 'Les données Affaires ont été mises à jour')
                else:
                    messages.warning(request, 'Les données Affaires sont incorrectes.'+ str(business_formset.errors))

        for key in request.POST.keys():
            if key.startswith('add_framework_agreement'):
                fa = Framework_agreement()
                number_list = request.POST.getlist('number')
                number = next((x for x in number_list if x), None) 
                if number:
                    fa.number = number_list[0]
                    signature_date_list = request.POST.get('signature_date', [''])
                    if signature_date_list:
                        fa.signature_date = datetime.strptime(signature_date_list[0], '%Y-%m-%d')
                    else:
                        fa.signature_date = None
                    fa.company = company
                    fa.save()
                    # On récupère l'id du business concerné
                    business_id = key[len('add_framework_agreement'):]
                    business = Business.objects.get(pk=business_id)
                    business.framework_agreement = fa
                    business.save()
                    messages.success(request,'Le nouvel accord-cadre \"' + str(fa.number) + \
                        '\" a été créé et associé à l\'affaire \"' + str(business.name) +'\".')
                    business_formset = BusinessFormSet(instance=company)  
                else:
                    messages.error(request,'Le numéro d\'accord cadre est obligatoire')
        for key in request.POST.keys():
            if key.startswith('add_po_market'):
                po = PO_market()
                number_list = request.POST.getlist('number')
                number = next((x for x in number_list if x), None)    
                if number:
                    po.number = number
                    signature_date_list = request.POST.get('signature_date', [''])
                    if signature_date_list:
                        po.signature_date = datetime.strptime(signature_date_list[0], '%Y-%m-%d')
                    else:
                        po.signature_date = None
                    po.company = company
                    #po.save()
                    # On récupère l'id du business concerné
                    business_id = key[len('add_po_market'):]
                    business = Business.objects.get(pk=business_id)
                    business.po_market = po
                    #business.save()"""
                    messages.success(request,'Le nouvel marché à bons de commande \"' + str(po.number) + \
                        '\" a été créé et associé à l\'affaire \"' + str(business.name) +'\".')
                    business_formset = BusinessFormSet(instance=company)
                else:
                    messages.error(request,'Le numéro du marché à bons de commande est obligatoire')

        businesses = Business.objects.filter(customer=company)
        # On réinitialise les accords-cadre à afficher dans les affaires
        framework_agreement_forms_dic = {}
        for business in businesses:
            prefix = f'fa{business.id}'
            framework_agreement_forms_dic[business.id] = Framework_agreementForm(auto_id=f'id_{prefix}_%s', instance=business)
        # On réinitialise les marchés à bons de commande à afficher dans les affaires
        po_market_forms_dic = {}
        for business in businesses:
            # Si un accord_cadre est associé à l'affaire en cours, on initialise le champ accord_cadre à ce dernier
            initial_data = {}
            if business.framework_agreement:
                initial_data['framework_agreement'] = business.framework_agreement
            prefix = f'po{business.id}'
            po_market_forms_dic[business.id] = PO_marketForm(auto_id=f'id_{prefix}_%s', \
                instance=business, initial = initial_data)

                
        return render(request, 'salesforecast/company_update_form.html',\
                {'company_form':customer_form, 'business_formset':business_formset, \
                    'framework_agreement_forms':framework_agreement_forms_dic, \
                    'po_market_forms': po_market_forms_dic,  }) 

html code:

div class="bloc"></div>
<form method="post">
  <div class="formulaire shadow">
    <h2 class="text-secondary">Dossier du client: {{company_form.name.value}} </h2>
    <div class="row">       
      <div class=" col-md-1">
        <label for="{{company_form.name.id_for_label}}" class="mx-2 text-secondary">{{company_form.name.label}}</label>
      </div>
      <div class="col-md-3">{{company_form.name}}<div class="fieldWrapper">{{company_form.name.errors}}</div></div>           
      <div class=" col-md-1">
        <label for="{{company_form.SIRET.id_for_label}}" class="mx-2 text-secondary">{{company_form.SIRET.label}}</label>
      </div>
      <div class="col-md-3">{{company_form.SIRET }}<div class="fieldWrapper">{{company_form.SIRET.errors}}</div></div>
      <div class="col-md-2">
        <label for="{{company_form.creation_date.id_for_label}}" class="mx-2 text-secondary">{{company_form.creation_date.label}}</label>
      </div>
      <div class="col-md-2">{{company_form.creation_date }}<div class="fieldWrapper">{{company_form.creation_date.errors}}</div></div>
    </div>
  </div>
  <div class="bloc"></div>

  <div class="accordion" id="accordion">
    <div class="accordion-item shadow" >
      <h3 class="accordion-header" id="headingBusinesses">
        <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseBusinesses" aria-expanded="true" aria-controls="collapseBusinesses">
          <h5>Les affaires</h5>
        </button>
      </h3>
      <div id="collapseBusinesses" class="accordion-collapse collapse show" aria-labelledby="headingBusinesses" data-bs-parent="#accordion">
        <div class="accordion-body">
          {% csrf_token %}
          {{ business_formset.management_form }}
          {% for form in business_formset %}
            <div class="row mx-2">
              <div class="col-md-auto ">
                <td style = "display:none">{{ form.id }}</td>
                {% if form.instance.id %}
                <a href="/salesforecast/businesses/{{form.instance.id}}" title="Gérer les échéances prévisionnelles de facturation"><i class='bx bxs-detail nav_icon'></i></a>
                {% endif %}
              </div>
              <div class="col">
                {{form.name}}
              </div>
              <div class="col-md-auto">
                <label for="{{form.account_exec.id_for_label}}">Responsable</label>
                {{form.account_exec}}
              </div>
            </div>
            <div class="row mx-2">
              <div class="col-md-3">
                <label for="{{form.framework_agreement.id_for_label}}">Accord-cadre</label>
                {{form.framework_agreement}}
                {% if form.instance.id %}
                  <span class="btn btn-link" data-bs-toggle="modal" data-bs-target="#AddFrameworkAgreement{{form.instance.id}}">
                    <i class='bx bxs-plus-circle nav_icon' title="Ajouter un accord-cadre et y associer cette affaire"></i>
                  </span>
                  <!-- Modal d'ajout d'un accord-cadre-->
                  <div class="modal fade" id="AddFrameworkAgreement{{form.instance.id}}" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel">
                    <div class="modal-dialog">
                      <div class="modal-content">
                        <div class="modal-header">
                          <h5 class="modal-title" id="AddFrameworkAgreementLabel{{form.instance.id}}">Ajout d'un accord-cadre</h5>
                          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                        </div>
                        <div class="modal-body">

                          {{ framework_agreement_forms|get_item:form.instance.id }}

                        </div>
                        <div class="modal-footer">
                          <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
                          <button type="submit" formmethod="POST"  name="add_framework_agreement{{form.instance.id}}" 
                              id="add_framework_agreement{{form.instance.id}}"  class="btn btn-primary">
                              <i class='bx bx-save nav_icon'></i>Confirmer
                          </button>
                        </div>
                      </div>
                    </div>
                  </div>
                {% endif %}              
              </div>
              <div class="col-md-3">
                <label for="{{form.po_market.id_for_label}}">Marché à BC</label>
                {{form.po_market}}
                {% if form.instance.id %}
                  <span class="btn btn-link" data-bs-toggle="modal" data-bs-target="#AddPOMarket{{form.instance.id}}">
                    <i class='bx bxs-plus-circle nav_icon' title="Ajouter un accord-cadre et y associer cette affaire"></i>
                  </span>
                  <!-- Modal d'ajout d'un accord-cadre-->
                  <div class="modal fade" id="AddPOMarket{{form.instance.id}}" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel">
                    <div class="modal-dialog">
                      <div class="modal-content">
                        <div class="modal-header">
                          <h5 class="modal-title" id="AddPOMarketLabel{{form.instance.id}}">Ajout d'un marché à bons de commande</h5>
                          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                        </div>
                        <div class="modal-body">

                          {{ po_market_forms|get_item:form.instance.id }}

                        </div>
                        <div class="modal-footer">
                          <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
                          <button type="submit" formmethod="POST"  name="add_po_market{{form.instance.id}}" 
                              id="add_po_market{{form.instance.id}}"  class="btn btn-primary">
                              <i class='bx bx-save nav_icon'></i>Confirmer
                          </button>
                        </div>
                      </div>
                    </div>
                  </div>
                {% endif %}   
              </div>
            </div>
            <div class="row mx-2"> 
              <div class="col-md-3">
                <label for="{{form.ponderation.id_for_label}}">{{form.ponderation.label}}</label>          
                {{ form.ponderation }}
              </div>
              <div class="col-md-3 ">
                <label for="{{form.amount.id_for_label}}">{{form.amount.label}}</label>          
                {{ form.amount }}
              </div>
              <div class="col-md-3">
                <label for="{{form.amount_pondered.id_for_label}}">{{form.amount_pondered.label}}</label>          
                {{ form.amount_pondered }}
              </div>
              <div class="col-md-3">
                <label for="{{form.amount_to_plan.id_for_label}}">{{form.amount_to_plan.label}}</label>          
                {{ form.amount_to_plan }}
              </div>  
            </div>    
            <div class="line"></div>
          {% endfor %}
        </div>
      </div>
    </div>
          </div>
      </div>
 
    </div>
  </div>
  <div class="bloc"></div>
  <div>
    <button id = "update" name="update" type="submit" class="btn btn-outline-secondary">
    <i class='bx bx-save'></i>
    Sauvegarder
    </button>
  </div>

</form>

example of what I am getting in my request.POST

<QueryDict: {'name': ['Premier client saisi'], 'SIRET': [''], 'creation_date': ['2023-04-28'], 'initial-creation_date': ['2023-04-28'], 'csrfmiddlewaretoken': ['Clgf4HgUjZ5rtIM5zCV5O55p3wI178S02biIz0EfB8Ipnaa92EaVSkD8BLEVCvkp', 'Clgf4HgUjZ5rtIM5zCV5O55p3wI178S02biIz0EfB8Ipnaa92EaVSkD8BLEVCvkp', 'Clgf4HgUjZ5rtIM5zCV5O55p3wI178S02biIz0EfB8Ipnaa92EaVSkD8BLEVCvkp'], 'customer-TOTAL_FORMS': ['4'], 'customer-INITIAL_FORMS': ['3'], 'customer-MIN_NUM_FORMS': ['0'], 'customer-MAX_NUM_FORMS': ['1000'], 'customer-0-id': ['1'], 'customer-0-name': ['Affaire détectée 1er client'], 'customer-0-account_exec': [''], 'customer-0-framework_agreement': ['59'], 'customer-0-po_market': ['1'], 'signature_date': ['', '', ''], 'framework_agreement': ['59', '59', ''], 'number': ['', '', ''], 'customer-0-ponderation': ['1'], 'customer-0-amount': ['100.00'], 'customer-1-id': ['2'], 'customer-1-name': ['Affaire signée 1er client'], 'customer-1-account_exec': [''], 'customer-1-framework_agreement': ['59'], 'customer-1-po_market': [''], 'customer-1-ponderation': ['3'], 'customer-1-amount': ['200.00'], 'customer-2-id': ['3'], 'customer-2-name': ['Affaire terminée 1er client'], 'customer-2-account_exec': [''], 'customer-2-framework_agreement': [''], 'customer-2-po_market': [''], 'customer-2-ponderation': ['4'], 'customer-2-amount': ['300.00'], 'customer-3-id': [''], 'customer-3-name': [''], 'customer-3-account_exec': [''], 'customer-3-framework_agreement': [''], 'customer-3-po_market': [''], 'customer-3-ponderation': ['1'], 'customer-3-amount': [''], 'contact_set-TOTAL_FORMS': ['1'], 'contact_set-INITIAL_FORMS': ['0'], 'contact_set-MIN_NUM_FORMS': ['0'], 'contact_set-MAX_NUM_FORMS': ['1000'], 'contact_set-0-id': [''], 'contact_set-0-first_name': [''], 'contact_set-0-last_name': [''], 'contact_set-0-job': [''], 'contact_set-0-email': [''], 'contact_set-0-phone': [''], 'address_set-TOTAL_FORMS': ['1'], 'address_set-INITIAL_FORMS': ['0'], 'address_set-MIN_NUM_FORMS': ['0'], 'address_set-MAX_NUM_FORMS': ['1000'], 'address_set-0-id': [''], 'address_set-0-label': [''], 'address_set-0-street_number': [''], 'address_set-0-street': [''], 'address_set-0-postal_code': [''], 'address_set-0-city': [''], 'address_set-0-country': [''], 'update': ['']}>

No key “fa1_number” or “fa1_signature_date”, just “number” and “signature_date” while the html code contains those:

                  <span class="btn btn-link" data-bs-toggle="modal" data-bs-target="#AddFrameworkAgreement4">
                    <i class='bx bxs-plus-circle nav_icon' title="Ajouter un accord-cadre et y associer cette affaire"></i>
                  </span>
                  <!-- Modal d'ajout d'un accord-cadre-->
                  <div class="modal fade" id="AddFrameworkAgreement4" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel">
                    <div class="modal-dialog">
                      <div class="modal-content">
                        <div class="modal-header">
                          <h5 class="modal-title" id="AddFrameworkAgreementLabel4">Ajout d'un accord-cadre</h5>
                          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                        </div>
                        <div class="modal-body">

                          <tr>
    <th><label for="id_fa4_signature_date">Date de signature :</label></th>
    <td>
      
      <div class="input-group dbdp">
  <input type="text" name="signature_date" class="form-control text-primary" id="id_fa4_signature_date" data-dbdp-config="{&quot;variant&quot;: &quot;date&quot;, &quot;backend_date_format&quot;: &quot;YYYY-MM-DD&quot;, &quot;options&quot;: {&quot;showTodayButton&quot;: true, &quot;locale&quot;: &quot;fr&quot;, &quot;calendarWeeks&quot;: true, &quot;format&quot;: &quot;DD/MM/YYYY&quot;}}" data-dbdp-debug="">


  <div class="input-group-addon input-group-append input-group-text">
    <i class="bi-calendar"></i>
  </div>
</div>

Hello,
Has anyone an idea to help me to solve this issue?
Best regards
Richard

It is hard to help you with the information you provide, but if I was you, I recode my view with a function view.