-
Notifications
You must be signed in to change notification settings - Fork 31
Adjust Post & Question admin project filters #4672
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,4 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from admin_auto_filters.filters import AutocompleteFilterFactory | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from admin_auto_filters.filters import AutocompleteFilterFactory, AutocompleteFilter | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from datetime import datetime, timedelta, timezone as dt_timezone | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from django import forms | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -15,6 +15,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from posts.models import Post | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from posts.tasks import run_post_generate_history_snapshot | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from projects.models import Project | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from questions.constants import UnsuccessfulResolutionType | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from questions.models import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AggregateForecast, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -480,6 +481,27 @@ def clean(self): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise forms.ValidationError("Invalid action selected.") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class DefaultOrSecondaryProjectFilter(AutocompleteFilter): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Autocomplete filter — `?default_or_secondary_project=<id>` matches | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| questions whose post has the project as default_project OR in the projects M2M.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| title = "Default or Secondary Project" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| field_name = "default_project" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rel_model = Post | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parameter_name = "default_or_secondary_project" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use_pk_exact = False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def queryset(self, request, queryset): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value = self.value() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not value: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return queryset | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| project = Project.objects.get(pk=int(value)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except (Project.DoesNotExist, ValueError): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return queryset.none() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return queryset.filter(post__in=Post.objects.filter_projects(project)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+484
to
+502
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: The autocomplete widget will display Posts instead of Projects because Additionally, 🐛 Proposed fix class DefaultOrSecondaryProjectFilter(AutocompleteFilter):
"""Autocomplete filter — `?default_or_secondary_project=<id>` matches
questions whose post has the project as default_project OR in the projects M2M."""
title = "Default or Secondary Project"
- field_name = "default_project"
- rel_model = Post
+ field_name = "post__default_project"
+ rel_model = Project
parameter_name = "default_or_secondary_project"
use_pk_exact = False📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @admin.register(Question) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class QuestionAdmin(CustomTranslationAdmin, DynamicArrayMixin): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| form = QuestionAdminForm | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -521,6 +543,7 @@ class QuestionAdmin(CustomTranslationAdmin, DynamicArrayMixin): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AutocompleteFilterFactory("Author", "post__author"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AutocompleteFilterFactory("Default Project", "post__default_project"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AutocompleteFilterFactory("Project", "post__projects"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DefaultOrSecondaryProjectFilter, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| autocomplete_fields = ["group"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: Metaculus/metaculus
Length of output: 126
🏁 Script executed:
Repository: Metaculus/metaculus
Length of output: 1141
🏁 Script executed:
Repository: Metaculus/metaculus
Length of output: 2364
🏁 Script executed:
Repository: Metaculus/metaculus
Length of output: 1440
🏁 Script executed:
Repository: Metaculus/metaculus
Length of output: 1205
🌐 Web query:
Django admin list_filter URL parameters unregistered filter behavior💡 Result:
In Django admin, when accessing the changelist view with a URL parameter that does not correspond to any registered list_filter (an "unregistered filter"), the behavior depends on whether the parameter represents a valid model lookup and passes ModelAdmin.lookup_allowed. If the parameter is a valid lookup (e.g., ?myfield=value where myfield exists on the model or through relations) but not whitelisted by list_filter or lookup_allowed, Django raises DisallowedModelAdminLookup (a SuspiciousOperation subclass). This typically results in a 400 Bad Request response in production (DEBUG=False) or a debug page in development. If the parameter does not represent a valid lookup (e.g., ?invalidparam=value), it is silently ignored. The ChangeList processes only recognized parameters from registered filters (via get_filters), and unrecognized keys remain in self.params but do not affect the queryset or trigger errors. The page renders normally with the full (unfiltered by that param) queryset. The ?e=1 parameter appears in URLs after a previous DisallowedModelAdminLookup redirect (handled in older Django versions or custom code), indicating a prior invalid lookup attempt. In modern Django (4.2+), invalid lookups raise exceptions rather than redirecting. To allow custom unregistered parameters without affecting the queryset, add a pass-through SimpleListFilter with matching parameter_name whose lookups accepts the value and queryset returns the unchanged queryset, or override changelist_view to pop the parameter from request.GET after making it mutable.
Citations:
🏁 Script executed:
Repository: Metaculus/metaculus
Length of output: 45
🏁 Script executed:
Repository: Metaculus/metaculus
Length of output: 45
🏁 Script executed:
Repository: Metaculus/metaculus
Length of output: 294
🏁 Script executed:
Repository: Metaculus/metaculus
Length of output: 94
🏁 Script executed:
Repository: Metaculus/metaculus
Length of output: 1249
🏁 Script executed:
Repository: Metaculus/metaculus
Length of output: 185
🏁 Script executed:
Repository: Metaculus/metaculus
Length of output: 1908
🏁 Script executed:
Repository: Metaculus/metaculus
Length of output: 45
🏁 Script executed:
Repository: Metaculus/metaculus
Length of output: 45
🏁 Script executed:
Repository: Metaculus/metaculus
Length of output: 314
The
id__inURL parameter will cause a DisallowedModelAdminLookup error.In Django 5.2+, using a valid lookup like
id__inthat isn't whitelisted inQuestionAdmin.list_filterraises a security exception (DisallowedModelAdminLookup), resulting in a 400 Bad Request response. The links for "Questions in Primary Leaderboard" and "Questions NOT in Primary Leaderboard" will error instead of filtering.Add a custom filter to
QuestionAdmin.list_filterto whitelist theid__inlookup (e.g., usingSimpleListFilteror a custom filter class), or overridelookup_allowed()to explicitly allow this lookup.🤖 Prompt for AI Agents