def register(model, admin_class=None, site=None, **options): """ Provide tag support to the model when it is registered with the admin site. For tagged models (have one or more SingleTagField or TagField fields): * Admin will support TagField in list_display For tag models (subclass of TagModel): * Admin will provide a merge action to merge tags For other models: * No changes made Arguments: model Model to register admin_class Admin class for model site Admin site to register with Default: django.contrib.admin.site **options Extra options for admin class This only supports one model, but is otherwise safe to use with non-tagged models. """ # Look at the model we've been given if isinstance(model, tag_models.BaseTagDescriptor): # It's a tag descriptor; change it for the tag model itself model = model.tag_model elif not isinstance(model, ModelBase): raise ImproperlyConfigured( "Tagulous can only register a single model with admin.") # Ensure we have a valid admin site if site is None: site = admin.site # # Determine appropriate admin class # if not admin_class: admin_class = admin.ModelAdmin # Going to make a list of base classes to inject cls_bases = [] # If it's a tag model, subclass TagModelAdmin or TagTreeModelAdmin if issubclass(model, tag_models.BaseTagModel): if issubclass(model, tag_models.TagTreeModel): if not issubclass(admin_class, TagTreeModelAdmin): cls_bases += [TagTreeModelAdmin] else: if not issubclass(admin_class, TagModelAdmin): cls_bases += [TagModelAdmin] # If it's a tagged model, subclass TaggedModelAdmin singletagfields = tag_models.singletagfields_from_model(model) tagfields = tag_models.tagfields_from_model(model) if singletagfields or tagfields: if not issubclass(admin_class, TaggedModelAdmin): cls_bases += [TaggedModelAdmin] # If options specified, or other bases, will need to subclass admin_class if options or cls_bases: cls_bases += [admin_class] # Update options with anything the new subclasses could have overidden # in a custom ModelAdmin - unless they're already overridden in options options["__module__"] = __name__ if admin_class != admin.ModelAdmin: options.update( dict( (k, v) for k, v in admin_class.__dict__.items() if k in ["list_display", "list_filter", "exclude", "actions"] and k not in options)) admin_class = type(str("%sAdmin" % model.__name__), tuple(cls_bases), options) # Enhance the model admin class enhance(model, admin_class) # Register the model # Don't pass options - we've already dealt with that site.register(model, admin_class)
def register(model, admin_class=None, site=None, **options): """ Provide tag support to the model when it is registered with the admin site. For tagged models (have one or more SingleTagField or TagField fields): * Admin will support TagField in list_display For tag models (subclass of TagModel): * Admin will provide a merge action to merge tags For other models: * No changes made Arguments: model Model to register admin_class Admin class for model site Admin site to register with Default: django.contrib.admin.site **options Extra options for admin class This only supports one model, but is otherwise safe to use with non-tagged models. """ # Look at the model we've been given if isinstance(model, tag_models.BaseTagDescriptor): # It's a tag descriptor; change it for the tag model itself model = model.tag_model elif not isinstance(model, ModelBase): raise ImproperlyConfigured( 'Tagulous can only register a single model with admin.' ) # Ensure we have a valid admin site if site is None: site = admin.site # # Determine appropriate admin class # if not admin_class: admin_class = admin.ModelAdmin # Going to make a list of base classes to inject cls_bases = [] # If it's a tag model, subclass TagModelAdmin or TagTreeModelAdmin if issubclass(model, tag_models.BaseTagModel): if issubclass(model, tag_models.TagTreeModel): if not issubclass(admin_class, TagTreeModelAdmin): cls_bases += [TagTreeModelAdmin] else: if not issubclass(admin_class, TagModelAdmin): cls_bases += [TagModelAdmin] # If it's a tagged model, subclass TaggedModelAdmin singletagfields = tag_models.singletagfields_from_model(model) tagfields = tag_models.tagfields_from_model(model) if singletagfields or tagfields: if not issubclass(admin_class, TaggedModelAdmin): cls_bases += [TaggedModelAdmin] # If options specified, or other bases, will need to subclass admin_class if options or cls_bases: cls_bases += [admin_class] # Update options with anything the new subclasses could have overidden # in a custom ModelAdmin - unless they're already overridden in options options['__module__'] = __name__ if admin_class != admin.ModelAdmin: options.update(dict( (k, v) for k, v in admin_class.__dict__.items() if k in ['list_display', 'list_filter', 'exclude', 'actions'] and k not in options )) admin_class = type( str("%sAdmin" % model.__name__), tuple(cls_bases), options, ) # Enhance the model admin class enhance(model, admin_class) # Register the model # Don't pass options - we've already dealt with that site.register(model, admin_class)
def enhance(model, admin_class): """ Add tag support to the admin class based on the specified model """ # # Get a list of all tag fields # # Dict of single tag fields, {name: tag} single_tag_fields = {} # Dict of normal tag fields, {name: tag} tag_fields = {} # List of all single and normal tag fields tag_field_names = [] # Check for SingleTagField related fields for field in tag_models.singletagfields_from_model(model): single_tag_fields[field.name] = field tag_field_names.append(field.name) # Check for TagField m2m fields for field in tag_models.tagfields_from_model(model): tag_fields[field.name] = field tag_field_names.append(field.name) # # Ensure any tag fields in list_display are rendered by functions # # The admin.site.register will complain if it's a ManyToManyField, so this # will work around that. # # We also need to have a different name to the model field, otherwise the # ChangeList class will just use the model field - that would get the tag # strings showing in the table, but the column would be sortable which # would cause problems for TagFields, and the display function would never # get called, which would be unexpected for anyone maintaining this code. if hasattr(admin_class, "list_display"): # Make sure we're working with a list if isinstance(admin_class.list_display, tuple): admin_class.list_display = list(admin_class.list_display) for i, field in enumerate(admin_class.list_display): # If the field's not a callable, and not in the admin class already if not hasattr(field, "__call__") and not hasattr( admin_class, field): # Only TagFields (admin can already handle SingleTagField FKs) if field in tag_fields: # Create new field name and replace in list_display display_name = "_tagulous_display_%s" % field admin_class.list_display[i] = display_name # Add display function to admin class setattr(admin_class, display_name, _create_display(field)) # # If admin is for a tag model, ensure any inlines for tagged models are # subclasses of TaggedInlineFormSet. # if issubclass(model, tag_models.BaseTagModel) and hasattr( admin_class, "inlines"): for inline_cls in admin_class.inlines: # Make sure inline class uses TaggedBaseModelAdminMixin if not issubclass(inline_cls, TaggedBaseModelAdminMixin): inline_cls.__bases__ = ( TaggedBaseModelAdminMixin, ) + inline_cls.__bases__ # Make sure inlines used TaggedInlineFormSet if issubclass( inline_cls.model, tag_models.TaggedModel) and not issubclass( inline_cls.formset, tag_forms.TaggedInlineFormSet): orig_cls = inline_cls.formset inline_cls.formset = type( str("Tagged%s" % orig_cls.__name__), (tag_forms.TaggedInlineFormSet, orig_cls), {}, )
def enhance(model, admin_class): """ Add tag support to the admin class based on the specified model """ # # Get a list of all tag fields # # Dict of single tag fields, {name: tag} single_tag_fields = {} # Dict of normal tag fields, {name: tag} tag_fields = {} # List of all single and normal tag fields tag_field_names = [] # Check for SingleTagField related fields for field in tag_models.singletagfields_from_model(model): single_tag_fields[field.name] = field tag_field_names.append(field.name) # Check for TagField m2m fields for field in tag_models.tagfields_from_model(model): tag_fields[field.name] = field tag_field_names.append(field.name) # # Ensure any tag fields in list_display are rendered by functions # # The admin.site.register will complain if it's a ManyToManyField, so this # will work around that. # # We also need to have a different name to the model field, otherwise the # ChangeList class will just use the model field - that would get the tag # strings showing in the table, but the column would be sortable which # would cause problems for TagFields, and the display function would never # get called, which would be unexpected for anyone maintaining this code. if hasattr(admin_class, 'list_display'): # Make sure we're working with a list if isinstance(admin_class.list_display, tuple): admin_class.list_display = list(admin_class.list_display) for i, field in enumerate(admin_class.list_display): # If the field's not a callable, and not in the admin class already if not hasattr(field, '__call__') and not hasattr(admin_class, field): # Only TagFields (admin can already handle SingleTagField FKs) if field in tag_fields: # Create new field name and replace in list_display display_name = '_tagulous_display_%s' % field admin_class.list_display[i] = display_name # Add display function to admin class setattr(admin_class, display_name, _create_display(field)) # # If admin is for a tag model, ensure any inlines for tagged models are # subclasses of TaggedInlineFormSet. # if ( issubclass(model, tag_models.BaseTagModel) and hasattr(admin_class, 'inlines') ): for inline_cls in admin_class.inlines: # Make sure inline class uses TaggedBaseModelAdminMixin if not issubclass(inline_cls, TaggedBaseModelAdminMixin): inline_cls.__bases__ = ( TaggedBaseModelAdminMixin, ) + inline_cls.__bases__ # Make sure inlines used TaggedInlineFormSet if ( issubclass(inline_cls.model, tag_models.TaggedModel) and not issubclass(inline_cls.formset, tag_forms.TaggedInlineFormSet) ): orig_cls = inline_cls.formset inline_cls.formset = type( str('Tagged%s' % orig_cls.__name__), (tag_forms.TaggedInlineFormSet, orig_cls), {}, )