Extending Django Filer

Django Filer ships with support for image files, and generic files (everything that’s not an image).

So what if you wanted to add support for a particular kind of file thats’s not already included? It’s easy to extend it to do this, without needing to touch the Filer’s code at all (and no-one wants to have to maintain fork if they can avoid it).

So for example, you might want to be able to manage video files. You could of course simply store and file them as generic file types, but that might not be enough - perhaps your own application needs to know that certain files are in fact video files, so that it can treat them appropriately (process them, allow only them to be selected in certain widgets, and so on).

In this example we will create support for video files.

The model

The very basics

In your own application, you need to create a Video model. This model has to inherit from filer.models.filemodels.File.

# import the File class to inherit from
from filer.models.filemodels import File

# we'll need to refer to filer settings
from filer import settings as filer_settings

class Video(File):
    pass # for now...

When a file is uploaded, filer.admin.clipboardadmin.ClipboardAdmin.ajax_upload() loops over the different models in filer.settings.FILER_FILE_MODELS and calls its matches_file_type() to see if the file matches a known filename extension.

When a match is found, the filer will create an instance of that class for the file.

So let’s add a matches_file_type() method to the Video model:

@classmethod
def matches_file_type(cls, iname, ifile, mime_type):
    video_types = ['application/vnd.dvb.ait', 'video/x-sgi-movie', 'video/mp4', 'video/mpeg',
                   'video/x-msvideo', 'video/x-ms-wmv', 'video/ogg', 'video/webm', 'video/quicktime']
    return mime_type in video_types

Note

The signature of this classmethod changed in version 2.0.

Now you can upload files of those types into the Filer.

For each one you upload an instance of your Video class will be created.

Icons

At the moment, the files you upload will have the Filer’s generic file icon - not very appropriate or helpful for video. What you need to do is add a suitable _icon attribute to the class.

The filer.models.filemodels.File class we’ve inherited from has an icons() property, from filer.models.mixins.IconsMixin.

This checks for the _icon attribute; if it finds one, it uses it to build URLs for the icons in various different sizes. If _icons is video, a typical result might be /static/filer/icons/video_48x48.png.

Of course, you can also create an icons() property specific to your new model. For example, filer.models.imagemodels.Image does that, so that it can create thumbnail icons for each file rather than a single icon for all of that type.

In our Video model the simple case will do:

# the icon it will use
_icon = "video"

And in fact, the Filer already has an icon that matches this - if there were not already a set of video icons in the Filer’s static assets, we’d have to provide them - see filer/static/icons for examples.

The admin

Now we need to register our new model with the admin. Again, the very simplest case:

from django.contrib import admin
from filer.admin.fileadmin import FileAdmin
from models import Video

admin.site.register(Video, FileAdmin) # use the standard FileAdmin

… but of course if your model had particular fields of its own (as for example the Image model has a subject_location field) you would create your own ModelAdmin class for it, along with a form, special widgets and whatever else you needed.

Using your new file type

You’ve now done enough to be able to get hold of files of your new kind in the admin (wherever the admin uses a FilerFileField) but to make it really useful we need to to a little more.

For example, it might be useful to have:

  • its own field type to get hold of it in some other model

  • a special form for the field

  • a widget for selecting it in the admin

  • … and so on

How you use it will be up to you, but a fairly typical use case would be in a django CMS plugin, and that is the example that will be followed here.

Create a custom field for your file type

from filer.fields.file import FilerFileField

class FilerVideoField(FilerFileField):
    default_model_class = Video

Of course you could also create an admin widget and admin form, but it’s not necessary at this stage - the ones generic files use will do just fine.

Create some other model that uses it

Here, it’s going to be a django CMS plugin:

from cms.models import CMSPlugin

class VideoPluginEditor(CMSPlugin):
    video = FilerVideoField()
    # you'd probably want some other fields in practice...

You’ll have to provide an admin class for your model; in this case, the admin will be provided as part of the django CMS plugin architecture.

Note

If you are not already familiar with the django CMS plugin architecture, http://docs.django-cms.org/en/latest/how_to/custom_plugins.html#overview will provide an explanation.

from cms.plugin_base import CMSPluginBase
from models import VideoPluginEditor

class VideoPluginPublisher(CMSPluginBase):
    model = VideoPluginEditor
    render_template = "video/video.html"
    text_enabled = True
    admin_preview = False

    def icon_src(self, instance):
        return "/static/plugin_icons/video.png"

    def render(self, context, instance, placeholder):
        context.update({
            'video':instance,
            'placeholder':placeholder,
        })
        return context

plugin_pool.register_plugin(VideoPluginPublisher)

… and now, assuming you have created a suitable video/video.html, you’ve got a working plugin that will make use of your new Filer file type.

Other things you could add

Admin templating

filer/templates/templates/admin/filer/folder lists the individual items in each folder. It checks item.file_type to determine how to display those items and what to display for them.

You might want to extend this, so that the list includes the appropriate information for your new file type. In that case you will need to override the template, and in the Video model:

# declare the file_type for the list template
file_type = 'Video'

Note that if you do this, you will need to override the template - otherwise your items will fail to display in the folder lists.

Providing custom Image model

As the Image model is special, a different way to implement custom Image model is required, which uses the Django swappable models interface.

Defining the model

First a custom model must be defined; it should inherit from BaseImage, the basic abstract class:

from filer.models.abstract.BaseImage

class CustomImage(BaseImage):
    my_field = models.CharField(max_length=10)

    class Meta(BaseImage.Meta):
        # You must define a meta with en explicit app_label
        app_label = 'myapp'
        default_manager_name = 'objects'

The model can be defined in any installed application declared after django-filer.

BaseImage defines the following fields (plus the basic fields defined in File):

  • default_alt_text

  • default_caption

  • subject_location

you may add whatever fields you need, just like any other model.

..warning: app_label in Meta must be explicitly defined.

Customize the admin

If you added fields in your custom Image model, you have to customize the admin too:

from django.contrib import admin
from filer.admin.imageadmin import ImageAdmin


Image = load_model(filer_settings.FILER_IMAGE_MODEL)

class CustomImageAdmin(ImageAdmin):
    # your custom code
    pass

# Using build_fieldsets allows to easily integrate common field in the admin
# Don't define fieldsets in the ModelAdmin above and add the custom fields
# to the ``extra_main_fields`` or ``extra_fieldsets`` as shown below
CustomImageAdmin.fieldsets = CustomImageAdmin.build_fieldsets(
    extra_main_fields=('default_alt_text', 'default_caption', 'my_field'...),
    extra_fieldsets=(
        ('Subject Location', {
            'fields': ('subject_location',),
            'classes': ('collapse',),
        }),
    )
)

# Unregister the default admin
admin.site.unregister(Image)
# Register your own
admin.site.register(Image, CustomImageAdmin)

Swap the Image model

Set FILER_IMAGE_MODEL to the path of your custom model:

FILER_IMAGE_MODEL = 'myapp.CustomImage'