diff --git a/enterprise_catalog/apps/catalog/admin.py b/enterprise_catalog/apps/catalog/admin.py index a7217f27..26312109 100644 --- a/enterprise_catalog/apps/catalog/admin.py +++ b/enterprise_catalog/apps/catalog/admin.py @@ -1,6 +1,7 @@ from django.contrib import admin from django.urls import reverse -from django.utils.html import format_html +from django.utils.html import format_html, format_html_join +from django.utils.safestring import mark_safe from edx_rbac.admin import UserRoleAssignmentAdmin from enterprise_catalog.apps.catalog.constants import ( @@ -15,9 +16,19 @@ ContentMetadata, EnterpriseCatalog, EnterpriseCatalogRoleAssignment, + RestrictedCourseMetadata, ) +def _html_list_from_objects(objs, viewname, str_callback=None): + str_callback = str_callback or str + return format_html_join( + sep=mark_safe('
'), + format_string='{}', + args_generator=((reverse(viewname, args=[obj.pk]), str_callback(obj)) for obj in objs), + ) + + class UnchangeableMixin(admin.ModelAdmin): """ Mixin for disabling changing models through the admin @@ -59,17 +70,77 @@ class ContentMetadataAdmin(UnchangeableMixin): ) readonly_fields = ( 'associated_content_metadata', - 'catalog_queries', - 'get_catalog', + 'get_catalog_queries', + 'get_catalogs', 'modified', ) + exclude = ( + 'catalog_queries', + ) + + @admin.display(description='Catalog Queries') + def get_catalog_queries(self, obj): + catalog_queries = obj.catalog_queries.all() + return _html_list_from_objects( + objs=catalog_queries, + viewname="admin:catalog_catalogquery_change", + str_callback=lambda cq: cq.short_str_for_listings(), + ) @admin.display(description='Enterprise Catalogs') - def get_catalog(self, obj): + def get_catalogs(self, obj): catalogs = EnterpriseCatalog.objects.filter( catalog_query_id__in=obj.catalog_queries.all().values_list('id') ) - return f"{list(catalogs)}" + return _html_list_from_objects(catalogs, "admin:catalog_enterprisecatalog_change") + + +@admin.register(RestrictedCourseMetadata) +class RestrictedCourseMetadataAdmin(UnchangeableMixin): + """ Admin configuration for the custom RestrictedCourseMetadata model. """ + list_display = ( + 'content_key', + 'get_catalog_query_for_list', + 'get_unrestricted_parent', + ) + search_fields = ( + 'content_key', + 'catalog_query', + ) + readonly_fields = ( + 'get_catalog_query', + 'get_catalogs', + 'modified', + ) + exclude = ( + 'catalog_query', + ) + + @admin.display( + description='Catalog Query' + ) + def get_catalog_query_for_list(self, obj): + link = reverse("admin:catalog_catalogquery_change", args=[obj.catalog_query.id]) + return format_html('{}', link, obj.catalog_query.short_str_for_listings()) + + @admin.display( + description='Catalog Query' + ) + def get_catalog_query(self, obj): + link = reverse("admin:catalog_catalogquery_change", args=[obj.catalog_query.id]) + return format_html('{}', link, obj.catalog_query.pretty_print_content_filter()) + + @admin.display( + description='Unrestricted Parent' + ) + def get_unrestricted_parent(self, obj): + link = reverse("admin:catalog_contentmetadata_change", args=[obj.unrestricted_parent.id]) + return format_html('{}', link, str(obj.unrestricted_parent)) + + @admin.display(description='Enterprise Catalogs') + def get_catalogs(self, obj): + catalogs = EnterpriseCatalog.objects.filter(catalog_query=obj.catalog_query) + return _html_list_from_objects(catalogs, "admin:catalog_enterprisecatalog_change") @admin.register(CatalogQuery) diff --git a/enterprise_catalog/apps/catalog/models.py b/enterprise_catalog/apps/catalog/models.py index 8d561bf5..82b72913 100644 --- a/enterprise_catalog/apps/catalog/models.py +++ b/enterprise_catalog/apps/catalog/models.py @@ -166,6 +166,14 @@ def __str__(self): f"and content_filter '{self.pretty_print_content_filter()}'>" ) + def short_str_for_listings(self): + """ + Return *short* human-readable string representation for listings. + """ + return ( + f"" + ) + class EnterpriseCatalog(TimeStampedModel): """ @@ -715,11 +723,7 @@ def __str__(self): """ Return human-readable string representation. """ - return ( - "".format( - content_key=self.content_key - ) - ) + return f"<{self.__class__.__name__} for '{self.content_key}'>" class ContentMetadata(BaseContentMetadata):