在Models.py中保存前对ManyToManyField进行验证

2 投票
2 回答
1260 浏览
提问于 2025-04-16 00:02

我有以下这些模型:

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 个回答

0

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

编辑: 把之前那个(错误的)模型验证示例换成了表单验证。

1

因为我没有收到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用户实例),你将有机会去纠正这个错误。

撰写回答