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="{"variant": "date", "backend_date_format": "YYYY-MM-DD", "options": {"showTodayButton": true, "locale": "fr", "calendarWeeks": true, "format": "DD/MM/YYYY"}}" data-dbdp-debug="">
<div class="input-group-addon input-group-append input-group-text">
<i class="bi-calendar"></i>
</div>
</div>