我在表单中有一个表格,由表单集生成。
在这种情况下,我的问题是在修改其中一个项目之后保存所有项目,添加一个新的"虚拟" column作为另外两个的总和(仅在显示表时生成,未保存)。 我尝试了不同的方法,但没有人工作。
问题:
save
根本不起作用。当它只是一个表单时,它可以工作,但不适用于formset amount
列Sum
box_one
和box_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>
...
答案 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
背后的想法是将业务逻辑放在模型中,并遵循胖模型 - 瘦控制器方法。因此amount
和box_one
之和box_two
可以在不同的地方重复使用而无需重复代码。
答案 1 :(得分:3)
ImageView
是aggregate 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'))
进行查询,则需要对查询进行注释。
您未在视图类中提供amount
方法。您需要自己提供一个,因为您没有从为您提供一个通用视图继承。请参阅Handling forms with class-based views上的文档。您还应该考虑从处理表单的通用视图继承。例如,post
未实现ListView
方法,但post
确实如此。
请注意,您的模板也不会呈现表单错误。由于您手动呈现了表单集,因此应考虑添加字段错误(例如FormView
),以便在HTML中显示验证问题。请参阅rendering fields manually上的文档。
此外,您可以使用{{ form.field.errors}}
方法记录/打印错误。例如:
post
然后,如果表单未验证,您应该在控制台/日志中看到错误。