在admin中以内联形式限制select中的外键选项

mig*_*hal 69 django inline admin foreign-keys limit

该模型的逻辑是:

  • A Building有很多Rooms
  • A Room可能在另一个内部Room(例如,一个壁橱 - 在'self'上的ForeignKey)
  • A Room只能Room在同一建筑物内的另一个内部(这是棘手的部分)

这是我的代码:

#spaces/models.py
from django.db import models    

class Building(models.Model):
    name=models.CharField(max_length=32)
    def __unicode__(self):
        return self.name

class Room(models.Model):
    number=models.CharField(max_length=8)
    building=models.ForeignKey(Building)
    inside_room=models.ForeignKey('self',blank=True,null=True)
    def __unicode__(self):
        return self.number
Run Code Online (Sandbox Code Playgroud)

和:

#spaces/admin.py
from ex.spaces.models import Building, Room
from django.contrib import admin

class RoomAdmin(admin.ModelAdmin):
    pass

class RoomInline(admin.TabularInline):
    model = Room
    extra = 2

class BuildingAdmin(admin.ModelAdmin):
    inlines=[RoomInline]

admin.site.register(Building, BuildingAdmin)
admin.site.register(Room)
Run Code Online (Sandbox Code Playgroud)

内联将仅显示当前建筑物中的房间(这是我想要的).但问题是,对于inside_room下拉列表,它会显示Rooms表中的所有房间(包括其他建筑物中的房间).

在内联中rooms,我需要将inside_room选择限制rooms在当前building(当前正由主BuildingAdmin窗体更改的建筑记录)中.

我无法找到一种方法来使用limit_choices_to模型中的任何一个,也无法弄清楚如何正确地覆盖管理员的内联formset(我觉得我应该以某种方式创建自定义内联表单,传递building_id of自定义内联的主要形式,然后根据这个限制字段选择的查询集 - 但我只是无法绕过如何做到这一点).

也许这对于管理网站来说太复杂了,但它似乎通常是有用的东西......

nog*_*gus 97

使用的请求实例作为obj的临时容器.覆盖内联方法formfield_for_foreignkey以修改查询集.这至少在django 1.2.3上有效.

class RoomInline(admin.TabularInline):

    model = Room

    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):

        field = super(RoomInline, self).formfield_for_foreignkey(db_field, request, **kwargs)

        if db_field.name == 'inside_room':
            if request._obj_ is not None:
                field.queryset = field.queryset.filter(building__exact = request._obj_)  
            else:
                field.queryset = field.queryset.none()

        return field



class BuildingAdmin(admin.ModelAdmin):

    inlines = (RoomInline,)

    def get_form(self, request, obj=None, **kwargs):
        # just save obj reference for future processing in Inline
        request._obj_ = obj
        return super(BuildingAdmin, self).get_form(request, obj, **kwargs)
Run Code Online (Sandbox Code Playgroud)

  • 太感谢了!另一种方法是在调用super之前根据文档分配kwargs ['queryset']:https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.formfield_for_foreignkey (3认同)

小智 17

阅读完这篇文章并进行了大量实验后,我想我已经找到了一个相当确定的答案.由于这是一个不常用的设计模式,我为Django管理员编写了一个Mixin来使用它.

(动态地)限制ForeignKey字段的查询集现在就像子类化LimitedAdminMixin和定义get_filters(obj)返回相关过滤器的方法一样简单.或者,filters如果不需要动态过滤,则可以在管理员上设置属性.

用法示例:

class MyInline(LimitedAdminInlineMixin, admin.TabularInline):
    def get_filters(self, obj):
        return (('<field_name>', dict(<filters>)),)
Run Code Online (Sandbox Code Playgroud)

这里<field_name>是要过滤的FK字段的名称,<filters>是一个参数列表,正如您通常在查询集filter()方法中指定它们一样.


nik*_*cat 15

limit_choices_to ForeignKey的选项,允许,限制的对象可用的管理选择

  • 这没有帮助,因为在limit_choices_to中运行的查询没有引用"父类".即,如果模型A具有B的外键,也具有C的外键,并且C具有B的外键,并且我们希望确保A仅引用与A相同的引用相同B的C. ,查询需要知道A-> B,但它没有. (2认同)

ala*_*lav 8

您可以创建几个自定义类,然后将对父实例的引用传递给表单.

from django.forms.models import BaseInlineFormSet
from django.forms import ModelForm

class ParentInstInlineFormSet(BaseInlineFormSet):
    def _construct_forms(self):
        # instantiate all the forms and put them in self.forms
        self.forms = []
        for i in xrange(self.total_form_count()):
            self.forms.append(self._construct_form(i, parent_instance=self.instance))

    def _get_empty_form(self, **kwargs):
        return super(ParentInstInlineFormSet, self)._get_empty_form(parent_instance=self.instance)
    empty_form = property(_get_empty_form)


class ParentInlineModelForm(ModelForm):
    def __init__(self, *args, **kwargs):
        self.parent_instance = kwargs.pop('parent_instance', None)
        super(ParentInlineModelForm, self).__init__(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

在课堂上RoomInline只需添加:

class RoomInline(admin.TabularInline):
      formset = ParentInstInlineFormset
      form = RoomInlineForm #(or something)
Run Code Online (Sandbox Code Playgroud)

在您的表单中,您现在可以在init方法中访问self.parent_instance!parent_instance现在可用于过滤选择和诸如此类的东西

就像是:

class RoomInlineForm(ParentInlineModelForm):
    def __init__(self, *args, **kwargs):
        super(RoomInlineForm, self).__init__(*args, **kwargs)
        building = self.parent_instance
        #Filtering and stuff
Run Code Online (Sandbox Code Playgroud)


Dan*_*kin 8

@nogus 中的问题回答了 popup 中的 url 仍然错误 /?_to_field=id&_popup=1

允许用户在弹出窗口中选择错误的项目

为了最终让它工作,我不得不改变field.widget.rel.limit_choices_todict

class RoomInline(admin.TabularInline):
    model = Room

    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):

        field = super(RoomInline, self).formfield_for_foreignkey(
            db_field, request, **kwargs)

        if db_field.name == 'inside_room':
            building = request._obj_
            if building is not None:
                field.queryset = field.queryset.filter(
                    building__exact=building)
                # widget changed to filter by building
                field.widget.rel.limit_choices_to = {'building_id': building.id}
            else:
                field.queryset = field.queryset.none()

        return field

class BuildingAdmin(admin.ModelAdmin):

    inlines = (RoomInline,)

    def get_form(self, request, obj=None, **kwargs):
        # just save obj reference for future processing in Inline
        request._obj_ = obj
        return super(BuildingAdmin, self).get_form(request, obj, **kwargs)
Run Code Online (Sandbox Code Playgroud)


mig*_*hal 5

这个问题和答案非常相似,适用于常规管理表单

在内联内部 - 这就是它崩溃的地方......我无法获取主表单的数据以获取我在限制范围内所需的外键值(或内联记录之一以获取值) .

这是我的 admin.py。我想我正在寻找魔法来取代 ???? with--如果我插入一个硬编码值(比如 1),它可以正常工作并正确限制内联中的可用选择...

#spaces/admin.py
from demo.spaces.models import Building, Room
from django.contrib import admin
from django.forms import ModelForm


class RoomInlineForm(ModelForm):
  def __init__(self, *args, **kwargs):
    super(RoomInlineForm, self).__init__(*args, **kwargs)
    self.fields['inside_room'].queryset = Room.objects.filter(
                               building__exact=????)                       # <------

class RoomInline(admin.TabularInline):
  form = RoomInlineForm
  model=Room

class BuildingAdmin(admin.ModelAdmin):
  inlines=[RoomInline]

admin.site.register(Building, BuildingAdmin)
admin.site.register(Room)
Run Code Online (Sandbox Code Playgroud)


cet*_*eek 1

如果丹尼尔在编辑你的问题后没有回答 - 我认为我不会有太大帮助......:-)

我建议您尝试强制将一些逻辑融入到 django 管理中,这些逻辑最好作为您自己的视图、表单和模板组来实现。

我认为不可能将这种过滤应用于 InlineModelAdmin。