Feb 22, 2019 · Updated: Jul 12, 2021 · by Tim Kamanin
I'm building a backend for a scraping tool that has two simple models Website
and Page
, where Website
can have many pages.
In a simplified form it looks like:
class Website(models.Model):
url = models.URLField(unique=True)
class Page(models.Model):
website = models.ForeignKey(
"Website", on_delete=models.CASCADE, related_name="pages"
)
url = models.URLField(max_length=2083)
title = models.CharField(max_length=255)
content = models.TextField()
And I have an admin panel set in admin.py
like this:
from django.contrib.admin import ModelAdmin
class WebsiteAdmin(AdminViews):
actions = [scrape_website]
list_display = ["url"]
search_fields = ["url"]
Now what I want to do is to add a filter to the right-hand sidebar. It will allow me to filter websites by a scraped/not scraped status. And I take that "scraped" means a website has at least one page in the DB and "not scraped" means websites without any pages saved in the database.
And this is where we need to create a custom filter for Django admin. Like many things with Django, it's ridiculously simple, follow my hands:
from django.contrib.admin import ModelAdmin, SimpleListFilter
class ScrapeStatusFilter(SimpleListFilter):
title = "Scrape status" # a label for our filter
parameter_name = "pages" # you can put anything here
def lookups(self, request, model_admin):
# This is where you create filter options; we have two:
return [
("scraped", "Scraped"),
("not_scraped", "Not scraped"),
]
def queryset(self, request, queryset):
# This is where you process parameters selected by use via filter options:
if self.value() == "scraped":
# Get websites that have at least one page.
return queryset.distinct().filter(pages__isnull=False)
if self.value():
# Get websites that don't have any pages.
return queryset.distinct().filter(pages__isnull=True)
The last step is to add ScrapeStatusFilter
to WebsiteAdmin
:
class WebsiteAdmin(AdminViews):
actions = [scrape_website]
list_display = ["url"]
search_fields = ["url"]
list_filter = (ScrapeStatusFilter,)
And that should be it. You can now filter a list of websites via the custom crafted filter.
You're welcome!
Hey, if you've found this useful, please share the post to help other folks find it: