django formset bug?
碰到了一个郁闷的问题,修改inlineformset时,全部删掉子表,再新增一行时,报错.
背景: 用django配合jq做动态表格,实现用js动态添加/删除行,并通过inlineformset更新到数据库.示例代码在这里:https://github.com/TommyU/dynamic_form/
重现步骤:
1. 新增一个待办事宜,并设置清单,如下:
2. submit
3. 回头修改(通过列表的update按钮进入),把以上数据全部清空(圈起来的),只留下最后一行空白行,提交.
然后报错了,如下
Environment: Request Method: POST Request URL: http://127.0.0.1:8000/todolist/update/14/ Django Version: 1.6.5 Python Version: 2.7.5 Installed Applications: ('django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'south', 'eForm', 'formset_test') Installed Middleware: ('django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.locale.LocaleMiddleware') Traceback: File "C:\Python27\lib\site-packages\django\core\handlers\base.py" in get_response 112. response = wrapped_callback(request, *callback_args, **callback_kwargs) File "C:\Python27\lib\site-packages\django\views\generic\base.py" in view 69. return self.dispatch(request, *args, **kwargs) File "C:\Python27\lib\site-packages\django\views\generic\base.py" in dispatch 87. return handler(request, *args, **kwargs) File "C:\Python27\lib\site-packages\django\views\generic\edit.py" in post 228. return super(BaseUpdateView, self).post(request, *args, **kwargs) File "C:\Python27\lib\site-packages\django\views\generic\edit.py" in post 171. return self.form_valid(form) File "E:/workspace/django_test/mysite/mysite\formset_test\views.py" in form_valid 31. if iter_form.is_valid(): File "C:\Python27\lib\site-packages\django\forms\formsets.py" in is_valid 292. err = self.errors File "C:\Python27\lib\site-packages\django\forms\formsets.py" in errors 267. self.full_clean() File "C:\Python27\lib\site-packages\django\forms\formsets.py" in full_clean 314. form = self.forms[i] File "C:\Python27\lib\site-packages\django\utils\functional.py" in __get__ 49. res = instance.__dict__[self.func.__name__] = self.func(instance) File "C:\Python27\lib\site-packages\django\forms\formsets.py" in forms 133. forms = [self._construct_form(i) for i in xrange(self.total_form_count())] File "C:\Python27\lib\site-packages\django\forms\models.py" in _construct_form 848. form = super(BaseInlineFormSet, self)._construct_form(i, **kwargs) File "C:\Python27\lib\site-packages\django\forms\models.py" in _construct_form 567. connection=connections[self.get_queryset().db]) File "C:\Python27\lib\site-packages\django\db\models\fields\__init__.py" in get_db_prep_lookup 387. value = self.get_prep_lookup(lookup_type, value) File "C:\Python27\lib\site-packages\django\db\models\fields\__init__.py" in get_prep_lookup 369. return self.get_prep_value(value) File "C:\Python27\lib\site-packages\django\db\models\fields\__init__.py" in get_prep_value 613. return int(value) Exception Type: ValueError at /todolist/update/14/ Exception Value: invalid literal for int() with base 10: ''
post数据:
似乎一定要求子表必需有一行数据是原表的?
update对应的python代码如下:
class todo_listUpdateView(UpdateView): model = todo_list template_name = 'todo_list_create.html' success_url = '/todolist/' form_class = todo_listForm def get_context_data(self, **kwargs): context = super(todo_listUpdateView, self).get_context_data(**kwargs) if self.request.POST: context.update(item_form = todo_itemSet(self.request.POST)) else: context.update(item_form = todo_itemSet(instance =todo_list.objects.get(pk = self.kwargs.get('pk',False)))) return context def form_valid(self, form): iter_form = todo_itemSet(self.request.POST,instance=self.object) if iter_form.is_valid(): self.object = form.save() iter_form.instance = self.object iter_form.save() return HttpResponseRedirect(self.get_success_url()) else: return self.render_to_response(self.get_context_data())
============================经过调试,分析,看文档===================
发现如果用js直接把数据行给remove掉,如果全部move了,会报错.(原因有待分析).
其实,对于inlineformset的更新操作,django有做一个很不错的设计,直接给标志位打标记即可,什么都不用处理.核心修改如下:
1. 从create模板中分离出update模板(因为js处理不同)
2. 将update模板的deleteForm函数修改为:
function deleteForm(btn, prefix) { $(".delete_mark",$(btn).parent().parent()).children().attr("checked","checked") $(btn).parents('.item').hide(); return false; }
同时update模板添加标志位:
<p class="delete_mark">{{form.DELETE}}</p>
py代码不用修改,就这么简单!(其实django都处理好了,有时间要分析下)
===============备注===================
重现以上bug的方法是将todo_listUpdateView的template_name修改为"todo_list_create.html",如下图所示: