|
| 1 | +from django.contrib import admin |
| 2 | +from django.forms.models import modelform_factory |
| 3 | + |
| 4 | +# Imports for Dynamic app registrations |
| 5 | +from django.apps import apps |
| 6 | + |
| 7 | +# Models |
| 8 | +from web.models import * |
| 9 | + |
| 10 | +# Json Widget |
| 11 | +# from web.widget import JsonEditorWidget |
| 12 | + |
| 13 | + |
| 14 | + |
| 15 | +# CONFIG CONSTANTS |
| 16 | +admin.site.site_header = 'WOLFx Admin' |
| 17 | +exempt = [] # modelname in this list will not be registered |
| 18 | +global_app_name = 'web' # Replace '' with your app name |
| 19 | + |
| 20 | + |
| 21 | +# ADMIN.PY MAIN CODE |
| 22 | +class GenericStackedAdmin(admin.StackedInline): |
| 23 | + extra = 1 |
| 24 | + |
| 25 | + # This method ensures the field order is correct for inlines as well |
| 26 | + def get_formset(self, request, obj=None, **kwargs): |
| 27 | + formset = super().get_formset(request, obj, **kwargs) |
| 28 | + form = formset.form |
| 29 | + custom_order = [field for field in form.base_fields if field not in CommonModel._meta.fields] |
| 30 | + custom_order += [field for field in CommonModel._meta.fields if field in form.base_fields] |
| 31 | + form.base_fields = {field: form.base_fields[field] for field in custom_order} |
| 32 | + return formset |
| 33 | + |
| 34 | + |
| 35 | +class GenericAdmin(admin.ModelAdmin): |
| 36 | + def __init__(self, model, admin_site): |
| 37 | + self.model = model |
| 38 | + self.inlines = [] |
| 39 | + self.actions = [] |
| 40 | + self.admin_meta = getattr(model, 'admin_meta', {}) |
| 41 | + |
| 42 | + # Dynamic admin meta from model |
| 43 | + # Specify a static dictionary in model |
| 44 | + try: |
| 45 | + # Admin Meta |
| 46 | + if model.admin_meta: |
| 47 | + for k,v in model.admin_meta.items(): |
| 48 | + self.__setattr__(k,v) |
| 49 | + except: |
| 50 | + pass |
| 51 | + |
| 52 | + # Dynamic Actions from model |
| 53 | + # Specify a key 'actions' in the admin_meta dictionary in model |
| 54 | + try: |
| 55 | + if 'actions' in model.admin_meta: |
| 56 | + for action_name in model.admin_meta['actions']: |
| 57 | + # Ensure action_name is a string |
| 58 | + if isinstance(action_name, str): |
| 59 | + action_function = getattr(model, action_name, None) |
| 60 | + if callable(action_function): |
| 61 | + self.add_action(action_function, action_name) |
| 62 | + except Exception as e: |
| 63 | + # Handle or log the exception |
| 64 | + pass |
| 65 | + |
| 66 | + # Register Inlines |
| 67 | + self.register_inlines() |
| 68 | + |
| 69 | + super().__init__(model, admin_site) |
| 70 | + |
| 71 | + def formfield_for_dbfield(self, db_field, request, **kwargs): |
| 72 | + # Check if the field is a JSONField |
| 73 | + if isinstance(db_field, models.JSONField) and self.admin_meta: |
| 74 | + # Retrieve the schema configuration for JSON fields |
| 75 | + json_fields_meta = self.model.admin_meta.get('json_fields', {}) |
| 76 | + |
| 77 | + # Retrieve the schema for the specific field, if defined |
| 78 | + json_schema = json_fields_meta.get(db_field.name, {}).get('schema') |
| 79 | + |
| 80 | + if json_schema: |
| 81 | + # Initialize the custom widget with the specified schema |
| 82 | + kwargs['widget'] = JsonEditorWidget(schema=json_schema) |
| 83 | + else: |
| 84 | + # Else load the django-jsoneditor widget |
| 85 | + kwargs['widget'] = JSONEditor() |
| 86 | + |
| 87 | + return super().formfield_for_dbfield(db_field, request, **kwargs) |
| 88 | + |
| 89 | + # Function to get the fieldsets |
| 90 | + def get_fieldsets(self, request, obj=None): |
| 91 | + # If admin meta has fieldssets defined then return that fieldsets |
| 92 | + if 'fieldsets' in self.admin_meta: |
| 93 | + return self.admin_meta['fieldsets'] |
| 94 | + |
| 95 | + # Define the fieldsets |
| 96 | + common_fields = [field.name for field in CommonModel._meta.fields if field.editable] |
| 97 | + other_fields = [field.name for field in self.model._meta.fields if (field.name not in common_fields and field.editable and field.name != 'id')] |
| 98 | + m2m_fields = [field.name for field in self.model._meta.many_to_many] |
| 99 | + other_fields = other_fields + m2m_fields |
| 100 | + |
| 101 | + fieldsets = [ |
| 102 | + (self.model._meta.object_name, { |
| 103 | + 'fields': other_fields, |
| 104 | + }), |
| 105 | + ('Meta Data', { |
| 106 | + 'fields': common_fields, |
| 107 | + }), |
| 108 | + ] |
| 109 | + |
| 110 | + return fieldsets |
| 111 | + |
| 112 | + def get_readonly_fields(self, request, obj=None): |
| 113 | + # Get a list of non-editable fields |
| 114 | + readonly_fields = [field.name for field in self.model._meta.fields if (not field.editable or field.name == 'id')] |
| 115 | + |
| 116 | + return readonly_fields |
| 117 | + |
| 118 | + # Function to add actions to the admin class |
| 119 | + def add_action(self, action_function, action_name): |
| 120 | + def wrapper_action(modeladmin, request, queryset): |
| 121 | + for obj in queryset: |
| 122 | + action_method = getattr(obj, action_name) |
| 123 | + if callable(action_method): |
| 124 | + action_method(request) |
| 125 | + |
| 126 | + wrapper_action.__name__ = f'admin_action_{action_name}' # Change the name |
| 127 | + wrapper_action.short_description = action_name.replace('_', ' ').title() |
| 128 | + |
| 129 | + if not hasattr(self, 'actions') or not self.actions: |
| 130 | + self.actions = [wrapper_action] |
| 131 | + else: |
| 132 | + # Prevent re-adding the same action |
| 133 | + if wrapper_action not in self.actions: |
| 134 | + self.actions.append(wrapper_action) |
| 135 | + |
| 136 | + self.__dict__[wrapper_action.__name__] = wrapper_action |
| 137 | + |
| 138 | + def register_inlines(self): |
| 139 | + if hasattr(self.model, 'admin_meta') and 'inline' in self.model.admin_meta: |
| 140 | + for inline_info in self.model.admin_meta['inline']: |
| 141 | + for related_model, fk_name in inline_info.items(): |
| 142 | + self.add_inline(related_model, fk_name) |
| 143 | + |
| 144 | + def add_inline(self, related_model_name, fk_name): |
| 145 | + related_model = apps.get_model(app_label=global_app_name, model_name=related_model_name) # Replace 'your_app_name' |
| 146 | + inline_class_name = f"{related_model.__name__}Inline" |
| 147 | + |
| 148 | + class_attrs = { |
| 149 | + 'model' : related_model, |
| 150 | + 'fk_name' : fk_name, |
| 151 | + 'form' : modelform_factory(related_model, exclude=[]), |
| 152 | + } |
| 153 | + |
| 154 | + InlineAdminClass = type(inline_class_name, (GenericStackedAdmin,), class_attrs) |
| 155 | + self.inlines.append(InlineAdminClass) |
| 156 | + |
| 157 | + # Custom Media so that we can add custom js files |
| 158 | + class Media: |
| 159 | + js = ('https://code.jquery.com/jquery-3.7.0.js', ) |
| 160 | + |
| 161 | + |
| 162 | +app = apps.get_app_config(global_app_name) |
| 163 | +for model_name, model in app.models.items(): |
| 164 | + # If model_name consists history |
| 165 | + if model_name not in exempt and 'histor' not in model_name.lower(): |
| 166 | + # print(model_name + ' ' + str(model)) |
| 167 | + admin.site.register(model, GenericAdmin) |
| 168 | + |
0 commit comments