[英]Django form validation with authenticated user as a field
模型:
class ProjectType(models.Model):
project_type_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=45, help_text='Type of project', verbose_name='Project Type')
slug = models.SlugField(max_length=45, blank=True)
description = models.CharField(max_length=400, help_text='Description of the main purpose of the project', verbose_name='Project Type Description')
default = models.BooleanField(default=False)
owner = models.ForeignKey(User)
class Meta:
...
unique_together = (('slug', 'owner'),('name', 'owner'))
我需要一个表单来创建/更新ProjectType。 请注意所有者字段 - 它应该是当前登录的用户。 问题是如何确保正确验证unique_together中的约束。
我不想在表单上显示所有者字段 - 它是当前用户,因此它应该由系统自动设置。 但无论我如何尝试这样做,验证都不起作用,或者还有其他错误。
我试过的方法(单独或组合):
在ProjectTypeForm中定义init (以各种方式),例如:
def __init__(self, *args, **kwargs): self.user = kwargs.pop('user', None) super(ProjectTypeForm, self).__init__(*args, **kwargs) self.fields['owner'].initial = self.user
在视图中设置值,如:
... if request.method == 'POST': project_type = ProjectType(owner=request.user) form = ProjectTypeForm(request.POST, instance=project_type, user = request.user.pk) # also tries w/o pk ...
以各种方式覆盖表单的clean()方法,沿着这些方向:
def clean(self): cleaned_data = super(ProjectTypeForm, self).clean() slug=cleaned_data.get('slug') owner = cleaned_data.get('owner') if slug: user = User.objects.get(pk=owner) ...
其中许多方法都基于stackoverflow.com上的各种答案。 但是,无论我尝试什么,我都找不到满足我需要的方法:(1)自动设置所有者字段和(2)验证唯一性:owner / type_name和owner / type_slug。 我遇到的典型错误是(a) 所有者未被识别为用户(被视为PK),(b)不正确的验证(如缺乏验证或错过了正在编辑的同一记录等事实) ,(c)所有者是必填字段。
对于记录 - 如果所有者是表单中的常规字段,一切都按预期工作,但我不能允许用户设置所有者值。
对此有什么希望优雅的解决方案吗?
谢谢!
从表单中排除所有者字段,并将用户保存在表单的init方法中 - 然后您可以使用它来验证表单,例如
class ProjectTypeForm(...):
...
def __init__(self, user, *args, **kwargs):
super(ProjectTypeForm, self).__init__(*args, **kwargs)
self.user = user
def clean(self):
user_projects = ProjectType.objects.filter(owner=self.user)
if user_projects.filter(slug=self.cleaned_data['slug']):
raise forms.ValidationError('...')
elif user_projects.filter(name=self.cleaned_data['name']):
raise forms.ValidationError('...')
else:
return self.cleaned_data
然后在您的视图中,在创建新的ProjectType时执行以下操作:
if request.method == 'POST':
form = ProjectTypeForm(request.user, request.POST)
if form.is_valid():
ptype = form.save(commit=False)
ptype.owner = request.user
ptype.save()
您不应该需要它来保存现有的ProjectType对象。
正如我在评论中提到的,一个可能的解决方案基本上是与Django表单一起使用并使用表单上的所有者字段。 所以,我所做的是以这种方式修改init :
def __init__(self, user, *args, **kwargs):
super(ProjectTypeForm, self).__init__(*args, **kwargs)
self.fields['owner'] = forms.ModelChoiceField(
label='Owner*',
queryset=User.objects.filter(username=user.username),
help_text="Project types are unique to logged-in users who are set as their owners.",
required=True,
empty_label=None)
基本上,它的作用仍然是使用ChoiceField,但将其设置为一个选项 - 当前用户。 另外,empty_label = None确保没有“空”选择。 效果是(因为用户名是唯一的)当前用户名显示为可见,并且是具有更多选择的其他下拉列表中的唯一选择。
在视图中我遵循这种方法:
...
if request.method == 'POST':
project_type = ProjectType()
form = ProjectTypeForm(request.user,request.POST, instance=project_type,)
if form.is_valid():
project_type.save()
return HttpResponseRedirect(reverse('project_types'))
else:
form = ProjectTypeForm(request.user)
...
基本上就是这样 - 独特约束(以及整个事物)的验证就像一个魅力。
我喜欢这个解决方案吗? 不,我认为这是一个黑客(具有讽刺意味的是,即使它符合标准的Django方法)。 但它需要一些完全没必要的东西。 这种方法的一个好处是它清楚地向当前用户传达他/她被设置为项目类型所有者。 但即使考虑到这一点,我宁愿显示一条消息(而不是字段), 当前用户X将被设置为正在创建的项目类型的所有者 。 因此,如果有人有更好的解决方案,请提交它以说明Django的全部功能和灵活性。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.