我想在不满足某个约束时阻止django模型中的保存并给出验证错误,以便django员工用户知道出了什么问题。
约束是使用count()
参数指定的中间表中的through
。
models.py :
class Goal(models.Model):
name = models.CharField(max_length=128)
class UserProfile(models.Model):
goals = models.ManyToManyField(Goal, through=UserProfileGoals, blank=True)
class UserProfileGoal(models.Model):
goal = models.ForeignKey(Goals)
user_profile = models.ForeignKey(UserProfile)
class UserGoalConstraint(models.Model):
user_profile = models.OneToOneField(UserProfile)
max_goals = models.PositiveIntegerField()
因此UserGoalConstraint.max_goals
为我提供了存储在UserProfile.goal
模型中的最大可定义UserProfileGoal
的数量(相同UserGoal
可以更频繁地存储到{UserProfile
1}})
我已阅读并尝试了几个帖子的解决方案,这些帖子正在使用ModelForm的clean()
,模型的clean()
和pre_save
信号事件,
但我遇到的实际问题是,我怎么知道它只是一个更新还是一个新的数据库条目,因为
class UserProfileGoal(models.Model):
goal = models.ForeignKey(Goals)
user_profile = models.ForeignKey(UserProfile)
def clean(self):
goal_counter = self.user_profile.goals.count() + 1
try:
qs = UserGoalConstraint.objects.get(user_profile=self.user_profile)
except UserGoalConstraint.DoesNotExist:
raise ObjectDoesNotExist('Goal Constraint does not exist')
if goal_counter > qs.max_goals:
raise ValidationError('There are more goals than allowed goals')
不起作用,因为clean()
也可以是更新,而+1给出了错误的结果,导致ValidationError。
我的客户端应使用django-admin界面直接通过内联向用户个人资料添加目标:
admin.py:
class UserProfileGoalInline(admin.TabularInline):
model=UserProfileGoal
class UserProfileAdmin(admin.ModelAdmin)
...
inlines = [UserProfileGoalInline, ]
因此,当他向用户个人资料添加许多目标时,他需要得到很好的信息。
也许我错过了一些关于如何解决这个问题的明显内容......? 我正在寻找一个工作和某种用户友好的解决方案(=在管理界面获得通知)。
[UPDATE]:
我尝试知道在self.pk is None
clean()
技巧检查是否创建了它
if self.pk is not None:
return # it is not a create
...
我认为这会解决这个问题...... 但是,在admin内联中,当staff用户同时添加多个目标时,clean()无法识别这些目标。调试输出显示添加了2个目标,目标计数器保持相同的数字,即使第二个条目应该还有一个并且应该给出验证错误
答案 0 :(得分:1)
感谢@zaidfazil的首发解决方案:
class UserProfileGoalForm(forms.ModelForm):
class Meta:
model = UserProfileGoal
...
def clean(self):
cleaned_data = super(UserProfileGoalForm, self).clean()
if self.instance.pk is not None:
return cleaned_data
user_profile = self.cleaned_data.get('user_profile')
goal_count = user_profile.goals.count()
goal_limit = UserGoalConstraint.objects.get(user_profile=user_profile).max_goals # removed try catch for get for easier reading
if goal_count >= goal_limit:
raise ValidationError('Maximum limit reached for goals')
return cleaned_data
但是,这不会处理UserProfile管理界面中的内联:如果您同时添加多个clean()
并按保存,则Goal
将无法正确处理。
所以我将UserProfileGoalForm
应用于内联并定义了max_num
:
class UserProfileGoalInline(admin.TabularInline):
model=UserProfileGoal
form = UserProfileGoalForm
def get_max_num(self, request, obj=None, **kwargs):
if obj is None:
return
goal_limit = UserGoalConstraint.objects.get(training_profile=obj).max_goals
return goal_limit # which will overwrite the inline's max_num attribute
现在,我的客户端最多只能添加max_goals
的{{1}}值,UserGoalConstraint
的可能管理表格也会处理约束:
UserProfileGoal
答案 1 :(得分:0)
您可以使用ModelForm
clean
方法
class GoalForm(forms.ModelForm):
class Meta:
model = Goal
.....
def clean(self):
cleaned_data = super(GoalForm, self).clean()
if self.instance.pk is not None:
return cleaned_data
goal_limit = self.user_profile.usergoalconstraint.max_goals
goal_count = self.user_profile.goals.count()
if goal_count >= goal_limit:
raise ValidationError("Maximum limit reached for goals")
return cleaned_data