mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-16 09:37:03 +02:00
Fix other fields
This commit is contained in:
parent
4515797680
commit
43a595081f
2 changed files with 75 additions and 8 deletions
|
@ -135,7 +135,7 @@ class AuditedAdmin(admin.ModelAdmin, AdminSortFields):
|
||||||
return self.form_field_order_helper(form_field, db_field)
|
return self.form_field_order_helper(form_field, db_field)
|
||||||
|
|
||||||
|
|
||||||
class ListHeaderAdmin(AuditedAdmin):
|
class ListHeaderAdmin(AuditedAdmin, OrderableFieldsMixin):
|
||||||
"""Custom admin to add a descriptive subheader to list views."""
|
"""Custom admin to add a descriptive subheader to list views."""
|
||||||
|
|
||||||
def get_changelist(self, request, **kwargs):
|
def get_changelist(self, request, **kwargs):
|
||||||
|
@ -423,12 +423,18 @@ class UserDomainRoleAdmin(ListHeaderAdmin):
|
||||||
|
|
||||||
_meta = Meta()
|
_meta = Meta()
|
||||||
|
|
||||||
|
# TODO - maybe instead of get we just call it "sort"?
|
||||||
# Columns
|
# Columns
|
||||||
list_display = [
|
list_display = [
|
||||||
"user",
|
"get_user",
|
||||||
"domain",
|
"get_domain",
|
||||||
"role",
|
"role",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
orderable_fk_fields = [
|
||||||
|
("domain", "name"),
|
||||||
|
("user", ["first_name", "last_name"]),
|
||||||
|
]
|
||||||
|
|
||||||
# Search
|
# Search
|
||||||
search_fields = [
|
search_fields = [
|
||||||
|
@ -490,13 +496,20 @@ class DomainInvitationAdmin(ListHeaderAdmin):
|
||||||
|
|
||||||
class DomainInformationAdmin(ListHeaderAdmin):
|
class DomainInformationAdmin(ListHeaderAdmin):
|
||||||
"""Customize domain information admin class."""
|
"""Customize domain information admin class."""
|
||||||
|
|
||||||
|
# TODO - include the orderable fk fields inside list display
|
||||||
|
|
||||||
# Columns
|
# Columns
|
||||||
list_display = [
|
list_display = [
|
||||||
"domain",
|
"get_domain",
|
||||||
"organization_type",
|
"organization_type",
|
||||||
"created_at",
|
"created_at",
|
||||||
"submitter",
|
"get_submitter",
|
||||||
|
]
|
||||||
|
|
||||||
|
orderable_fk_fields = [
|
||||||
|
("domain", "name"),
|
||||||
|
("submitter", ["first_name", "last_name"]),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Filters
|
# Filters
|
||||||
|
@ -624,7 +637,7 @@ class DomainApplicationAdminForm(forms.ModelForm):
|
||||||
self.fields["status"].widget.choices = available_transitions
|
self.fields["status"].widget.choices = available_transitions
|
||||||
|
|
||||||
|
|
||||||
class DomainApplicationAdmin(ListHeaderAdmin, OrderableFieldsMixin):
|
class DomainApplicationAdmin(ListHeaderAdmin):
|
||||||
|
|
||||||
"""Custom domain applications admin class."""
|
"""Custom domain applications admin class."""
|
||||||
|
|
||||||
|
@ -720,7 +733,7 @@ class DomainApplicationAdmin(ListHeaderAdmin, OrderableFieldsMixin):
|
||||||
]
|
]
|
||||||
|
|
||||||
filter_horizontal = ("current_websites", "alternative_domains", "other_contacts")
|
filter_horizontal = ("current_websites", "alternative_domains", "other_contacts")
|
||||||
|
|
||||||
# lists in filter_horizontal are not sorted properly, sort them
|
# lists in filter_horizontal are not sorted properly, sort them
|
||||||
# by website
|
# by website
|
||||||
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
||||||
|
|
|
@ -16,9 +16,16 @@ logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class OrderableFieldsMixin:
|
class OrderableFieldsMixin:
|
||||||
|
"""
|
||||||
|
Mixin to add multi-field ordering capabilities to a Django ModelAdmin on admin_order_field.
|
||||||
|
"""
|
||||||
orderable_fk_fields = []
|
orderable_fk_fields = []
|
||||||
|
|
||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
This magic method is called when a new instance of the class (or subclass) is created.
|
||||||
|
It dynamically adds a new method to the class for each field in `orderable_fk_fields`.
|
||||||
|
"""
|
||||||
new_class = super().__new__(cls)
|
new_class = super().__new__(cls)
|
||||||
for field, sort_field in cls.orderable_fk_fields:
|
for field, sort_field in cls.orderable_fk_fields:
|
||||||
setattr(new_class, f"get_{field}", cls._create_orderable_field_method(field, sort_field))
|
setattr(new_class, f"get_{field}", cls._create_orderable_field_method(field, sort_field))
|
||||||
|
@ -26,7 +33,54 @@ class OrderableFieldsMixin:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _create_orderable_field_method(cls, field, sort_field):
|
def _create_orderable_field_method(cls, field, sort_field):
|
||||||
|
"""
|
||||||
|
This class method is a factory for creating dynamic methods that will be attached to the ModelAdmin subclass.
|
||||||
|
It is used to customize how fk fields are ordered. By default, fks are ordered by id, so if you wish to
|
||||||
|
order by "name" instead, you need to manually override that.
|
||||||
|
|
||||||
|
|
||||||
|
In essence, this function will more or less generate code that looks like this,
|
||||||
|
for a given tuple defined in orderable_fk_fields:
|
||||||
|
|
||||||
|
```
|
||||||
|
def get_requested_domain(self, obj):
|
||||||
|
return obj.requested_domain
|
||||||
|
# Allows column order sorting
|
||||||
|
get_requested_domain.admin_order_field = "requested_domain__name"
|
||||||
|
# Sets column's header
|
||||||
|
get_requested_domain.short_description = "requested domain"
|
||||||
|
```
|
||||||
|
|
||||||
|
Or for fields with multiple order_fields:
|
||||||
|
|
||||||
|
```
|
||||||
|
def get_submitter(self, obj):
|
||||||
|
return obj.submitter
|
||||||
|
# Allows column order sorting
|
||||||
|
get_requested_domain.admin_order_field = ["submitter__first_name", "submitter__last_name"]
|
||||||
|
# Sets column's header
|
||||||
|
get_requested_domain.short_description = "submitter"
|
||||||
|
```
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
cls: The class that this method is being called on. In the context of this mixin, it would be the ModelAdmin subclass.
|
||||||
|
field: A string representing the name of the attribute that the dynamic method will fetch from the model instance.
|
||||||
|
sort_field: A string or list of strings representing the field(s)
|
||||||
|
that Django should sort by when the column is clicked in the admin interface.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
method: The dynamically created method.
|
||||||
|
|
||||||
|
The dynamically created method has the following attributes:
|
||||||
|
__name__: A string representing the name of the method. This is set to "get_{field}".
|
||||||
|
admin_order_field: A string or list of strings representing the field(s) that Django should sort by when the column is clicked in the admin interface.
|
||||||
|
short_description: A string used as the column header in the admin interface. Will replace underscores with spaces.
|
||||||
|
"""
|
||||||
def method(obj):
|
def method(obj):
|
||||||
|
"""
|
||||||
|
The dynamically created method.
|
||||||
|
When called on a model instance, it returns the value of the specified attribute.
|
||||||
|
"""
|
||||||
attr = getattr(obj, field)
|
attr = getattr(obj, field)
|
||||||
return attr
|
return attr
|
||||||
|
|
||||||
|
@ -41,7 +95,7 @@ class OrderableFieldsMixin:
|
||||||
else:
|
else:
|
||||||
method.admin_order_field = f"{field}__{sort_field}"
|
method.admin_order_field = f"{field}__{sort_field}"
|
||||||
|
|
||||||
method.short_description = field.replace("_", " ").title()
|
method.short_description = field.replace("_", " ")
|
||||||
return method
|
return method
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue