使用新的"虚拟"保存基于类的视图formset项目。柱

时间:2018-03-18 16:09:31

标签: python django save formset

我在表单中有一个表格,由表单集生成。

在这种情况下,我的问题是在修改其中一个项目之后保存所有项目,添加一个新的"虚拟" column作为另外两个的总和(仅在显示表时生成,未保存)。 我尝试了不同的方法,但没有人工作。

问题

  • save根本不起作用。当它只是一个表单时,它可以工作,但不适用于formset
  • 我尝试将amountSum box_onebox_two生成,但未成功。我也试过这种方式生成表单,但这不起作用:
formset = modelformset_factory(
    Item, form=ItemForm)(queryset=Item.objects.order_by(
        'code__name').annotate(amount=Sum('box_one') + Sum('box_two')))

此问题与前一个问题有关,但这个新问题更简单: Pre-populate HTML form table from database using Django

StackOverflow以前的相关问题很老,对我不起作用。

我正在使用Django 2.0.2

任何帮助将不胜感激。提前谢谢。

当前代码:

models.py

class Code(models.Model):
    name = models.CharField(max_length=6)
    description = models.CharField(max_length=100)

    def __str__(self):
        return self.name

class Item(models.Model):
    code = models.ForeignKey(Code, on_delete=models.DO_NOTHING)
    box_one = models.IntegerField(default=0)
    box_two = models.IntegerField(default=0)

    class Meta:
        ordering = ["code"]

views.py

class ItemForm(ModelForm):
    description = CharField()

    class Meta:
        model = Item
        fields = ['code', 'box_one', 'box_two']

    def save(self, commit=True):
        item = super(ItemForm, self).save(commit=commit)
        item.box_one = self.cleaned_data['box_one']
        item.box_two = self.cleaned_data['box_two']
        item.code.save()

    def get_initial_for_field(self, field, field_name):
        if field_name == 'description' and hasattr(self.instance, 'code'):
            return self.instance.code.description
        else:
            return super(ItemForm, self).get_initial_for_field(
                field, field_name)


class ItemListView(ListView):
    model = Item

    def get_context_data(self, **kwargs):
        data = super(ItemListView, self).get_context_data()
        formset = modelformset_factory(Item, form=ItemForm)()
        data['formset'] = formset
        return data

urls.py

app_name = 'inventory'
urlpatterns = [
    path('', views.ItemListView.as_view(), name='index'),

item_list.html

...
          <div>
            <form action="" method="post"></form>
            <table>
                {% csrf_token %}
                {{ formset.management_form }}
                {% for form in formset %}
                    <thead>
                        <tr>
                        {% if forloop.first %}
                            <th>{{ form.code.label_tag }}  </th>
                            <th>{{ form.description.label_tag }}  </th>
                            <th> <label>Amount:</label> </th>
                            <th>{{ form.box_one.label_tag }}  </th>
                            <th>{{ form.box_two.label_tag }}  </th>
                        {% endif %}
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>{{ form.code }}</td>
                            <td>{{ form.description }}</td>
                            <td>{{ form.amount }}</td>
                            <td>{{ form.box_one }}</td>
                            <td>{{ form.box_two }}</td>
                        </tr>
                    </tbody>

                {% endfor %}

                <input type="submit" value="Update" />
            </table>
            </form>
          </div>
...

2 个答案:

答案 0 :(得分:3)

你已经走在了正确的道路上。所以你说你需要一个虚拟列。您可以在模型类中定义一个虚拟属性,该属性不会存储在数据库表中,但它可以作为模型类的任何其他属性访问。

这是您应该添加到模型类Item的代码:

class Item(models.Model):
    # existing code

    @property
    def amount(self):
        return self.box_one + self.box_one

现在你可以这样做:

item = Item.objects.get(pk=1)
print(item.box_one) # return for example 1
print(item.box_two) # return for example 2
print(item.amount) # it will return 3 (1 + 2 = 3)

编辑:
通过ModelForm,我们可以访问模型实例,从而访问其所有属性。在模板中渲染模型表单时,我们可以访问如下属性:

{{ form.instance.amount }}

虚拟财产amount背后的想法是将业务逻辑放在模型中,并遵循胖模型 - 瘦控制器方法。因此amountbox_one之和box_two可以在不同的地方重复使用而无需重复代码。

答案 1 :(得分:3)

使用虚拟列

注释查询

ImageViewaggregate expression,在这种情况下,您不希望如何注释此查询。相反,您应该使用F exrepssion添加两个数字字段的值

Sum

因此,您更正的查询集将是

qs.annotate(virtual_col=F('field_one') + F('field_two'))

cezar提供的答案非常有用,如果打算仅将该属性用于行级&#39;操作。但是,如果您打算基于Item.objects.order_by('code__name').annotate(amount=F('box_one') + F('box_two')) 进行查询,则需要对查询进行注释。

保存formset

您未在视图类中提供amount方法。您需要自己提供一个,因为您没有从为您提供一个通用视图继承。请参阅Handling forms with class-based views上的文档。您还应该考虑从处理表单的通用视图继承。例如,post未实现ListView方法,但post确实如此。

请注意,您的模板也不会呈现表单错误。由于您手动呈现了表单集,因此应考虑添加字段错误(例如FormView),以便在HTML中显示验证问题。请参阅rendering fields manually上的文档。

此外,您可以使用{{ form.field.errors}}方法记录/打印错误。例如:

post

然后,如果表单未验证,您应该在控制台/日志中看到错误。