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)
Example #2
0
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),
                    {},
                )
Example #4
0
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),
                    {},
                )