组合形式和 model_formset 的 NoReverseMatch 错误

NoReverseMatch error for combined form and model_formset

我一直在尝试使用 model_formsets 创建一个结合父模型和子模型的 for,方法是遵循此 tutorial.

中的用例三

型号

class Shoppinglist(models.Model):
    name = models.CharField(max_length=50)
    description = models.TextField(max_length=2000)
    created = models.DateField(auto_now_add=True) 
    created_by = models.ForeignKey(User, related_name='lists', on_delete=models.CASCADE)
    last_updated = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name

class Item(models.Model):
    name = models.CharField(max_length=80, unique=True)
    amount = models.IntegerField(default=1)
    shoppinglist = models.ForeignKey(Shoppinglist, on_delete=models.CASCADE)

    def __str__(self):
        return self.name

网址

urlpatterns = [
   url(r'^shoppinglists/(?P<pk>\d+)/$', views.shoppinglist_list, name='shoppinglist_list'),
   url(r'^shoppinglists/new/$', views.create_shoppinglist_with_items, name='shoppinglist_new'),
]

表格

class ShoppingListForm(forms.ModelForm):
    description = forms.CharField(
        widget=forms.Textarea(
            attrs={'rows': 5, 'placeholder': 'Tell us about your list?'}
        ), 
        max_length=4000,
        help_text='The max length of the text is 4000 characters.'
    )

    class Meta:
        model = Shoppinglist
        fields = ['name', 'description']

ItemFormset = modelformset_factory(
    Item,
    fields=('name', 'amount'),
    extra=1,
    widgets={
        'name': forms.TextInput(
            attrs={
                'class': 'form-control',
                'placeholder': 'Enter Item Name here'
            }
        )
    },
    can_delete=True
    
)

查看

@login_required
def create_shoppinglist_with_items(request):
    template_name = 'list_with_items.html'
    if request.method == 'GET':
        listform = ShoppinglistForm(request.GET or None)
        formset = ItemFormset(queryset=Item.objects.none())
    elif request.method == 'POST':
        listform = ShoppinglistForm(request.POST)
        formset = ItemFormset(request.POST)
        if listform.is_valid() and formset.is_valid():
            shoppinglist = listform.save(commit=False)
            shoppinglist.created_by = request.user
            shoppinglist = listform.save()
            for form in formset:               
                item = form.save(commit=False)
                item.shoppinglist = shoppinglist
                item.save()
            return redirect('shoppinglist_list', pk=shoppinglist.pk)
    return render(request, template_name, {
        'listform': listform,
        'formset': formset,
    })

模板

{% block content %}
<form method="post">
    {% csrf_token %}

    <label>List Name</label>
    {{ listform.name }}
    {% if listform.first_name.errors %}
        {% for error in listform.first_name.errors %}
            {{ error|escape }}
        {% endfor %}
    {% endif %}

    <label>Description</label>
    {{ listform.description }}
    {% if listform.description.errors %}
        {% for error in listform.description.errors %}
            {{ error|escape }}
        {% endfor %}
    {% endif %}

    {{ formset.management_form }}

    {% for form in formset %}
        <div class="item-formset">
            {{ form.amount }}
            {% if form.amount.errors %}
                {% for error in form.amount.errors %}
                    {{ error|escape }}
                {% endfor %}
            {% endif %}

            {{ form.name }}
            {% if form.name.errors %}
                {% for error in form.name.errors %}
                    {{ error|escape }}
                {% endfor %}
            {% endif %}
        </div>
    {% endfor %}

    {% if formset.non_form_errors %}
        {% for error in formset.non_form_errors %}
            {{ error|escape }}
        {% endfor %}
    {% endif %}

    <div class="row spacer">       
            <button type="submit" class="btn btn-block btn-primary">Create</button>
    </div>
</form>
{% endblock %}

{% block extra_js %}
<script>
  $('.item-formset').formset({
      addText: 'add item',
      deleteText: 'remove'
  });
</script>
{% endblock %}

我已经修改了代码,以便将用户带到新创建的父模型,但是当我转到该页面时却收到 NoReverseMatch 错误。

Reverse for 'shoppinglist_list' with arguments '('',)' not found. 1 pattern(s) tried: ['shoppinglists/(?P<pk>\d+)/$']

来自docs

If you call save() with commit=False, then it will return an object that hasn’t yet been saved to the database. In this case, it’s up to you to call save() on the resulting model instance.

完成 shoppinglist = listform.save(commit=False) 之后,您在 shoppinglist 变量中获得了模型实例。

所以你应该做 shoppinglist = shoppinglist.save() 而不是 shoppinglist = listform.save()

保存模型实例后,您可以访问 shoppinglist.pk

只是为了关闭此票证,模板中有一个拼写错误,我已更正,这是导致此问题的原因。所以上面的代码现在可以工作了。