Jaap Joris Vens
Jaap Joris Vens

Reputation: 3560

How can I limit the choices in an InlineAdmin based on the current instance?

I have a database of articles that are reused across multiple Django sites. There is also some site-specific information for each Article, stored in the SiteArticle model. One bit of information is a list of site-specific tags for each SiteArticle. Here's the models.py:

class Article(models.Model):
    sites = models.ManyToManyField(Site, through='SiteArticle')

class SiteArticle(models.Model):
    site = models.ForeignKey(Site)
    article = models.ForeignKey(Entiteit)
    tags = models.ManyToManyField('Tag', blank=True)

class Tag(models.Model):
    name = models.CharField(max_length=255)
    site = models.ForeignKey(Site, related_name='tags')

I use an inline admin to edit and add the SiteArticle objects of each Article. Here's admin.py:

class InlineSiteArticle(admin.StackedInline):
    model = SiteArticle

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    inlines = [InlineSiteArticle]

When editing an article, I would like the inline SiteArticle forms to only display the tags of the relevant site. I tried overriding the formfield_for_manytomany() method, but here I don't have access to instance variable (which should be an instance of the current SiteArticle) that I need to filter the queryset:

def formfield_for_manytomany(self, db_field, request, **kwargs):
    if db_field.name == "tags":
        kwargs["queryset"] = instance.site.tags.all()
                             ^^^^^^^^
    return super(InlineSiteArticle, self).formfield_for_manytomany(db_field, request, **kwargs)

I already looked at this Stack Overflow answer which solves a very related problem. In my case, however, I do not need access to the "parent" instance, but simply to the instance of the current form's SiteArticle object. How can I solve this?

EDIT -- I already figured out that get_formset() does get an instance passed in. However, this instance is an Article, not the SiteArticle needed to filter the queryset.

Upvotes: 0

Views: 98

Answers (1)

Jaap Joris Vens
Jaap Joris Vens

Reputation: 3560

Here's the answer I figured out myself. Feel free to edit, comment, or provide a better solution!

I created a custom form for editing SiteArticles, which I passed to the ArticleAdmin using the form option of ModelAdmin. In the constructor of this form I performed the filtering of the queryset based on the current model instance, which is now available as self.instance.

class CustomSiteArticleForm(forms.ModelForm):
    class Meta:
        model = SiteArticle
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super(CustomSiteArticleForm, self).__init__(*args, **kwargs)
        if self.instance.pk:
            self.fields['tags'].queryset = self.instance.site.tags.all()
        else:
            self.fields['tags'].queryset = Tag.objects.none()

Upvotes: 1

Related Questions