2

假设我正在编写一个多博客应用程序,并且我希望每个作者为其文章使用唯一的标题(但每个用户都是唯一的,而不是全球唯一的):

class Article(models.Model):
    author = models.ForeignKey('auth.User')
    title = models.CharField(max_length=255)
    #[...]

    class Meta:
        unique_together = (('title', 'owner'),)

现在,我希望应用程序自动填充作者字段:

class ArticleAdmin(ModelAdmin):

    exclude = ('owner',)

    def save_model(self, request, obj, form, change):
        if not change:
            obj.owner = request.user
        obj.save()

实际上这不起作用:如果我尝试使用现有的作者-标题组合创建一个新文章,Django 将不会检查唯一性(因为作者被排除在表单之外)并且当它到达数据库时我会得到一个 IntegrityError。

我想在 Article 类中添加一个干净的方法:

def clean(self):
    if Article.objects.filter(title=self.title, owner=self.owner).exists():
        raise ValidationError(u"...")

但似乎在 ArticleAdmin.save_model()之前调用了Article.clean(),所以这不起作用。

这里已经提出了这个问题的几个变体,但似乎没有一个解决方案对我有用:

  • 我不能使用 Form.clean() 或其他没有可用请求的表单方法,因为我需要 request.user。
  • 出于同样的原因,模型级别的验证是不可能的。
  • 一些答案是指基于类的视图或自定义视图,但我想留在 Django 管理员的上下文中。

有什么想法可以在不重写一半管理应用程序的情况下做到这一点吗?

4

1 回答 1

0

实际上,您正在寻找一种request在 ModelAdmin 中引入自定义表单的方法:

from django.core.exceptions import ValidationError

def make_add_form(request, base_form):
    class ArticleForm(base_form):
        def clean(self):
            if Article.objects.filter(title=self.cleaned_data['title'], owner=request.user).exists():
                raise ValidationError(u"...")
            return self.cleaned_data

        def save(self, commit=False):
            self.instance.owner = request.user
            return super(ArticleForm, self).save(commit=commit)

    return ArticleForm


class ArticleAdmin(admin.ModelAdmin):
    exclude = ('owner',)

    def get_form(self, request, obj=None, **kwargs):
        if obj is None: # add
            kwargs['form'] = make_add_form(request, self.form)
        return super(ArticleAdmin, self).get_form(request, obj, **kwargs)
于 2012-05-03T16:19:12.647 回答