首页 文章

如何限制Django raw_id_field的ForeignKey选择

提问于
浏览
11

如何限制使用raw_id_fields选项显示的Django 's admin when they'中显示的ForeignKey字段的选项?

当呈现为选择框时,设置该字段's queryset value with the choices to want. However, this queryset appears to be completely ignored when rendered using raw_id_fields. It generates a link to that ForeignKey'的模型很简单,允许您通过弹出窗口从该模型中选择任何记录 . 您仍然可以通过自定义URL来过滤这些值,但我找不到通过ModelAdmin执行此操作的方法 .

5 回答

  • 2

    我在Django 1.8 / Python 3.4项目中使用类似于FSp的方法:

    from django.contrib import admin
    from django.contrib.admin import widgets
    from django.contrib.admin.sites import site
    from django import forms
    
    class BlogRawIdWidget(widgets.ForeignKeyRawIdWidget):
        def url_parameters(self):
            res = super().url_parameters()
            res['type__exact'] = 'PROJ'
            return res
    
    class ProjectAdminForm(forms.ModelForm):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.fields['blog'].queryset = Blog.objects.filter(type='PROJ')
            self.fields['blog'].widget = BlogRawIdWidget(rel=Project._meta.get_field('blog').rel, admin_site=site)
    
        class Meta:
            # Django 1.8 convenience:
            fields = '__all__'
            model = Project
    
    class ProjectAdmin(admin.ModelAdmin):
        form = ProjectAdminForm
        raw_id_fields = ('blog',)
    

    在django.admin中只选择blog.type =='PROJ'作为外键Project.blog . 因为最终用户可能会并且将会选择任何东西,不幸的是 .

  • 7

    对于实际项目,我发现给定的解决方案(自定义 ModelAdmin 查询集)有点过于严格 .

    我的工作通常如下:

    • 在我的 ModelAdmin 中创建自定义过滤器(例如,子类化 admin.SimpleListFilter ,请参阅doc

    • 创建我的widget ForeignKeyRawIdWidget 的子类,如下所示:

    class CustomRawIdWidget(ForeignKeyRawIdWidget):
    
        def url_parameters(self):
            """
            activate one or more filters by default
            """
    
            res = super(CustomRawIdWidget, self).url_parameters()
    
            res["<filter_name>__exact"] = "<filter_value>"
    
            return res
    

    请注意,自定义小部件唯一做的是“预选”过滤器,而过滤器又负责“限制”查询集

    • 使用自定义小部件:
    class MyForm(forms.ModelForm):
    
        myfield = forms.ModelChoiceField(queryset=MyModel.objects.all(),
            ...
            widget=CustomRawIdWidget(
                 MyRelationModel._meta.get_field('myfield').rel,
                 admin.site))
    

    此方法的一个弱点是窗口小部件选择的过滤器不会阻止从该模型中选择其他实例 . 如果需要,我重写方法 ModelAdmin.save_model(...) (参见doc)以检查相关实例是否只是允许的实例 .

    我发现这种方法有点复杂,但比限制整个 ModelAdmin 的查询集要灵活得多 .

  • 3

    下面的方法适用于我,但它是一个影响每个需要使用Customer模型的管理员的查询集 . 但如果你有另一个管理员,例如需要不同查询集的发票,您可能想要使用模型代理进行一些实验 .

    模型

    class Customer(models.Model):
        name = models.CharField(max_length=100)
        is_active = models.BooleanField()
    
    class Order(models.Model):
        cust = models.ForeignKey(Customer)
    

    管理员

    class CustomerAdmin(admin.ModelAdmin):         
        def queryset(self, request):
            qs = super(CustomerAdmin, self).queryset(request)           
            return qs.filter(is_active=1)
    
    class OrderAdmin():     
        raw_id_fields = ('cust', )
    
  • 4

    如果您需要根据模型实例过滤raw_id list_view弹出窗口,可以使用以下示例:

    1. Write custom widget

    class RawIdWidget(widgets.ForeignKeyRawIdWidget):
    
        def url_parameters(self):
            res = super(RawIdWidget, self).url_parameters()
            object = self.attrs.get('object', None)
            if object:
                # Filter variants by product_id
                res['product_id'] = object.variant.product_id
            return res
    

    2. Pass instance on form init

    class ModelForm(forms.ModelForm):
    
        def __init__(self, *args, **kwargs):
            super(ModelForm, self).__init__(*args, **kwargs)
            obj = kwargs.get('instance', None)
            if obj and obj.pk is not None:
                self.fields['variant'].widget = RawIdWidget(
                    rel=obj._meta.get_field('variant').rel,
                    admin_site=admin.site,
                    # Pass the object to attrs
                    attrs={'object': obj}
                )
    
  • 5

    我创建了一个遗传解决方案来解决传递给弹出窗口的自定义参数的问题 . 您只需要在项目中复制此代码:

    from django.contrib.admin import widgets
    
    class GenericRawIdWidget(widgets.ForeignKeyRawIdWidget):
        url_params = []
    
        def __init__(self, rel, admin_site, attrs=None, \
            using=None, url_params=[]):
            super(GenericRawIdWidget, self).__init__(
                rel, admin_site, attrs=attrs, using=using)
            self.url_params = url_params
    
        def url_parameters(self):
            """
            activate one or more filters by default
            """
            res = super(GenericRawIdWidget, self).url_parameters()
            res.update(**self.url_params)
    
            return res
    

    然后,您可以像这样使用:

    field.widget = GenericRawIdWidget(YOURMODEL._meta.get_field('YOUR_RELATION').rel,
                admin.site, url_params={"<YOURMODEL>__id__exact":     object_id})
    

    我用这种方式使用它:

    class ANSRuleInline(admin.TabularInline):
        model = ANSRule 
        form = ANSRuleInlineForm
        extra = 1
        raw_id_fields = ('parent',)
    
        def __init__(self, *args, **kwargs):
            super (ANSRuleInline,self ).__init__(*args,**kwargs)
    
        def formfield_for_dbfield(self, db_field, **kwargs):
            formfield = super(ANSRuleInline, self).formfield_for_dbfield(db_field, **kwargs)
            request = kwargs.get("request", None)
            object_id = self.get_object(request)
    
            if db_field.name == 'parent':
                formfield.widget = GenericRawIdWidget(ANSRule._meta.get_field('parent').rel,
                    admin.site, url_params={"pathology__id__exact": object_id})
    
            return formfield
    
        def get_object(self, request):
            object_id = request.META['PATH_INFO'].strip('/').split('/')[-1]
            try:
                object_id = int(object_id)
            except ValueError:
                return None
            return object_id
    

    当我使用 GenericRawIdWidget 时,我将dict传递给url_params,它将在url上使用 .

相关问题