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

Abe*_*Paz 10 python django save formset

我在表单中有一个表格,由formset生成.

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

问题:

  • save根本不起作用.当它只是一种形式时它起作用,但不适用于形式
  • 我想,以产生列amount作为Sumbox_onebox_two没有成功.我也试过这种方式生成表单,但这不起作用:
formset = modelformset_factory(
    Item, form=ItemForm)(queryset=Item.objects.order_by(
        'code__name').annotate(amount=Sum('box_one') + Sum('box_two')))
Run Code Online (Sandbox Code Playgroud)

此问题与前一个问题有关,但这个新问题更简单: 使用Django从数据库预填充HTML表单表

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"]
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

urls.py

app_name = 'inventory'
urlpatterns = [
    path('', views.ItemListView.as_view(), name='index'),
Run Code Online (Sandbox Code Playgroud)

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>
...
Run Code Online (Sandbox Code Playgroud)

cez*_*zar 3

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

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

class Item(models.Model):
    # existing code

    @property
    def amount(self):
        return self.box_one + self.box_one
Run Code Online (Sandbox Code Playgroud)

现在你可以这样做:

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)
Run Code Online (Sandbox Code Playgroud)

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

{{ form.instance.amount }}
Run Code Online (Sandbox Code Playgroud)

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

  • 这很好地解决了OP的问题。但应该注意的是,不能使用属性来查询对象。例如 `Item.objects.filter(amount=x)` 是不可能的。它们应该被认为仅可用于“行级”操作。 (2认同)