我有以下内容:
class AccountAdmin(models.Model):
account = models.ForeignKey(Account)
is_master = models.BooleanField()
name = models.CharField(max_length=255)
email = models.EmailField()
class Meta:
unique_together = (('Account', 'is_master'), ('Account', 'username'),)
如果我在同一个帐户上创建一个与另一个用户名相同的新AccountAdmin,而不是让我在模板中显示错误,则会因为IntegrityError而导致页面中断。我希望在我看来,我可以去:
if new_accountadmin_form.is_valid():
new_accountadmin_form.save()
我如何克服这个问题。是否有第二种is_valid()
类型的方法检查数据库是否违反了unique_together = (('Account', 'is_master'), ('Account', 'username'),)
部分?
我不想在我的视图中捕获IntegrityError。那个域逻辑与表示逻辑混合在一起。它违反DRY,因为如果我在2页上显示相同的表格,我将不得不重复相同的块。它也违反DRY,因为如果我有两种形式用于同一件事,我必须写相同的内容,但是:再次。
答案 0 :(得分:7)
有两种选择:
a)使用try块来保存模型并捕获IntegrityError并处理它。类似的东西:
try:
new_accountadmin_form.save()
except IntegrityError:
new_accountadmin_form._errors["account"] = ["some message"]
new_accountadmin_form._errors["is_master"] = ["some message"]
del new_accountadmin_form.cleaned_data["account"]
del new_accountadmin_form.cleaned_data["is_master"]
b)在表单的clean()方法中,检查一行是否存在,并使用适当的消息引发forms.ValidationError
。示例here。
所以,b)它是......这就是我referenced the documentation; all you need is there.
的原因但它会是这样的:
class YouForm(forms.Form):
# Everything as before.
...
def clean(self):
""" This is the form's clean method, not a particular field's clean method """
cleaned_data = self.cleaned_data
account = cleaned_data.get("account")
is_master = cleaned_data.get("is_master")
username = cleaned_data.get("username")
if AccountAdmin.objects.filter(account=account, is_master=is_master).count() > 0:
del cleaned_data["account"]
del cleaned_data["is_master"]
raise forms.ValidationError("Account and is_master combination already exists.")
if AccountAdmin.objects.filter(account=account, username=username).count() > 0:
del cleaned_data["account"]
del cleaned_data["username"]
raise forms.ValidationError("Account and username combination already exists.")
# Always return the full collection of cleaned data.
return cleaned_data
对于它的价值 - 我刚刚意识到你的unique_together上面引用了一个名为username的字段,该字段未在模型中表示。
在调用各个字段的所有clean方法之后调用上面的clean方法。
答案 1 :(得分:2)
以完全通用的方式。在模型中有以下两个辅助fns:
def getField(self,fieldName):
# return the actual field (not the db representation of the field)
try:
return self._meta.get_field_by_name(fieldName)[0]
except models.fields.FieldDoesNotExist:
return None
和
def getUniqueTogether(self):
# returns the set of fields (their names) that must be unique_together
# otherwise returns None
unique_together = self._meta.unique_together
for field_set in unique_together:
return field_set
return None
并且在表单中有以下fn:
def clean(self):
cleaned_data = self.cleaned_data
instance = self.instance
# work out which fields are unique_together
unique_filter = {}
unique_fields = instance.getUniqueTogether()
if unique_fields:
for unique_field in unique_fields:
field = instance.getField(unique_field)
if field.editable:
# this field shows up in the form,
# so get the value from the form
unique_filter[unique_field] = cleaned_data[unique_field]
else:
# this field is excluded from the form,
# so get the value from the model
unique_filter[unique_field] = getattr(instance,unique_field)
# try to find if any models already exist in the db;
# I find all models and then exlude those matching the current model.
existing_instances = type(instance).objects.filter(**unique_filter).exclude(pk=instance.pk)
if existing_instances:
# if we've gotten to this point,
# then there is a pre-existing model matching the unique filter
# so record the relevant errors
for unique_field in unique_fields:
self.errors[unique_field] = "This value must be unique."
答案 2 :(得分:1)
Model.Meta.unique_together创建一个仅限于数据库的约束,而ModelForm.is_valid()主要基于正确的类型。事件,如果它确实检查了约束,那么你会遇到一个竞争条件,它仍然会在save()调用中导致IntegrityError。
您可能希望捕获IntegrityError:
if new_accountadmin_form.is_valid():
try:
newaccountadmin_form.save()
except IntegrityError, error:
# here's your error handling code