如果POST请求只有一个字段作为参数,如何使用ModelForm仅更新实例中的一个字段? ModelField尝试覆盖POST请求中未传递的字段,其中包含None,导致数据丢失。
我有一个+25字段的模型说
class C(models.Model):
a = models.CharField(max_length=128)
b = models.CharField(max_length=128)
...
x = models.IntegerField()
我有一个桌面应用程序执行POST请求,以便通过views.py中公开的api方法编辑C的实例
在api方法中,我使用ModelForm验证字段如下:
form = CModelForm(request.POST, instance=c_instance)
if form.is_valid():
form.save()
当执行save()时,django会抱怨某些其他字段不能为null或(如果所有字段都是可选字段)会用None覆盖它们。
有人知道如何管理吗?我会手动进行所有检查并手动更新,但该模型有很长的字段列表......
答案 0 :(得分:12)
得到了这个数字。我所做的是使用实例中的值更新request.POST字典 - 以便自动显示所有未更改的字段。这样就可以了:
from django.forms.models import model_to_dict
from copy import copy
def UPOST(post, obj):
'''Updates request's POST dictionary with values from object, for update purposes'''
post = copy(post)
for k,v in model_to_dict(obj).iteritems():
if k not in post: post[k] = v
return post
然后你就做了:
form = CModelForm(UPOST(request.POST,c_instance),instance=c_instance)
答案 1 :(得分:8)
您可以通过如下指定这些字段来使用ModelForm中的字段子集:
class PartialAuthorForm(ModelForm):
class Meta:
model = Author
fields = ('name', 'title')
来自文档:
如果在使用ModelForm创建表单时指定字段或排除, 然后不会设置不在结果表单中的字段 表单的save()方法。
https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#using-a-subset-of-fields-on-the-form
答案 2 :(得分:1)
Django的ModelForm
并非旨在处理模型上任意字段子集的部分更新。
桌面应用程序OP所描述的用例,可以通过Django Rest Framework更好地处理。{/ p>
具体来说,您将创建一个继承自ModelSerializer
的序列化程序,然后在UpdateAPIView
中使用它。 DRF中的序列化程序类似于Django中的表单。
如果您不介意使用DRF添加另一个依赖项,这是一个非常棒的库,这可能会推动您自己的解决方案。
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = '__all__'
class MyModelDetail(generics.RetrieveUpdateAPIView):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer
答案 3 :(得分:1)
我解决了类似于Roman Semko的答案(可能不适用于ManyToMany字段):
更改表单的__init__
方法以更新数据:
import urllib
from django import forms
from django.http import QueryDict
from django.forms.models import model_to_dict
class MyModelForm (forms.ModelForm):
class Meta:
model = MyModel
def __init__ (self, *args, **kwargs):
super(MyModelForm, self).__init__(*args, **kwargs)
# if form has being submitted and
# model instance exists, then get data
if self.is_bound and self.instance.pk:
# get current model values
modeldict = model_to_dict( instance )
modeldict.update( self.data.dict() )
# add instance values to data
urlencoded = urllib.urlencode( modeldict )
self.data = QueryDict( urlencoded )
答案 4 :(得分:1)
我知道我参加聚会很晚,但是您能为表格创建一个工厂函数吗?
def model_form_factory(cls, data, *args, **kwargs):
""" Create a form to validate just the fields passed in the data dictionary.
e.g. form = form_factory(MyModel, request.POST, ...)
"""
data = data.copy()
data.pop('csrfmiddlewaretoken', None)
class PartialForm(forms.ModelForm):
class Meta:
model = cls
fields = data.keys()
return PartialForm(data, *args, **kwargs)
答案 5 :(得分:0)
好吧,您必须创建方法以在请求中缺少数据的情况下初始化表单,例如:
class MyModel(Model):
... your model ...
def initial(self,data):
fields = [list of possible fields here]
for field in fields:
if data.get(field) is None:
data[field] = getattr(self,field)
return data
然后,以如下形式传递此数据:
form = MyForm(instance.initial(request.POST.copy()),instance=instance)
对于JSON:
from json import loads
form = MyForm(instance.initial(loads(request.body)),instance=instance)
答案 6 :(得分:0)
我很惊讶没有人提到modelform_factory
。
通过创建特定版本的表单类,定义您感兴趣的字段子集,可以轻松实现部分表单更新:
from django.forms import modelform_factory
...
foo = get_object_or_404(Foo, slug=foo_slug)
if request.method == "POST":
Form = modelform_factory(
Foo, form=FooForm, fields=("my_first_field", "my_second_field",)
)
form = Form(request.POST, instance=foo)
if form.is_valid():
foo = form.save()
return redirect(foo)
else:
form = FooForm(instance=foo)