在Models.py中保存前对ManyToManyField进行验证
我有以下这些模型:
class Application(models.Model):
users = models.ManyToManyField(User, through='Permission')
folder = models.ForeignKey(Folder)
class Folder(models.Model):
company = models.ManyToManyField(Compnay)
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile')
company = models.ManyToManyField(Company)
我想做的是检查一下,应用程序的用户中是否有和这个应用程序(通过文件夹)属于同一公司的。如果有的话,这个应用实例就不应该被保存。
问题在于,ManyToManyFields(多对多字段)在“保存后”信号发出之前是不会更新的。
目前唯一的选择似乎是使用新的 m2m_changed 信号。但是我不太确定如何撤销已经发生的保存操作。
另一个选择是重写保存函数(在 models.py 中,因为我这里是在说管理员界面),但我不太清楚如何访问多对多字段的内容。
最后,我还读到了一些关于在 admin.py 中重写模型的保存函数的内容,但我仍然不知道如何访问多对多字段的内容。
我到处搜索这个问题,但没有找到任何对我有用的解决方案。
如果有什么不清楚的地方,请告诉我。
谢谢你的帮助!
Heleen
2 个回答
forms.py
class ApplicationForm(ModelForm):
class Meta:
model = Application
def clean(self):
cleaned_data = self.cleaned_data
users = cleaned_data['users']
folder = cleaned_data['folder']
if users.filter(profile__company__in=folder.company.all()).count() > 0:
raise forms.ValidationError('One of the users of this Application works in one of the Folder companies!')
return cleaned_data
admin.py
class ApplicationAdmin(ModelAdmin):
form = ApplicationForm
编辑: 把之前那个(错误的)模型验证示例换成了表单验证。
因为我没有收到Botondus的回复,所以我决定在Django用户Google小组里问一个新问题,最后得到了jaymz的回答。
我发现Botondus的方法是正确的,只是没有完全奏效。之所以在这种情况下不行,是因为我在想要进行验证的字段上使用了一个Through模型。根据我之前在一个问题中得到的反馈,我了解到首先是保存Application实例,然后再保存ManyToMany实例(我觉得这样理解是对的,但如果错了请纠正我)。所以我想,如果我在Through模型的ManyToMany字段上进行验证,这应该不会阻止Application实例被保存。但实际上,它确实会阻止这个操作。
所以,如果你的模型的管理界面里有一个ManyToMany字段,并且你想对这个字段进行验证,你需要在Through模型里指定clean函数,像这样:
admin.py
class PermissionInline(admin.TabularInline):
form = PermissionForm
model = Permission
extra = 3
forms.py
class PermissionForm(forms.ModelForm):
class Meta:
model = Permission
def clean(self):
cleaned_data = self.cleaned_data
user = cleaned_data['user']
role = cleaned_data['role']
if role.id != 1:
folder = cleaned_data['application'].folder
if len(filter(lambda x:x in user.profile.company.all(),folder.company.all())) > 0: # this is an intersection
raise forms.ValidationError("One of the users of this Application works for one of the Repository's organisations!")
return cleaned_data
如果验证出现错误,那么什么都不会被保存(既不会保存Application实例,也不会保存ManyToMany用户实例),你将有机会去纠正这个错误。