5

模型:

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中的约束得到正确验证。

我不想在表单上显示所有者字段 - 它是当前用户,所以它应该由系统自动设置。但是无论我如何尝试这样做,验证都不起作用,或者还有其他错误。

在我尝试过的方法中(单独或组合):

  • 在相关的 ModelField 中创建隐藏字段
  • 在 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)唯一性验证:所有者/type_name 和 owner/type_slug。我遇到的典型错误是(a)所有者未被识别为用户(它被视为 PK),(b)不正确的验证(如缺少它或它错过了它是正在编辑的同一记录等事实) , (c) 所有者是必填字段。

作为记录 - 如果所有者是表单中的常规字段,则一切都按预期工作,但我不能允许用户设置所有者值。

有什么,希望优雅的解决方案吗?

谢谢!

4

2 回答 2

5

从表单中排除所有者字段,并将用户保存在表单的 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 对象。

于 2012-05-02T23:37:17.103 回答
0

正如我在评论中提到的,一种可能的解决方案本质上是与 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)
...

基本上,就是这样 - 验证唯一约束(和整个事情)就像一个魅力。

我喜欢这个解决方案吗?不,我认为它是一种 hack(具有讽刺意味的是,即使它与标准 Django 方法一起使用)。但它需要一些完全不必要的东西。这种方法的一个好处是它可以清楚地向当前用户传达她/他被设置为项目类型所有者的信息。但即使考虑到这一点,我宁愿显示一条消息(而不是一个字段),当前用户 X 将被设置为正在创建的项目类型的所有者所以,如果有人有更好的解决方案,请提交它以说明 Django 的全部功能和灵活性。

于 2012-05-03T15:37:45.263 回答