Extending Django Media Tree¶
There are several ways in which you may want to add functionality to Media Tree. Suppose you need to add support for a specific image format, or you need custom maintenance actions in the admin, or you might need to add some Javascript or CSS code to the FileNode form. For each of these cases, there is a so-called extender class.
Overview¶
The extender base classes provided by Media Tree are ModelExtender
,
AdminExtender
and FormExtender
, and by subclassing them you create your
custom extenders. The structure of extender classes is similar to that
of a regular Django Model
, ModelAdmin
, or Form
class, respectively,
meaning that you can define several attributes such as model Fields or form
Media, and they will be added to Media Tree during runtime.
Note
You can package and install your extender
classes as a regular Django application module, and have Media Tree
auto-discover installed extensions by providing a media_extension.py
module. An application containing one or more extenders and a
media_extension.py
that registers them is what is called a Media Tree
extension.
Media Tree already comes with some exemplary extensions in its
contrib.media_extensions
module. You should inspect these examples in order
to get an idea of how to build an extension. There is also a tutorial below that
should help you with creating your own extension.
Extender bases¶
An extender is created by subclassing one of the following base classes:
Extending the FileNode model¶
Extending the FileNode admin¶
Extending forms¶
Registering and installing Media Tree extensions¶
Each extension module is a regular Django application that is installed by
putting the application in the INSTALLED_APPS
setting.
An extension needs to contain a media_extension.py
module that registers all
extenders that the extension module contains:
Example of an extension.py
file:
from media_tree import extension
class SomeModelExtender(extension.ModelExtender):
"""Example extender"""
pass
extension.register(SomeModelExtender)
Notice that on the last line the extender is registered by calling the
function media_tree.extension.register()
.
Tutorial extension: Geotagging Photos¶
Assume you are using landscape photographs on your website, and in the FileNode admin you would like to be able to enter the latitude and longitude of the place where they were taken. This is called geotagging.
Getting started¶
The first step is to create a Django application that serves as the container for our new extender classes. You can do this as usual on the command line from your project folder:
django-admin startapp media_tree_geotagging
cd media_tree_geotagging
touch media_extension.py
Notice that on the last line we created a file called media_extension.py
.
Media Tree will scan all INSTALLED_APPS
for such a file, so that all
installed extensions will be auto-disovered.
We can delete most of the other files that the startapp
command created,
such as models.py
, as we are probably not going to need them.
Extending the Model¶
Now you can create the model extender in the file media_extension.py
,
subclassing the parent class provided by Media Tree:
from media_tree import extension
from django.db import models
class GeotaggingModelExtender(extension.ModelExtender):
lat = models.FloatField('latitude', null=True, blank=True)
lng = models.FloatField('longitude', null=True, blank=True)
extension.register(GeotaggingModelExtender)
This class looks similar to a regular Model, but it does not have its own
database table – instead, its fields are added to the FileNode
class when
you restart the development server.
Note
This extension adds the fields lat
and lng
to
the FileNode
model. You are going to have to add these fields to
the database table yourself by modifying the media_tree_filenode
table
with a database client, unless you installed it before running
syncdb
).
Extending the form¶
Of course we want to be able to edit our two new fields in the admin, so we need
to create a form extender and add a new fieldset. We do this by adding a new
class to media_extension.py
:
class GeotaggingFormExtender(extension.FormExtender):
class Meta:
fieldsets = [
('Geotagging', {
'fields': ['lat', 'lng'],
'classes': ['collapse']
})
]
extension.register(GeotaggingFormExtender)
Installing the extension¶
After you have created the database fields, you can install the extension by
adding it to the INSTALLED_APPS
in your project’s settings file:
INSTALLED_APPS = (
# ... your apps here ...
'media_tree',
'media_tree_geotagging'
)
Adding an Admin Action¶
Let’s assume you have a content editor on staff, and this person’s job is to check if photographs were geotagged, and to notify the photographer of the ones that aren’t. We can simplify this task by adding an admin action to the FileNode admin.
With this extender, the editor will be able to check the checkboxes next to image files, have them checked automatically to see if they are not yet geotagged, and email the photographer the admin links to those FileNode objects.
As you may be assuming by now, we create an admin extender in
media_extension.py
:
from django.core.mail import send_mail
class GeotaggingAdminExtender(extension.AdminExtender):
def notify_of_non_geotagged(modeladmin, request, queryset):
non_geotagged_links = []
for node in queryset:
# Check if node is JPG and not geotagged:
if node.extension == 'jpg':
if not node.lat or not node.lng:
non_geotagged_links.append(node.get_admin_url())
# Send email with admin links for these nodes, and message
# current user about status of the action.
if len(non_geotagged_links):
message = '\n'.join(non_geotagged_links) + '\n\nThanks!'
send_mail('Please geotag these files', message,
'from@example.com', ['to@example.com'])
modeladmin.message_user(request, 'Notification sent for' \
+ ' %i non-geotagged JPGs.' % len(non_geotagged_links))
else:
modeladmin.message_user(request, 'All selected images appear' \
+' to be OK.')
notify_of_non_geotagged.short_description = \
'Notify photographer if selected JPGs are not geotagged'
actions = [notify_of_non_geotagged]
This last example is a bit more verbose, but you will notice that it just
contains one method with the exact same signature like a regular Django admin
action, and on the last line we are specifying the list of actions that this
extender will contribute to the FileNode admin. Also, we are giving the method a
short_description
that will appear in the drop-down menu above the list
displaying all of our FileNodes.
And that’s it! We are now able to geotag images in the Django admin.
Adding Form Media¶
Of course it would be great if we had a map widget in the form where we can just
drop a pin on the location of the photograph. Creating such a widget is beyond
the scope of this tutorial, but if we had created a Javascript containing the
code that implements such a widget, we could easily add this file by adding a
Media
class to our form extender:
class GeotaggingFormExtender(extension.AdminExtender):
class Media:
js = (
'map_widget.js',
)
# ...
This Media definition is merged with the default media loaded for the FileNode form, and we can use it to load any code or CSS files required by our hypothetical map widget.
Conclusion¶
Using this extension system, you can change many aspects of how Media Tree behaves. There are more attributes and also signals that you can define in your extenders than the ones described in this tutorial. Code away and, please, share your extensions with the Interested Public!
Tutorial extension: Creating an icon set¶
Icon sets are also packaged as Django applications, and creating a custom set
is rather easy. Basically, an icon set is a Python module containing nothing
but an empty __init__.py
and a static
folder with the respective image
files. Here’s an example of how that could look like:
my_custom_audio_icon_set
__init__.py
static
audio_icons
audio.png
ogg.png
mp3.png
Note that this package contains three icons: One for generic audio files and
one for either OGG
or MP3
files.
Note
When displaying a file icon, Media Tree will scan all installed icon sets for
an icon that is named like the media file’s extension (e.g. mp3.png
),
then for one named like its mimetype (e.g. audio/x-mpeg.png
), then for
the mime supertype (e.g. audio.png
). Icon discovery is handled by a class
called MimetypeStaticIconFileFinder
, which by default only finds PNG
files.
To install this icon set, simply add my_custom_audio_icon_set
to your
INSTALLED_APPS
, collect its static files, and configure the new icon folder
using the MEDIA_TREE_ICON_DIRS
setting. See Installing icon sets (optional)
for more detailed instructions.