【问题标题】:Limit choices and validate django's foreign key to related objects (also in REST)限制选择并验证 django 对相关对象的外键(也在 REST 中)
【发布时间】:2016-10-01 06:52:47
【问题描述】:

我的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 框架,所以当添加或编辑 Todo 时,我必须检查 Category

所以,我还需要序列化程序中的函数validate 来限制模型正确(因为它不调用模型的cleanfull_clean 方法并且不检查limit_choices_to

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

或者,如果无法以简单的方式实现它,我正在寻找有关如何以最轻松的方式编写代码的建议。

【问题讨论】:

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


    【解决方案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
        ...
    

    为了让ManyToManyFieldInlineModelAdmin 中正确显示(例如TabularInline),这里有更多肮脏的技巧(可以做得更好吗?)

    您必须从对象中保存查询字段值,然后在字段中手动设置查询集。我的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', ...)
    

    不确定它是否真的按计划在所有情况下都有效。我会继续这个小调查。

    问题仍然存在。能不能简单点?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-18
      • 2018-11-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-10
      • 1970-01-01
      相关资源
      最近更新 更多