限制选择并验证django对相关对象的外键(也在REST中)

时间:2016-06-01 18:47:42

标签: python django django-admin django-rest-framework

我的models.py就像这样:

class Category(models.Model):
    user = models.ForeignKey(User)
    name = models.CharField(max_length=256, db_index=True)

class Todo(models.Model):
    user = models.ForeignKey(User)
    category = models.ForeignKey(Category)
    ...

我想将Category Todo的选择仅限于那些Todo.user = Category.user

的选项

我发现的每个解决方案都是为表单内的ModelForm或实现方法设置查询集。 (与limit_choices_to一样,不可能(?))

问题在于我不仅有一个具有此类限制问题的模型(例如Tag等)。

另外,我正在使用django REST框架,因此我必须在添加或编辑Category时检查Todo

因此,我还需要序列化程序中的函数validate来正确限制模型(因为它不会调用模型的cleanfull_clean方法,也不会检查limit_choices_to)< / p>

所以,我正在寻找一个简单的解决方案,它将适用于django Admin和REST框架。

或者,如果不可能以简单的方式实现它,我正在寻找如何以最无痛的方式编写代码的建议。

1 个答案:

答案 0 :(得分:1)

这是我迄今为止所发现的:

要在管理员中显示Foreignkey权限,您必须在ModelAdmin

中指定一个表单
class TodoAdminForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['category'].queryset = Category.objects.filter(user__pk=self.instance.user.pk)

@admin.register(Todo)
class TodoAdmin(admin.ModelAdmin):
    form = TodoAdminForm
    ...

要让ManyToManyField显示在InlineModelAdmin(例如TabularInline),这里会出现更多脏问题(可以做得更好吗?)

您必须从对象中保存您的quiring字段值,然后在字段中手动设置queryset。我的through模型有两个成员todotag

我想过滤tag字段(指向模型Tag):

class MembershipInline(admin.TabularInline):
    model = Todo.tags.through

    def get_formset(self, request, obj=None, **kwargs):
        request.saved_user_pk = obj.user.pk  # Not sure if it can be None
        return super().get_formset(request, obj, **kwargs)

    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
        if db_field.name == 'tag':
            kwargs['queryset'] = Tag.objects.filter(user__pk=request.saved_user_pk)
        return super().formfield_for_foreignkey(db_field, request, **kwargs)

最后,为了将元素仅限于Django REST框架中的相关元素,我必须实现自定义Field

class PrimaryKeyRelatedByUser(serializers.PrimaryKeyRelatedField):
    def get_queryset(self):
        return super().get_queryset().filter(user=self.context['request'].user)

并在我的序列化工具中使用它,如

class TodoSerializer(serializers.ModelSerializer):
    category = PrimaryKeyRelatedByUser(required=False, allow_null=True, queryset=Category.objects.all())
    tags = PrimaryKeyRelatedByUser(required=False, many=True, queryset=Tag.objects.all())

class Meta:
    model = Todo
    fields = ('id', 'category', 'tags', ...)

不确定它是否按计划在所有情况下实际运行。我将继续这个小调查。

问题仍然存在。可以简化吗?