特定数据库

时间:2016-02-05 01:12:01

标签: python django django-models django-forms

我在settings.py('default'和'banco1')中定义了两个不同的数据库连接。

然后在某些视图中,我收到一个POST,其中包含一些我要针对我创建的ModelForm进行验证的数据:

 product_form = ProductForm(request.POST)
 if product_form.is_valid():

产品型号:

class Product(models.Model):

    name = models.CharField(max_length=125, blank=False, null=False)
    description = models.CharField(max_length=255, blank=True)
    abbreviation = models.CharField(max_length=15, blank=True)
    category = models.ForeignKey('inventory.Category',
        on_delete=models.SET_NULL, null=True, blank=True)
    metric = models.CharField(max_length=15, blank=True, null=True)

表格:

class ProductForm(ModelForm):
    class Meta:
        model = Product
        fields = ['name', 'description', 'abbreviation', 'category', 'metric']

观点:

 def create(request):
        if request.method != 'POST':
            return JsonResponse('Operação inválida', safe=False)

        product_form = ProductForm(request.POST)
        if product_form.is_valid():
            f = product_form.save(commit = False)
            f.save(using=request.session['database'])
            return HttpResponse("Novo product salvo com sucesso")
        else:
            return HttpResponse("Impossível salvar product. Verifique os campos")

产品模型总是保存在'banco1'数据库中,因此应该从'banco1'数据库中查询FK验证,但每当我尝试使用product_form.is_valid()进行验证时,Django会尝试查询'default'数据库并抛出验证错误,因为'default'数据库中无法满足FK约束。

如何设置模型表单以使其验证再次成为手动选择的数据库?

修改

建议我使用DB ROUTER模式来选择正确的数据库配置,这样可以正常工作。我的问题是,这个选择应该基于一些标准而不仅仅是模型。如何将视图中的某些变量传递给我的db路由器类中的db_for_read方法?

EDIT2

新的ProductForm类

class ProductForm(ModelForm):
    class Meta:
        model = Product
        fields = ['name', 'description', 'abbreviation', 'category', 'metric']

    def __init__(self,database):
        category = forms.ModelChoiceField(queryset=Category.objects.using(database).all())

尝试在我的视图中实例化表单,如下所示:

product_form = ProductForm(request.POST, request.session['database'])

这给了我一个错误:

  

init ()需要2个位置参数,但有3个被赋予

我知道我的方式无法覆盖__init__方法,但是我想在python中实现这一点。

编辑3

上次错误:

  

回溯:

     

文件“C:\ Users \ Pavarine \ Documents \ Visual Studio   2015年\项目\ GIMS \ GIMS \ ENV \ LIB \站点包\ Django的\核心\处理器\ base.py”   在get_response中     149. response = self.process_exception_by_middleware(e,request)

     

文件“C:\ Users \ Pavarine \ Documents \ Visual Studio   2015年\项目\ GIMS \ GIMS \ ENV \ LIB \站点包\ Django的\核心\处理器\ base.py”   在get_response中     147. response = wrapped_callback(request,* callback_args,** callback_kwargs)

     

文件“C:\ Users \ Pavarine \ Documents \ Visual Studio   在创建中2015 \ Projects \ gims \ gims \ gims \ apps \ inventory \ views.py“     247.如果product_form.is_valid():

     

文件“C:\ Users \ Pavarine \ Documents \ Visual Studio   2015年\项目\ GIMS \ GIMS \ ENV \ LIB \站点包\ Django的\形式\ forms.py”   在is_valid     161.返回self.is_bound而不是self.errors

     

文件“C:\ Users \ Pavarine \ Documents \ Visual Studio   2015年\项目\ GIMS \ GIMS \ ENV \ LIB \站点包\ Django的\形式\ forms.py”   在错误     153. self.full_clean()

     

文件“C:\ Users \ Pavarine \ Documents \ Visual Studio   2015年\项目\ GIMS \ GIMS \ ENV \ LIB \站点包\ Django的\形式\ forms.py”   在full_clean     362. self._clean_fields()

     

文件“C:\ Users \ Pavarine \ Documents \ Visual Studio   2015年\项目\ GIMS \ GIMS \ ENV \ LIB \站点包\ Django的\形式\ forms.py”   在_clean_fields     374. value = field.widget.value_from_datadict(self.data,self.files,   self.add_prefix(名))

     

文件“C:\ Users \ Pavarine \ Documents \ Visual Studio   2015年\项目\ GIMS \ GIMS \ ENV \ LIB \站点包\ Django的\形式\ widgets.py”   在value_from_datadict中     231. return data.get(name)

     

异常类型:/ inventory / product / create异常时的AttributeError   值:'tuple'对象没有属性'get'

1 个答案:

答案 0 :(得分:3)

要启用路由器,只需将其添加到DATABASE_ROUTERS中的setting.py,请参阅此处的详细信息:https://docs.djangoproject.com/en/1.9/topics/db/multi-db/#using-routers

每个路由器方法获取hints字典,该字典应包含表示正在使用的模型实例的instance键。根据您希望从视图中获取的信息,您可能会很好地使用实例属性中的信息。见这里:https://docs.djangoproject.com/en/1.9/topics/db/multi-db/#topics-db-multi-db-hints

即:

def db_for_read(self, model, **hints):
    model_instance = hints.get('instance')   
    if model_instance is not None:
        pass
        #perform actions with model_instance attributes
    return None

除此之外,我认为没有其他直接的方法可以将信息从活动视图传递到路由器,因为路由器应该使用模型并根据所使用的模型做出决策。

数据库路由器用于模型层中的自动数据库选择,但模型方法允许手动选择数据库。请参阅此处的示例:https://docs.djangoproject.com/en/1.9/topics/db/multi-db/#manually-selecting-a-database

所以另一个解决方案是重新定义表单类的方法,即save()(请参阅https://docs.djangoproject.com/en/1.9/topics/forms/modelforms/#the-save-method),并手动保存表单数据,指定要与using参数一起使用的数据库。即:

class ProductForm(ModelForm):
    class Meta:
        ...

    def save(self, commit=True):
        product_to_save=super(ProductForm, self).save(commit=False)
        #the above creates product_to_save instance, but doesn't saves it to DB
        product_to_save.save(using=='banco1')
        return product_to_save

在评论中,我建议将需要对其他数据库进行验证的字段进行子类化,但是可能更简单的方法......因为它是FK字段,它可能是接受{ModelChoiceField的{​​{1}}实例。 1}}构造函数中的参数,所以你可以提供它,即:

queryset

请参阅:https://docs.djangoproject.com/en/1.9/ref/forms/fields/#fields-which-handle-relationships

回答EDIT2

重新定义构造函数的方式class ProductForm(ModelForm): category_field = forms.ModelMultipleChoiceField(queryset=Category.objects.using('banco1')) ... 参数获取databaserequest.POST未映射到任何内容,这就是您收到错误的原因。

您应该考虑其他参数并调用超类构造函数,否则您将破坏MRO,所以这样的事情应该可以完成这项任务:

request.session['database']

然后照常使用命名参数:

class ProductForm(ModelForm):
    ...

    def __init__(self, *args, db_to_use='default', **kwargs):
        super(ProductForm, self).__init__(*args, **kwargs)
        #category = forms.ModelChoiceField(queryset=Category.objects.using(db_to_use).all())
        #the below line suggested as improvement by Alasdair and confirmed as working by Pavarine, so I updated the answer here
        self.fields['category'].queryset = Category.objects.using(database).all()