问题描述
我想在我的项目中使用 inlineformset factory
!我已经编写了自己的 js 来添加更多字段,但它只将最后一个表单保存在子表单中!我不想使用jquery.formset.js
这是我的models.py
class Main(models.Model):
admin = models.ForeignKey(User,on_delete=models.CASCADE)
company = models.ForeignKey(Companies,on_delete=models.CASCADE)
items = models.ManyToManyField(Item,through='Child')
invoice_number = models.IntegerField()
class Child(models.Model):
main = models.ForeignKey(Main,on_delete=models.CASCADE)
item = models.ForeignKey(Item,on_delete=models.CASCADE)
quantity = models.IntegerField()
这是我的 views.py ,我使用了基于类的视图
class CreateMainInlineFormset(LoginrequiredMixin,SuccessMessageMixin,CreateView):
model = Main
form_class = MainForm
template_name = 'main/create.html'
def get_context_data(self,*args,**kwargs):
data = super().get_context_data(*args,**kwargs)
if self.request.POST:
data['items'] = ChildInlineFormSet(self.request.POST)
data['items'].full_clean()
else:
data['items'] = ChildInlineFormSet()
return data
def form_valid(self,form):
context = self.get_context_data()
items = context['items']
with transaction.atomic():
self.object = form.save(commit=False)
form.instance.admin = self.request.user
if items.is_valid() and form.is_valid() and items.cleaned_data!={}:
items.instance = self.object
form.save(commit=True)
items.save()
else:
return render(self.request,self.template_name,context)
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy('maininfo:list-item')
这是我的模板html + js脚本,我认为问题出在这里,我不知道如何以正确的方式增加子表单!
$("#addRow").click(function () {
var html = $("#relInputs").clone();
$('#relatiedInput').append(html);
});
$(document).on('click','#removeRow',function () {
if ($("#relatiedInput> #relInputs").length != 1)
$(this).closest('#relInputs').remove();
});
<div class="mt-1 text-right mx-auto" style="direction: rtl;">
<form action="" method="POST">{% csrf_token %}
{{items.management_form}}
<div id="allInputs">
<div class=" grid grid-cols-1 md:grid-cols-3 gap-5">
<div class="flex flex-wrap border grayBG rounded-tl-lg rounded-bl-lg ">
<p
class="w-3/12 bg-red-500 flex scale-100 items-center justify-center whiteCOLOR rounded-tl-lg rounded-bl-lg">
company:
</p>
{{form.company | add_class:'w-7/12 p-2 rounded-tl-lg rounded-bl-lg focus:outline-none text-right grayBG whiteCOLOR'}}
<!-- error -->
{% if form.company.errors %}
<div class="redCOLOR pb-1 my-0 text-center rounded-lg w-full md:w-6/12 mx-auto">{{form.collection.errors}}</div>
{% endif %}
</div>
<div class="flex flex-wrap border grayBG rounded-tl-lg rounded-bl-lg ">
<p
class="w-4/12 bg-red-500 flex scale-100 items-center justify-center whiteCOLOR rounded-tl-lg rounded-bl-lg">
invoice number:
</p>
{{ form.invoice_number | add_class:'w-7/12 p-2 rounded-tl-lg rounded-bl-lg focus:outline-none text-right grayBG whiteCOLOR' }}
<!-- error -->
{% if form.invoice_number.errors %}
<div class="redCOLOR pb-1 my-0 text-center rounded-lg w-full md:w-6/12 mx-auto">{{form.invoice_number.errors}}</div>
{% endif %}
</div>
</div>
<div id="relatiedInput" class="w-full grayBG p-2 mt-6 rounded-lg">
{% for item in items.forms %}
{{item.id}}
<div id="relInputs" class="border p-2 rounded-lg mb-4 mt-3 inp">
<button id="removeRow" type="button" class="bg-red-500 whiteCOLOR px-2 py-1 rounded mb-1"><i class="fas fa-trash"></i></button>
<div class="grid grid-cols-1 md:grid-cols-3 gap-2 mt-3">
<div class="flex flex-wrap border grayBG rounded-tl-lg rounded-bl-lg ">
<p
class="w-5/12 whiteBG flex scale-100 items-center justify-center grayCOLOR rounded-tl-lg rounded-bl-lg">
item:
</p>
{{item.item | add_class:'w-7/12 p-2 rounded-tl-lg rounded-bl-lg focus:outline-none text-right grayBG whiteCOLOR'}}
<!-- error -->
{% if item.item.errors %}
<div class="redCOLOR pb-1 my-0 text-center rounded-lg w-full md:w-6/12 mx-auto">{{item.item.errors}}</div>
{% endif %}
</div>
<div class="flex flex-wrap border grayBG rounded-tl-lg rounded-bl-lg ">
<p
class="w-5/12 whiteBG flex scale-100 items-center justify-center grayCOLOR rounded-tl-lg rounded-bl-lg">
quantity:
</p>
{{item.quantity | add_class:'w-7/12 p-2 rounded-tl-lg rounded-bl-lg focus:outline-none text-right grayBG whiteCOLOR'}}
<!-- error -->
{% if item.quantity.errors %}
<div class="redCOLOR pb-1 my-0 text-center rounded-lg w-full md:w-6/12 mx-auto">{{item.selling_price.errors}}</div>
{% endif %}
</div>
</div>
</div>
{% endfor %}
</div>
</div>
<div class="mt-10">
<button class="grayBG px-6 rounded-lg py-2 whiteCOLOR">
<i class="fas fa-save redCOLOR"></i>
submit
</button>
</div>
</div>
</form>
</div>
解决方法
我就是这样做的
JS:
let container = document.querySelector("#form-container")
let birdForm = document.querySelectorAll(".ingredient-form")
let addButton = document.querySelector("#add-form")
let totalForms = document.querySelector("#id_recipeingredients_set-TOTAL_FORMS")
let formNum = birdForm.length-1
function addForm(e){
let newForm = birdForm[0].cloneNode(true)
let formRegex = RegExp(`recipeingredients_set-(\\d){1}-`,'g')
formNum++
newForm.innerHTML = newForm.innerHTML.replace(formRegex,`recipeingredients_set-${formNum}-`)
let deleteButton = document.createElement('input')
deleteButton.type = "button"
deleteButton.value = "Delete"
deleteButton.className = "delete-btn"
newForm.appendChild(deleteButton)
deleteButton.addEventListener('click',deleteForm)
container.insertBefore(newForm,addButton)
totalForms.setAttribute('value',`${formNum+1}`)
}
function deleteForm(e){
formNum--
e.target.parentNode.remove()
totalForms.setAttribute('value',`${formNum+1}`)
}
蟒蛇:
class RecipeCreateView(LoginRequiredMixin,CreateView):
template_name = 'recipes/create.html'
form_class = RecipeModelForm
queryset = Recipe.objects.all()
success_url = reverse_lazy('recipes:main-recipe-view')
def get(self,request,*args,**kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
ingredient_form = IngredientFormSet()
for n in ingredient_form:
n.fields['unit'].queryset = FoodWeight.objects.none()
return self.render_to_response(self.get_context_data(form=form,ingredient_form=ingredient_form))
def post(self,**kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
ingredient_form = IngredientFormSet(self.request.POST)
if form.is_valid() and ingredient_form.is_valid():
return self.form_valid(form,ingredient_form)
else:
return self.form_invalid(form,ingredient_form)
def form_valid(self,form,ingredient_form):
self.object = form.save(commit=False)
self.object.author = self.request.user.profile
self.object.save()
form.save_m2m()
if self.object.published:
create_action(self.request.user,'Added new recipe.',self.object)
ingredient_form.instance = self.object
ingredient_form.save()
return HttpResponseRedirect(self.get_success_url())
模板:
<form id="form-container" data-unit-url={% url 'recipes:load_weights' %} method='POST' enctype="multipart/form-data">
{% csrf_token %}
{{ ingredient_form.management_form }}
{{ form.as_p }}
{% for form in ingredient_form %}
<div class="ingredient-form">
{{form}}
</div>
{% endfor %}
<button id="add-form" type="button">Add another ingredient</button>
<input type="submit" value="Post recipe">
</form>