zoukankan      html  css  js  c++  java
  • 带有多对多的modleform表单保存到另一个数据库遇到的问题

    带有多对多的modleform表单保存到另一个数据库遇到的问题

    环境介绍

    django版本1.11,数据库做了主从同步,读写分离。

    # 涉及到读写django在save时会using不同数据库配置
    class Router:
        def db_for_write(self, model, **kwargs):
            return 'google'
    
        def db_for_read(self, model, **kwargs):
            return 'default'
    

    问题介绍

    modelform是Model类的复用体现,从modleform生成的表单字段,是读的过程,生成的对象保存了一些数据库来源信息,带有数据的form对象有个方法,可以直接save(),不用获取出各个字段值再保存到modle的对象,再用modle的对象去save,但是如果写入另一个数据库会报错,代码片段如下:

    class Customer(models.Model):
        # ...
    	class_list = models.ManyToManyField('ClassList',)
    
    class CustomerForm(forms.ModelForm):
        class Meta:
            model = models.Customer
            fields = "__all__"
    
    if request.method == 'POST':
        form_obj = CustomerForm(data=request.POST, instance=customer)
        if form_obj.is_valid():
            customer_form=form_obj.save()
            next = request.GET.get('next')
            if next:
                return redirect(next)
            return redirect(reverse('customer_list'))
    

    报错如下

    ValueError: Cannot add "<ClassList: Linux3>": instance is on database "google", value is on database "default"
    

    普通字段没关系,如果是外键或多对多关系,其中包含了一个关系管理对象,关系管理对象中保存的对象来源于读取的数据库。

    解决方法

    queryset,其中包含的是modle对象,将其转换为pk值,再去保存就会脱开原数据库。

    if request.method == 'POST':
        form_obj = CustomerForm(data=request.POST, instance=customer)
        if form_obj.is_valid():
            val = [i.pk for i in form_obj.cleaned_data['class_list'].all()]
            form_obj.cleaned_data['class_list'] = val
            form_obj.save()
            next = request.GET.get('next')
            if next:
                return redirect(next)
            return redirect(reverse('customer_list'))
    

    可以先把form对象转换为model对象,再处理多对多关系,同样是要将queryset的对象转换成pk列表,更加麻烦,做法参考后面知识点。

    附:官方文档save()方法解释

    每个 ModelForm 还有一个 save() 方法。此方法根据绑定到表单的数据创建并保存数据库对象。 ModelForm 的子类可接受一个现有的模型实例作为关键字参数 instance ;如果提供了,则 save() 会更新这个实例。如果没有,则 save() 会创建一个对应模型的新实例。

    >>> from myapp.models import Article
    >>> from myapp.forms import ArticleForm
    
    # Create a form instance from POST data.
    >>> f = ArticleForm(request.POST)
    
    # Save a new Article object from the form's data.
    >>> new_article = f.save()
    
    # Create a form to edit an existing Article, but use
    # POST data to populate the form.
    >>> a = Article.objects.get(pk=1)
    #
    >>> f = ArticleForm(request.POST, instance=a)
    >>> f.save()
    

    请注意,如果表单尚未验证,调用 save() 将通过检查 form.errors 来实现验证。如果表单验证不过,则会引发 ValueError —— 比如,如果 form.errors 返回 True

    save() 方法接受一个可选参数 commit ,它的值是 True 或者 False 。如果调用 save()的时候使用 commit=False ,那么它会返回一个尚未保存到数据库的对象。在这种情况下,需要您自己在生成的模型实例上调用 save() 。如果要在保存对象之前对对象执行自定义操作,或者要使用其中一个专用的 model save options,这很有用。 commit 的值默认为 True

    另一个使用 commit=False 的作用,您可以在模型与另一个模型有多对多关系的时候看到。如果您的模型具有多对多关系,并且在保存表单时指定了 commit=False ,Django无法立即保存多对多关系的表单数据。这是因为实例的多对多数据只有实例在数据库中存在时才能保存。

    要解决这个问题,Django会在您每次使用 commit=False 保存表单时,向 ModelForm 子类添加一个 save_m2m() 方法。在您手动保存表单生成的实例后,可以调用 save_m2m()来保存多对多的表单数据。例如:

    # 用POST数据创建一个表单实例
    >>> f = AuthorForm(request.POST)
    
    # 返回一个尚未保存的实例对象
    >>> new_author = f.save(commit=False)
    
    # 修改一些数据
    >>> new_author.some_field = 'some_value'
    
    # 保存这个新的实例
    >>> new_author.save()
    
    # 调用save_m2m(),保存具有 多对多关系 的数据
    >>> f.save_m2m()
    

    只有在您使用 save(commit=False) 的时候才需要调用 save_m2m() 。当您在表单上使用普通的 save() 时,无需调用其他方法,所有数据(包括多对多数据)都会被保存。例如:

    # 用POST数据创建表单实例
    >>> a = Author()
    >>> f = AuthorForm(request.POST, instance=a)
    
    # 创建并保存这个新实例,不需要在做其他事
    >>> new_author = f.save()
    

    除了 save()save_m2m() 方法之外,ModelForm 与普通的表单工作方式一样。例如,用 is_valid() 方法来检查合法性,用 is_multipart() 方法来确定表单是否需要multipart文件上传(之后是否必须将 request.FILES 传递给表单),等等。更多相关信息,请参阅 Binding uploaded files to a form

    【官方链接】

  • 相关阅读:
    写最简单的jsp判断今天是这个星期的第几天
    用jsp写出记忆曲线的表格(用学习新概念英语做例子)
    使用Eclipse建立web工程
    java最最基础知识(入门必备)
    如何配置eclipse里面的tomcat
    简单的eclipse配置
    简单的jdk配置
    python画图(标记、marker、设置标记大小、marker符号大全)(图文详细入门教程五)
    python画图(线条颜色、大小、类型:点、虚线等)(图文详细入门教程四)
    python画图(添加图例、网格)(图文详细入门教程三)
  • 原文地址:https://www.cnblogs.com/yinhaiping/p/13681522.html
Copyright © 2011-2022 走看看