特定数据库中的 Django 模型形式 is_valid()
Django model form is_valid() in specific database
我在 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")
Product 模型应该始终保存在 'banco1' 数据库中,因此应该从 'banco1' 数据库中查询 FK 验证,但每当我尝试使用 product_form.is_valid()
Django 尝试查询 'default' 数据库并抛出验证错误,因为在 'default' 数据库中无法满足 FK 约束。
我如何设置模型表单以再次手动验证 selected 数据库?
编辑
有人建议我使用 DB ROUTER 模式来 select 正确的数据库配置,效果很好。我的问题是这个 selection 应该基于一些标准而不仅仅是模型。如何将一些变量从视图传递到我的数据库路由器 class 中的 db_for_read
方法?
EDIT2
新建产品表单Class
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() takes 2 positional arguments but 3 were given
我知道我重写 __init__
方法的方式无效,但我不想在 python 中意识到这一点。
编辑 3
最近的错误:
Traceback:
File "C:\Users\Pavarine\Documents\Visual Studio
2015\Projects\gims\gims\env\lib\site-packages\django\core\handlers\base.py"
in get_response
149. response = self.process_exception_by_middleware(e, request)
File "C:\Users\Pavarine\Documents\Visual Studio
2015\Projects\gims\gims\env\lib\site-packages\django\core\handlers\base.py"
in get_response
147. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\Pavarine\Documents\Visual Studio
2015\Projects\gims\gims\gims\apps\inventory\views.py" in create
247. if product_form.is_valid():
File "C:\Users\Pavarine\Documents\Visual Studio
2015\Projects\gims\gims\env\lib\site-packages\django\forms\forms.py"
in is_valid
161. return self.is_bound and not self.errors
File "C:\Users\Pavarine\Documents\Visual Studio
2015\Projects\gims\gims\env\lib\site-packages\django\forms\forms.py"
in errors
153. self.full_clean()
File "C:\Users\Pavarine\Documents\Visual Studio
2015\Projects\gims\gims\env\lib\site-packages\django\forms\forms.py"
in full_clean
362. self._clean_fields()
File "C:\Users\Pavarine\Documents\Visual Studio
2015\Projects\gims\gims\env\lib\site-packages\django\forms\forms.py"
in _clean_fields
374. value = field.widget.value_from_datadict(self.data, self.files,
self.add_prefix(name))
File "C:\Users\Pavarine\Documents\Visual Studio
2015\Projects\gims\gims\env\lib\site-packages\django\forms\widgets.py"
in value_from_datadict
231. return data.get(name)
Exception Type: AttributeError at /inventory/product/create Exception
Value: 'tuple' object has no attribute 'get'
要启用路由器,只需将其添加到您的 setting.py
中的 DATABASE_ROUTERS
,请在此处查看详细信息: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
除此之外,我认为没有其他直接的方法可以将信息从活动视图传递到路由器,因为路由器应该使用模型并根据所使用的模型做出决策。
DB 路由器用于模型层中的自动 DB selection,但模型方法允许手动 select DB。请参阅此处的示例:https://docs.djangoproject.com/en/1.9/topics/db/multi-db/#manually-selecting-a-database
所以另一个解决方案是重新定义表单的方法 class,即 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
在评论中我建议子 class 需要针对其他数据库验证的字段,但可能还有更简单的方法...因为它是 FK 字段,它可能是 [=21 的实例=] 它在构造函数中接受 queryset
参数,所以你可以提供它,即:
class ProductForm(ModelForm):
category_field = forms.ModelMultipleChoiceField(queryset=Category.objects.using('banco1'))
...
参见:https://docs.djangoproject.com/en/1.9/ref/forms/fields/#fields-which-handle-relationships
对 EDIT2 的回答
您重新定义构造函数的方式使您的 database
参数得到 request.POST
并且 request.session['database']
未映射到任何内容,这就是您收到错误的原因。
你应该考虑其他参数并调用 superclass 构造函数,否则你会破坏 MRO,所以像这样的东西应该可以完成工作:
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()
然后像往常一样,但使用命名参数:
product_form = ProductForm(request.POST, db_to_use=request.session['database'])
我在 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")
Product 模型应该始终保存在 'banco1' 数据库中,因此应该从 'banco1' 数据库中查询 FK 验证,但每当我尝试使用 product_form.is_valid()
Django 尝试查询 'default' 数据库并抛出验证错误,因为在 'default' 数据库中无法满足 FK 约束。
我如何设置模型表单以再次手动验证 selected 数据库?
编辑
有人建议我使用 DB ROUTER 模式来 select 正确的数据库配置,效果很好。我的问题是这个 selection 应该基于一些标准而不仅仅是模型。如何将一些变量从视图传递到我的数据库路由器 class 中的 db_for_read
方法?
EDIT2
新建产品表单Class
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() takes 2 positional arguments but 3 were given
我知道我重写 __init__
方法的方式无效,但我不想在 python 中意识到这一点。
编辑 3
最近的错误:
Traceback:
File "C:\Users\Pavarine\Documents\Visual Studio 2015\Projects\gims\gims\env\lib\site-packages\django\core\handlers\base.py" in get_response 149. response = self.process_exception_by_middleware(e, request)
File "C:\Users\Pavarine\Documents\Visual Studio 2015\Projects\gims\gims\env\lib\site-packages\django\core\handlers\base.py" in get_response 147. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\Pavarine\Documents\Visual Studio 2015\Projects\gims\gims\gims\apps\inventory\views.py" in create 247. if product_form.is_valid():
File "C:\Users\Pavarine\Documents\Visual Studio 2015\Projects\gims\gims\env\lib\site-packages\django\forms\forms.py" in is_valid 161. return self.is_bound and not self.errors
File "C:\Users\Pavarine\Documents\Visual Studio 2015\Projects\gims\gims\env\lib\site-packages\django\forms\forms.py" in errors 153. self.full_clean()
File "C:\Users\Pavarine\Documents\Visual Studio 2015\Projects\gims\gims\env\lib\site-packages\django\forms\forms.py" in full_clean 362. self._clean_fields()
File "C:\Users\Pavarine\Documents\Visual Studio 2015\Projects\gims\gims\env\lib\site-packages\django\forms\forms.py" in _clean_fields 374. value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
File "C:\Users\Pavarine\Documents\Visual Studio 2015\Projects\gims\gims\env\lib\site-packages\django\forms\widgets.py" in value_from_datadict 231. return data.get(name)
Exception Type: AttributeError at /inventory/product/create Exception Value: 'tuple' object has no attribute 'get'
要启用路由器,只需将其添加到您的 setting.py
中的 DATABASE_ROUTERS
,请在此处查看详细信息: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
除此之外,我认为没有其他直接的方法可以将信息从活动视图传递到路由器,因为路由器应该使用模型并根据所使用的模型做出决策。
DB 路由器用于模型层中的自动 DB selection,但模型方法允许手动 select DB。请参阅此处的示例:https://docs.djangoproject.com/en/1.9/topics/db/multi-db/#manually-selecting-a-database
所以另一个解决方案是重新定义表单的方法 class,即 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
在评论中我建议子 class 需要针对其他数据库验证的字段,但可能还有更简单的方法...因为它是 FK 字段,它可能是 [=21 的实例=] 它在构造函数中接受 queryset
参数,所以你可以提供它,即:
class ProductForm(ModelForm):
category_field = forms.ModelMultipleChoiceField(queryset=Category.objects.using('banco1'))
...
参见:https://docs.djangoproject.com/en/1.9/ref/forms/fields/#fields-which-handle-relationships
对 EDIT2 的回答
您重新定义构造函数的方式使您的 database
参数得到 request.POST
并且 request.session['database']
未映射到任何内容,这就是您收到错误的原因。
你应该考虑其他参数并调用 superclass 构造函数,否则你会破坏 MRO,所以像这样的东西应该可以完成工作:
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()
然后像往常一样,但使用命名参数:
product_form = ProductForm(request.POST, db_to_use=request.session['database'])