Django使用ModelForm更新一个字段

时间:2011-11-21 18:07:01

标签: python django

如果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覆盖它们。

有人知道如何管理吗?我会手动进行所有检查并手动更新,但该模型有很长的字段列表......

7 个答案:

答案 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)