简体   繁体   中英

Django admin: how do I filter a ForeignKeyField widget based on the object's (not request.user's) data?

I have Chart and Module models (see code below). Each chart belongs to a module (via a ForeignKey ). A chart may have another chart as its parent (another ForeignKey ). What I would like in the admin is that the dropdown for parent on a particular chart only includes charts in the same module .

I'm looking for a Python solution, not AJAX. That means that on creating a new chart the dropdown will have to be empty (without a model instance there's no module selected) and that changing the module in the admin won't update the parent options to match until the model is saved. I'm ok with that.

There are plenty of similar-sounding questions (and answers) that turn out to filter the options according to the user ; ModelAdmin.formfield_for_foreignkey gets the request as an argument so you can use request.user , but it doesn't get given a model instance to play with. (I'm using Django 1.3.)

My models (highly simplified of course):

class Module(models.Model):
    pass

class Chart(models.Model):
    module = models.ForeignKey(Module)
    parent = models.ForeignKey(Chart, blank=True, null=True)

class ChartAdmin(admin.ModelAdmin):
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        # I would like to do this, but have no "instance":
        kwargs['queryset'] = Chart.objects.filter(module=instance.module)
        return super(ChartAdmin, self).formfield_for_foreignkey(self, db_field, request, **kwargs)

admin.site.register(Chart, ChartAdmin)

Just override the ModelForm being used:

class ChartAdminForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(ChartAdminForm, self).__init__(*args, **kwargs)
        if self.instance.module_id:
            self.fields['parent'].queryset = self.fields['parent'].queryset.filter(module=self.instance.module)

class ChartAdmin(admin.ModelAdmin):
    form = ChartAdminForm
    ...

From what i've seen in the source code, kwargs should contain only the widget (not helping !).

One possible hack, would be to override the get_form() modeladmin method, just to set request.current_object. Then, you could use request.current_object in formfield_callback:

def get_form(self, request, obj=None, **kwargs):
    request.current_object = obj
    return super(ChartAdmin, self).get_form(request, obj, **kwargs)

def formfield_for_foreignkey(self, db_field, request, **kwargs):
    instance = request.current_object

    kwargs['queryset'] = Chart.objects.filter(module=instance.module)
    return super(ChartAdmin, self).formfield_for_foreignkey(self, db_field, request, **kwargs)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM