Django 1.0: Filtering object list and ForeignKey (ModelChoiceField) in Admin Site (contrib.admin)
I’m currently building a custom multi-blogging application in Django, and one of the challenges I ran into when developing the admin section was how to restrict users to seeing and editing only their own data.
Imagine the following (simplified) models:
class Blog(models.Model):
owner = models.OneToOneField(User, primary_key=True)
title = models.CharField(max_length=32)
class Entry(models.Model):
blog = models.ForeignKey(Blog)
slug = models.SlugField(max_length=255, db_index=True)
headline = models.CharField(max_length=255)
content = models.TextField()
If I want to show the user only their own entries in the admin object list, I can filter the list in the ModelAdmin.queryset method — simple as pie:
class EntryAdmin(admin.ModelAdmin):
def queryset(self, request):
qs = super(EntryAdmin,self).queryset(request)
return qs.filter(blog__owner=request.user)
Now, say I want to also limit what blogs the user can assign the entry to. There’s a useful little ModelAdmin method called formfield_for_dbfield, but it doesn’t receive the request as an argument. What to do?
Well, the ModelAdmin.get_form method does receive the request as an argument, and it gets called before formfield_for_dbfield. So let’s just put the request object somewhere that we can get to it later:
class EntryAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None):
self.request = request
f = super(EntryAdmin,self).get_form(request, obj)
return f
Then when we call formfield_for_dbfield we have easy access to the request:
class EntryAdmin(admin.ModelAdmin):
def formfield_for_dbfield(self, dbfield, **kwargs):
if dbfield.name == 'blog':
kwargs['queryset'] = Blog.objects.filter(owner=self.request.user)
return super(EntryAdmin, self).formfield_for_dbfield(dbfield, **kwargs)
Not the world’s most elegant solution, but simple and effective. And there’s plenty more you can do with these techniques. Gotta love Django!
COMMENT: by Adam K., August 10th, 2009
You have request in keywords argument inside formfield_for_dbfield. So you may:
request = kwargs['request']
COMMENT: by Noemi Millman, August 10th, 2009
Is that the case in 1.0 as well as 1.1? I couldn’t for the life of me seem to get the request from the arguments to formfield_for_dbfield when building my last app (on 1.0). Either way, definitely good to know.