Filtering by Vocabulary-Specific Keywords#
The ProjectFilter class supports creating multiple keyword filters, each tied to a specific vocabulary. This allows users to filter projects by different types of controlled keywords (e.g., science keywords, platforms, instruments) rather than having a single generic “Keywords” filter.
Autocomplete for Large Vocabularies#
For extremely large vocabularies (e.g., GCMD Science Keywords with thousands of concepts), FairDM uses django-autocomplete-light to efficiently load concepts on demand rather than loading entire querysets into the browser.
Concept Autocomplete View#
The ConceptAutocomplete view at /autocomplete/concept/ provides filtered concept lookups:
# URL: /autocomplete/concept/?vocabulary=gcmd-science-keywords&q=ocean
# Returns: Concepts from the specified vocabulary matching the search term
Features:
Filters by vocabulary name via URL parameter or forwarded field
Searches concept labels and names
Only authenticated users can access
Returns results ordered by label
Usage in custom widgets:
from dal import autocomplete
from django import forms
field = forms.ModelMultipleChoiceField(
queryset=Concept.objects.all(),
widget=autocomplete.ModelSelect2Multiple(
url='concept-autocomplete?vocabulary=gcmd-science-keywords',
attrs={
'data-placeholder': 'Select keywords...',
'data-minimum-input-length': 2,
}
)
)
Keyword Form Configuration#
How It Works#
The ProjectFilter base class has a KEYWORD_VOCABULARIES configuration attribute that accepts a list of vocabulary specifications. For each vocabulary specified, the filter automatically creates a separate filter field that:
Only shows concepts from that specific vocabulary
Only shows concepts that are actually used by projects
Filters the
keywordsrelationship on the Project model
Basic Usage#
To add vocabulary-specific filters, create a custom filter class that extends ProjectFilter and specify the vocabularies you want:
from django.utils.translation import gettext_lazy as _
from fairdm.core.project.filters import ProjectFilter
class MyProjectFilter(ProjectFilter):
"""Custom project filter with vocabulary-specific keyword filters."""
KEYWORD_VOCABULARIES = [
("gcmd-science-keywords", _("Science Keywords")),
("gcmd-platforms", _("Platforms")),
("gcmd-instruments", _("Instruments")),
]
This will automatically create three new filter fields:
keywords_gcmd_science_keywords- labeled “Science Keywords”keywords_gcmd_platforms- labeled “Platforms”keywords_gcmd_instruments- labeled “Instruments”
How Filter Names Are Generated#
The filter field name is generated by:
Prefixing with
keywords_Converting the vocabulary name to a valid Python identifier (replacing
-with_)
For example:
"gcmd-science-keywords"→keywords_gcmd_science_keywords"my-custom-vocab"→keywords_my_custom_vocab
Finding Vocabulary Names#
Vocabulary names in the database correspond to the name field on vocabulary objects. To find available vocabularies:
from research_vocabs.models import Vocabulary
# List all vocabularies
for vocab in Vocabulary.objects.all():
print(f"{vocab.name}: {vocab.title}")
Common GCMD vocabularies include:
gcmd-science-keywordsgcmd-platformsgcmd-instrumentsgcmd-locationsgcmd-projects
Widget Customization#
By default, vocabulary filters use CheckboxSelectMultiple widgets for multi-select functionality. The filters use OR logic, meaning projects matching ANY selected keyword will be returned.
If you need different widget behavior, you can override the filter creation in __init__:
class MyProjectFilter(ProjectFilter):
KEYWORD_VOCABULARIES = [
("gcmd-science-keywords", _("Science Keywords")),
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Customize the widget for a specific vocabulary filter
self.filters["keywords_gcmd_science_keywords"].widget = forms.SelectMultiple(
attrs={"class": "form-select", "size": "10"}
)
Empty State Handling#
The filters automatically exclude concepts that aren’t used by any projects. This means if no projects use concepts from a vocabulary, that filter will have an empty queryset and won’t show any options.
Complete Example#
# myapp/filters.py
from django.utils.translation import gettext_lazy as _
from fairdm.core.project.filters import ProjectFilter
class GeoscienceProjectFilter(ProjectFilter):
"""Project filter configured for geoscience research portals."""
KEYWORD_VOCABULARIES = [
("gcmd-science-keywords", _("Science Keywords")),
("gcmd-platforms", _("Observation Platforms")),
("gcmd-instruments", _("Instruments")),
("gcmd-locations", _("Locations")),
]
Then use it in your view configuration or registry:
# myapp/config.py
from .filters import GeoscienceProjectFilter
PROJECT_FILTER = GeoscienceProjectFilter
Performance Considerations#
The filter queries are optimized to:
Only load concepts from specified vocabularies
Only load concepts actually used by projects (via
projects__isnull=False)Use
distinct()to avoid duplicate results
For large vocabularies, consider adding database indexes on the Concept model’s vocabulary_id field if not already present.