在Django模板中使用“through”表格

huangapple go评论64阅读模式
英文:

Using the "through " table in a Django template

问题

I can help you translate the provided text. Here's the translation:

"我对Django非常陌生,已经尝试过寻找解决方案但没有成功。
我有这三个模型:

class Items(models.Model):
    ID = models.AutoField(primary_key=True)
    name = models.TextField()
    price = MoneyField(blank=True, null=True, decimal_places=2, max_digits=8, default_currency='EUR')
    def __str__(self):
        return self.name

class Interventions(models.Model):
    ID = models.AutoField(primary_key=True)
    date = models.DateField()
    time = models.TimeField(blank=True, null=True) 
    description = models.TextField()
    ID_items = models.ManyToManyField(Items, through='Details')
    
class Details(models.Model):
    ID = models.AutoField(primary_key=True)
    ID_item = models.ForeignKey(Items, on_delete = models.CASCADE)
    ID_intervention = models.ForeignKey(Interventions, on_delete = models.CASCADE)
    quantity = models.IntegerField(null=True, blank=True)
    def __str__(self):
        return str(self.ID_intervention)

我想要能够直接在'intervention.html'模板中使用'Details'表的'quantity'字段。

intervention.html

{% extends 'base.html' %}
{% load static %}
{% block content %}
	<h1>插入干预</h1> 
    <br>

    <form action="" method="post">
      {% csrf_token %}
      <div class="grid-container">
        <h3>日期</h3><div>{{ form.date }}</div>
        <h3>时间</h3><div>{{ form.time }}</div>
        <h3>描述</h3><div>{{ form.description }}</div>
		<h3>物品/物品</h3><div class="overflow_scroll">{{ form.ID_items}}</div>
      </div>
      <br>
      <input type="submit" value="保存">
    </form>
	</div>

{% endblock %}

不幸的是,'intervention.html'模板由'forms.py'文件中包含的'InterventionForm' ModelForm形成。
所以我不知道该如何继续。
我想要得到的结果会像图中的样子。

感谢大家的每一点帮助。"

英文:

I'm really new to Django and I've already tried to find a solution without success.
I have these three models:

class Items(models.Model):
    ID = models.AutoField(primary_key=True)
    name = models.TextField()
    price = MoneyField(blank=True, null=True, decimal_places=2, max_digits=8, default_currency=&#39;EUR&#39;)
    def __str__(self):
        return self.name

class Interventions(models.Model):
    ID = models.AutoField(primary_key=True)
    date = models.DateField()
    time = models.TimeField(blank=True, null=True) 
    description = models.TextField()
    ID_items = models.ManyToManyField(Items, through=&quot;Details&quot;)
    
class Details(models.Model):
    ID = models.AutoField(primary_key=True)
    ID_item = models.ForeignKey(Items, on_delete = models.CASCADE)
    ID_intervention = models.ForeignKey(Interventions, on_delete = models.CASCADE)
    quantity = models.IntegerField(null=True, blank=True)
    def __str__(self):
        return str(self.ID_intervention)

I would like to be able to use the "quantity" field of the Details table, directly in the "intervention.html" template.

intervention.html

{% extends &#39;base.html&#39;%}
{% load static %}
{% block content %}
	&lt;h1&gt;INSERT an INTERVENTION&lt;/h1&gt; 
    &lt;br&gt;

    &lt;form action=&quot;&quot; method=&quot;post&quot;&gt;
      {% csrf_token %}
      &lt;div class=&quot;grid-container&quot;&gt;
        &lt;h3&gt;Date&lt;/h3&gt;&lt;div&gt;{{ form.date }}&lt;/div&gt;
        &lt;h3&gt;Time&lt;/h3&gt;&lt;div&gt;{{ form.time }}&lt;/div&gt;
        &lt;h3&gt;Description&lt;/h3&gt;&lt;div&gt;{{ form.description }}&lt;/div&gt;
		&lt;h3&gt;Item/Items&lt;/h3&gt;&lt;div class=&quot;overflow_scroll&quot;&gt;{{ form.ID_items}}&lt;/div&gt;
      &lt;/div&gt;
      &lt;br&gt;
      &lt;input type=&quot;submit&quot; value=&quot;Save&quot;&gt;
    &lt;/form&gt;[![example][1]][1]
	&lt;/div&gt;

{% endblock %}

Unfortunately, the "intervention.html" template is formed by the "InterventionForm" ModelForm contained in the "forms.py" file.
And so I don't know how to proceed.
The result I'd like to get would be something like in the image.

Thank you all for every little help.

答案1

得分: 0

After several tries, I finally arrived at the solution.

Basically I changed the structure of Models.py, Forms.py and Views.py.

After which the rest is simple, in fact it is sufficient to adapt the html sheet of the interventions on which the "Intervention" view works and add some Javascript code to duplicate the formset to insert.

Models.py

class Articles(models.Model):
     ID = models.AutoField(primary_key=True)
     item_description = models.TextField()
     class Meta:
        ordering = ('item_description',)

class Interventions(models.Model):
     ID = models.AutoField(primary_key=True)
     start_date = models.DateField()
     description = models.TextField()
     Item_ID = models.ManyToManyField(Items, through="ItemDetail", blank=True)
    
class ItemDetail(models.Model):
     ID = models.AutoField(primary_key=True)
     article_id = models.ForeignKey(articles, on_delete = models.CASCADE)
     Intervention_ID = models.ForeignKey(Interventions, on_delete = models.CASCADE)
     quantity = models.IntegerField(null=True, blank=True, default='1')

Forms.py

class FormIntervention(forms.ModelForm):
     class Meta:
         model = Interventions
         exclude = ['ID', 'Item_ID']
         fields = [ 'start_date',
                     'intervention_time',
                     'description',
                 ]

class FormDetail(forms.ModelForm):
     class Meta:
         model = ItemDetail
         exclude = ['ID']
         fields = [ 'Article_ID',
                     'Intervention_ID',
                     'amount']

class ArticleForm(forms.ModelForm):
     class Meta:
         model = Articles
         fields = '__all__'
         widgets = {'item_description': TextInput(),}

#------------------------------------------------------------------------------------------------
FormSetDetail = inlineformset_factory(
     Interventions, DetailArticles,
     form = FormDetail,
     extra=1
)

Views.py

@login_required
def intervention(request):
     if request.method == 'POST':
         formIntervention = FormIntervention(request.POST or None, request.FILES or None)
         formsetArticles = FormSetDetail(request.POST or None, request.FILES or None, prefix='child_formset')
         if formIntervention.is_valid() and formsetArticles.is_valid():
             formint = formIntervention.save()
             formsetItems.instance = formint
             formsetArticles.save()

             messages.success(request, 'Action successfully added.')
         return HttpResponseRedirect('/') #used to prevent the form from being saved more than once after page refresh

     else:
         formIntervention = FormIntervention()
         formsetArticles = FormSetDetail(prefix='child_formset')

     return render(request, "addIntervention.html", {'formIntervention': formIntervention,
                                                   'formsetItems': formsetItems})

addIntervention.html:

     <form id="id-form-intervention" method="post">
       {% csrf_token %}
           <div id="form-container" class="external_scroll_reduced">
             {{ formsetArticles.management_form }} <!--add info on the formset in the form of hidden <input> (form-TOTAL_FORMS)-->
             <div id="div_formset">
               {% for form in formsetArticles %}
                   <div class="form-detail">
                       {{ form }}
                   </div>
               {% endfor %}
             </div>
             <button id="add-form" type="button" disabled>Add article</button>
           </div><br>
       <div class="item16"><input class="button" type="submit" value="save"></div>
     </form>

Formset.js

let detailForm = document.querySelectorAll('.form-detail');
let container = document.querySelector('#form-container');
let btnAdd = document.querySelector('#add-form');
let totalForms = document.querySelector('#id_child_formset-TOTAL_FORMS'); //thanks to {{ formset.management_form }}
let formNum = detailForm.length - 1; //-1 because it starts at 0

let ArrSelectItems = [];
let ArrDeleteItems = [];

let divFormsets = document.querySelector('#div_formset');
let labelsFormset = divFormsets.getElementsByTagName('label');
let checkboxes = divFormsets.querySelectorAll('input[type="checkbox"]');

for(let i = 0; i < labelsFormset.length; i++){labelsFormset[i].hidden = true;};

btnAdd.addEventListener('click', addForm);

let updateFormset = setInterval(function(){
   for(i=0; i < totalForms.value; i++){
     idFormsetArticle = 'id_child_formset-' + i + '-ID_article';
     idFormsetDelete = 'id_child_formset-'+ i + '-DELETE';
     selectFormset = document.getElementById(idFormsetArticle);
     deleteFormset = document.getElementById(idFormsetDelete);
     ArrSelectItems.push(selectFormset);
     ArrDeleteItems.push(deleteFormset);

     if(ArrSelectItems[i].value === ""){
       checkboxDelete = ArrDeleteArticles[i];
       checkboxDelete.checked = true;
     }else{
       checkboxDelete = ArrDeleteArticles[i];
       checkboxDelete.checked = false;
     };
   }
   //convert checkbox to boolean based on whether they are checked
   let checkboxValues = Array.from(ArrDeleteItems).map(function(checkbox){
     return checkbox.checked;
   });
   //check each checkbox value (.every) --> Check if at least one checkbox is selected (therefore the article has not been selected)
   let result = checkboxValues.every(function(value){
     return value === false;
   });

   if(result === false){
     btnAdd.disabled = true;
   }else{
     btnAdd.disabled = false;
   }

   ArrSelectItems = [];
   ArrDeleteItems = [];

}, 100);

  
function addForm(e){
   e.preventDefault();

   let newForm = detailForm[0].cloneNode(true); // clone Form detail
   let formRegexIdItem = RegExp(`child_formset-(\\d){1}-Item_ID`,'g'); //Regex to find all instances of the form by number number (I used the for loop, damn!)
   let formRegexQuantity = RegExp(`child_formset-(\\d){1}-quantity`,'g');
   let formRegexDelete = RegExp(`child_formset-(\\d){1}-DELETE`,'g');
  
   formNum++;
  
   newForm.innerHTML = newForm.innerHTML.replace(formRegexArticleId, `child_formset-${formNum}-Article_ID`); //update new form number
   newForm.innerHTML = newForm.innerHTML.replace(formRegexQuantity, `child_formset-${formNum}-quantity`);
   newForm.innerHTML = newForm.innerHTML.replace(formRegexDelete, `child_formset-${formNum}-DELETE`);
   container.insertBefore(newForm, btnAdd) // insert the new form, before the add button
  
   totalForms.setAttribute('value', `${formNum+1}`) //increment the total number of forms, contained in {{ formset.management_form }}, otherwise Django Error
}

Thanks anyway everyone!!!

英文:

After several tries, I finally arrived at the solution.

Basically I changed the structure of Models.py, Forms.py and Views.py.

After which the rest is simple, in fact it is sufficient to adapt the html sheet of the interventions on which the "Intervention" view works and add some Javascript code to duplicate the formset to insert.

Models.py

class Articles(models.Model):
ID = models.AutoField(primary_key=True)
item_description = models.TextField()
class Meta:
ordering = (&#39;item_description&#39;,)
class Interventions(models.Model):
ID = models.AutoField(primary_key=True)
start_date = models.DateField()
description = models.TextField()
Item_ID = models.ManyToManyField(Items, through=&quot;ItemDetail&quot;, blank=True)
class ItemDetail(models.Model):
ID = models.AutoField(primary_key=True)
article_id = models.ForeignKey(articles, on_delete = models.CASCADE)
Intervention_ID = models.ForeignKey(Interventions, on_delete = models.CASCADE)
quantity = models.IntegerField(null=True, blank=True, default=&#39;1&#39;)

Forms.py

class FormIntervention(forms.ModelForm):
class Meta:
model = Interventions
exclude = [&#39;ID&#39;, &#39;Item_ID&#39;]
fields = [ &#39;start_date&#39;,
&#39;intervention_time&#39;,
&#39;description&#39;,
]
class FormDetail(forms.ModelForm):
class Meta:
model = ItemDetail
exclude = [&#39;ID&#39;]
fields = [ &#39;Article_ID&#39;,
&#39;Intervention_ID&#39;,
&#39;amount&#39;]
class ArticleForm(forms.ModelForm):
class Meta:
model = Articles
fields = &#39;__all__&#39;
widgets = {&#39;item_description&#39;: TextInput(),}
#------------------------------------------------------------------------------------------------
FormSetDetail = inlineformset_factory(
Interventions, DetailArticles,
form = FormDetail,
extra=1
)

Views.py

@login_required
def intervention(request):
if request.method == &#39;POST&#39;:
formIntervention = FormIntervention(request.POST or None, request.FILES or None)
formsetArticles = FormSetDetail(request.POST or None, request.FILES or None, prefix=&#39;child_formset&#39;)
if formIntervention.is_valid() and formsetArticles.is_valid():
formint = formIntervention.save()
formsetItems.instance = formint
formsetArticles.save()
messages.success(request, &#39;Action successfully added.&#39;)
return HttpResponseRedirect(&#39;/&#39;) #used to prevent the form from being saved more than once after page refresh
else:
formIntervention = FormIntervention()
formsetArticles = FormSetDetail(prefix=&#39;child_formset&#39;)
return render(request, &quot;addIntervention.html&quot;, {&#39;formIntervention&#39;: formIntervention,
&#39;formsetItems&#39;: formsetItems})

addIntervention.html:

     &lt;form id=&quot;id-form-intervention&quot; method=&quot;post&quot;&gt;
{% csrf_token %}
&lt;div id=&quot;form-container&quot; class=&quot;external_scroll_reduced&quot;&gt;
{{ formsetArticles.management_form }} &lt;!--add info on the formset in the form of hidden &lt;input&gt; (form-TOTAL_FORMS)--&gt;
&lt;div id=&quot;div_formset&quot;&gt;
{% for form in formsetArticles %}
&lt;div class=&quot;form-detail&quot;&gt;
{{ form }}
&lt;/div&gt;
{% endfor %}
&lt;/div&gt;
&lt;button id=&quot;add-form&quot; type=&quot;button&quot; disabled&gt;Add article&lt;/button&gt;
&lt;/div&gt;&lt;br&gt;
&lt;div class=&quot;item16&quot;&gt;&lt;input class=&quot;button&quot; type=&quot;submit&quot; value=&quot;save&quot;&gt;&lt;/div&gt;
&lt;/form&gt;

Formset.js

let detailForm = document.querySelectorAll(&#39;.form-detail&#39;);
let container = document.querySelector(&#39;#form-container&#39;);
let btnAdd = document.querySelector(&#39;#add-form&#39;);
let totalForms = document.querySelector(&#39;#id_child_formset-TOTAL_FORMS&#39;); //thanks to {{ formset.management_form }}
let formNum = detailForm.length - 1; //-1 because it starts at 0
let ArrSelectItems = [];
let ArrDeleteItems = [];
let divFormsets = document.querySelector(&#39;#div_formset&#39;);
let labelsFormset = divFormsets.getElementsByTagName(&#39;label&#39;);
let checkboxes = divFormsets.querySelectorAll(&#39;input[type=&quot;checkbox&quot;]&#39;);
for(let i = 0; i &lt; labelsFormset.length; i++){labelsFormset[i].hidden = true;};
btnAdd.addEventListener(&#39;click&#39;, addForm);
let updateFormset = setInterval(function(){
for(i=0; i &lt; totalForms.value; i++){
idFormsetArticle = &#39;id_child_formset-&#39; + i + &#39;-ID_article&#39;;
idFormsetDelete = &#39;id_child_formset-&#39;+ i + &#39;-DELETE&#39;;
selectFormset = document.getElementById(idFormsetArticle);
deleteFormset = document.getElementById(idFormsetDelete);
ArrSelectItems.push(selectFormset);
ArrDeleteItems.push(deleteFormset);
if(ArrSelectItems[i].value === &quot;&quot;){
checkboxDelete = ArrDeleteArticles[i];
checkboxDelete.checked = true;
}else{
checkboxDelete = ArrDeleteArticles[i];
checkboxDelete.checked = false;
};
}
//convert checkbox to boolean based on whether they are checked
let checkboxValues = Array.from(ArrDeleteItems).map(function(checkbox){
return checkbox.checked;
});
//check each checkbox value (.every) --&gt; Check if at least one checkbox is selected (therefore the article has not been selected)
let result = checkboxValues.every(function(value){
return value === false;
});
if(result === false){
btnAdd.disabled = true;
}else{
btnAdd.disabled = false;
}
ArrSelectItems = [];
ArrDeleteItems = [];
}, 100);
function addForm(e){
e.preventDefault();
let newForm = detailForm[0].cloneNode(true); // clone Form detail
let formRegexIdItem = RegExp(`child_formset-(\\d){1}-Item_ID`,&#39;g&#39;); //Regex to find all instances of the form by number number (I used the for loop, damn!)
let formRegexQuantity = RegExp(`child_formset-(\\d){1}-quantity`,&#39;g&#39;);
let formRegexDelete = RegExp(`child_formset-(\\d){1}-DELETE`,&#39;g&#39;);
formNum++;
newForm.innerHTML = newForm.innerHTML.replace(formRegexArticleId, `child_formset-${formNum}-Article_ID`); //update new form number
newForm.innerHTML = newForm.innerHTML.replace(formRegexQuantity, `child_formset-${formNum}-quantity`);
newForm.innerHTML = newForm.innerHTML.replace(formRegexDelete, `child_formset-${formNum}-DELETE`);
container.insertBefore(newForm, btnAdd) // insert the new form, before the add button
totalForms.setAttribute(&#39;value&#39;, `${formNum+1}`) //increment the total number of forms, contained in {{ formset.management_form }}, otherwise Django Error
}

Thanks anyway everyone!!!

huangapple
  • 本文由 发表于 2023年5月17日 23:34:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/76273815.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定