简体   繁体   English

Django | 在同一个模板上多次使用同一个表单

[英]Django | Same form multiple times on the same template

I have a project with a model called "List," and a model called "ListItem."我有一个名为“List”的 model 和一个名为“ListItem”的 model 的项目。 There are multiple list items to each list.每个列表有多个列表项。 Here's what I'd like to do.这就是我想做的。 I'd like the user to be able to create a new list with as many items as they deem necessary on one form.我希望用户能够在一个表单上创建一个包含他们认为需要的尽可能多的项目的新列表。

Here's the logic I have so far:这是我到目前为止的逻辑:

models.py:模型.py:

class List(models.Model):
    team = models.ForeignKey(Team, on_delete=models.CASCADE)
    name = models.CharField(max_length=50)

    def __str__(self):
        return self.name


class ListItem(models.Model):
    team_list = models.ForeignKey(List, on_delete=models.CASCADE)
    content = models.TextField()
    index = models.IntegerField()

    def __str__(self):
        return f"{self.team_list} [{self.index}]"

forms.py: forms.py:

class NewListForm(forms.ModelForm):
    name = forms.CharField(max_length=50, label='Card Name')
    team = forms.ModelChoiceField(queryset=models.Team.objects.all())

    class Meta:
        model = models.List
        fields = ['name', 'team']


class NewListItemForm(forms.ModelForm):
    content = forms.CharField(widget=forms.Textarea,
                              label='Item', required=True)

    class Meta:
        model = models.ListItem
        fields = ['content']

views.py:视图.py:

def new_list(request):
    context = {
        'title': 'New List',
        'list_form': NewListForm,
        'list_item_form': NewListItemForm,
    }
    if request.method == 'POST':
        list_form = NewListForm(request.POST)
        list_item_form = NewListItemForm(request.POST)
        if list_form.is_valid() and list_item_form.is_valid():
            list_instance = list_form.save()
            list_item_instance = list_item_form.save(commit=False)
            list_item_instance.team_list = list_instance
            list_item_instance.index = 1
            list_item_instance.save()
            messages.success(request, "List has been created!")
            return redirect('home')
        else:
            messages.error(request, "That list name is already taken")
    return render(request, 'lists/new_list.html', context)

This works for creating 1 list and just 1 item.这适用于创建 1 个列表和 1 个项目。 Ultimately, I'd like the user to be able to add as many additional list item forms as is necessary AND have the "index" field of each item on that list increment by 1 with each new list item instance.最终,我希望用户能够根据需要添加尽可能多的附加列表项 forms 并且使该列表中每个项目的“索引”字段随着每个新的列表项实例增加 1。

Is there some way to loop through each instance of a repeated form and save a unique instance in my db an indefinite number of times?是否有某种方法可以遍历重复表单的每个实例并将唯一实例无限次地保存在我的数据库中?

I don't think you need to increment the index yourself, django should increment it automatically.我认为您不需要自己增加索引,django 应该自动增加它。 I used formset_factory and javascript on my template to have the ability to add fields to the form dynamically.我在模板上使用formset_factory和 javascript 以便能够动态地向表单添加字段。 I am not very familiar with django or javascript (I'm very early in the learning process), but this is how I got it to work in my case:我对 django 或 javascript 不是很熟悉(我在学习过程中很早),但这就是我如何让它在我的情况下工作:

models.py模型.py

class Instructors(models.Model):
    instructors = models.CharField(max_length=60)
    injury = models.ForeignKey("Injury", on_delete=models.CASCADE)

forms.py forms.py

class InstructorForm(forms.Form):
    instructor = forms.CharField(max_length=60, required=False)


InstructorFormset = formset_factory(InstructorForm, extra=1)

views.py (relevant section) views.py (相关部分)

...
formset = InstructorFormset(request.POST)
if formset.is_valid()
    for form in formset:
        if form.cleaned_data:
            instructor = Instructors(
                instructors=form.cleaned_data["instructor"],
                injury=injury,
            )
            instructor.save()
...
context = {"formset":formset}

template.html模板.html

{{ formset.management_form }}
    {% for form in formset %}
    <div class="row form-row spacer">
        {{ form.instructor }}
        <div class="input-group-append">
...
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script type='text/javascript'>
function updateElementIndex(el, prefix, ndx) {
    var id_regex = new RegExp('(' + prefix + '-\\d+)');
    var replacement = prefix + '-' + ndx;
    if ($(el).attr("for")) $(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
    if (el.id) el.id = el.id.replace(id_regex, replacement);
    if (el.name) el.name = el.name.replace(id_regex, replacement);
}
function cloneMore(selector, prefix) {
    var newElement = $(selector).clone(true);
    console.log(newElement);
    var total = $('#id_' + prefix + '-TOTAL_FORMS').val();
    newElement.find(':input:not([type=button]):not([type=submit]):not([type=reset])').each(function() {
        var name = $(this).attr('name');
        if (name != undefined) {
            console.log(name);
            console.log(total);
            var name = name.replace('-' + (total-1) + '-', '-' + total + '-');
            console.log("next");
        };
        var id = 'id_' + name;
        $(this).attr({'name': name, 'id': id}).val('').removeAttr('checked');
    });
    newElement.find('label').each(function() {
        var forValue = $(this).attr('for');
        if (forValue) {
          forValue = forValue.replace('-' + (total-1) + '-', '-' + total + '-');
          $(this).attr({'for': forValue});
        }
    });
    total++;
    $('#id_' + prefix + '-TOTAL_FORMS').val(total);
    $(selector).after(newElement);
    var conditionRow = $('.form-row:not(:last)');
    conditionRow.find('.btn.add-form-row')
    .removeClass('btn-success').addClass('btn-danger')
    .removeClass('add-form-row').addClass('remove-form-row')
    .html('-');
    return false;
}
function deleteForm(prefix, btn) {
    var total = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val());
    if (total > 1){
        btn.closest('.form-row').remove();
        var forms = $('.form-row');
        $('#id_' + prefix + '-TOTAL_FORMS').val(forms.length);
        for (var i=0, formCount=forms.length; i<formCount; i++) {
            $(forms.get(i)).find(':input').each(function() {
                updateElementIndex(this, prefix, i);
            });
        }
    }
    return false;
}
$(document).on('click', '.add-form-row', function(e){
    e.preventDefault();
    cloneMore('.form-row:last', 'form');
    return false;
});
$(document).on('click', '.remove-form-row', function(e){
    e.preventDefault();
    deleteForm('form', $(this));
    return false;
});
</script>

I don't remember where I got the js code, but I did not write it and can't take credit for it.我不记得我从哪里得到的 js 代码,但我没有写它,也不能归功于它。 Hopefully you have a better understanding than I do, but it works for me as is.希望你比我有更好的理解,但它对我有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM