django-photo-albums is a pluggable django image gallery app.
Image galleries can be attached to any Django model. And thanks to django 1.1 url namespaces it is possible to have multiple ‘albums’ app instances (for example, for different models) that use different sets of templates, different permission rules, have dedicated integration test suites and are available from different urls.
Each image gallery provide functionality for image viewing, editing, uploading, reordering, marking/unmarking as main and deleting.
django-photo-albums requires Django >= 1.1 (or svn version with url namespaces), setuptools for installation, django-generic-images for image management and django-annoying.
Sorl-thumbnails should be mentioned as a very good optional dependency for generating thumbnails, but you can use any other library if you want.
Testing if app instance is integrated correctly (at least that templates don’t raise exceptions) is easy because base class for integration testcases is provided.
$ pip install django-photo-albums
or:
$ easy_install django-photo-albums
or:
$ hg clone http://bitbucket.org/kmike/django-photo-albums/
$ cd django-photo-albums
$ python setup.py install
Then add ‘photo_albums’ and ‘generic_images’ to your INSTALLED_APPS in settings.py and run ./manage.py syncdb (syncdb is not needed if django-generic-images was already installed).
To add image gallery for your model you should complete following steps:
Create album site instance and plug it’s urls to urlconf:
from photo_albums.urls import PhotoAlbumSite
accounts_photo_site = PhotoAlbumSite(instance_name = 'user_images',
queryset = User.objects.all(),
template_object_name = 'album_user',
has_edit_permission = lambda request, obj: request.user==obj)
urlpatterns += patterns('', url(r'^accounts/', include(accounts_photo_site.urls)),)
Please note that if you deploy multiple albums (ex. for different models), you must provide unique instance_name for each instance to make url reversing work.
Included urls looks like <object_id>/<app_name>/<action> or <object_id>/<app_name>/<image_id>/<action>, where object_id is the id of object which is gallery attached to, app_name is “albums” by default, image_id is image id :) and action is performed action (view, edit, etc). It is possible to use slug instead of object’s id (look at object_regex and lookup_field parameters).
You can use these urls (assuming that user_images in an instance name, album_user is object for which gallery is attached to, image is image in gallery):
{% url user_images:show_album album_user.id %}
{% url user_images:edit_album album_user.id %}
{% url user_images:upload_main_image album_user.id %}
{% url user_images:upload_images album_user.id %}
{% url user_images:show_image album_user.id image.id %}
{% url user_images:edit_image album_user.id image.id %}
{% url user_images:delete_image album_user.id image.id %}
{% url user_images:set_as_main_image album_user.id image.id %}
{% url user_images:clear_main_image album_user.id image.id %}
{% url user_images:reorder_images album_user.id %}
{% url user_images:set_image_order album_user.id %}
Constructor parameters:
instance_name: String. Required. App instance name for url reversing. Must be unique.
queryset: QuerySet. Required. Albums will be attached to objects in this queryset.
object_regex: String. Optional, default is ‘d+’. It should be a URL regular expression for object in URL. You should use smth. like ‘[wd-]+’ for slugs.
lookup_field: String. Optional, default is ‘pk’. It is a field name to lookup. It may contain __ and follow relations (ex.: userprofile__slug).
app_name: String. Optional, default value is ‘album’. Used by url namespaces stuff.
extra_context: Dict. Optional. Extra context that will be passed to each view.
template_object_name: String. Optional. The name of template context variable with object for which album is attached. Default is ‘object’.
has_edit_permission: Optional. Function that accepts request and object and returns True if user is allowed to edit album for object and False otherwise. Default behaviour is to always return True.
context_processors: Optional. A list of callables that will be used as additional context_processors in each view
edit_form_class: Optional. ModelForm subclass to be used in edit_image view
upload_form_class: Optional. ModelForm subclass to be used in upload_main_image view
upload_formset_class: Optional. ModelFormSet to be used in upload_images view
Templates should be placed in templates/albums/<app_name> folder. App_name should be the name of queryset model’s app as it appears in contenttypes table (e.g. ‘auth’ for User).
Each view have at least 2 variables in context:
is set in PhotoAlbumsSite constructor, default is object)
current_app: app name, ‘albums’ by default
The views included in django-photo-albums make use of 7 templates:
show_album.html displays entire album
edit_album.html displays entire album. Used by edit_album view.
Theese 3 templates have images variable in context with iterable of all images in gallery.
variables in context. prev and next are id’s of previous and next (by image.order field) images in gallery.
Formset is of PhotoFormSet type (defined in photo_albums.forms module) and is available as formset context variable.
image becomes main in gallery. Has form in context, it’s a form of type AttachedImageForm defined in generic_images.forms module.
Has image in context. Should have a form that do POST request to delete view on submit.
Views for use with PhotoAlbumSite.
django-photo-albums provides base class (photo_albums.test_utils.AlbumTest) for writing integration tests for app instances.
The example usage:
from accounts.urls import accounts_photo_site
from photo_albums import test_utils
class UserAlbumTest(test_utils.AlbumTest):
# existing user's data
username = 'obiwanus'
password = 'vasia'
# fixtures to be loaded (at least with users, images and
# objects with galleries)
fixtures = ['my_fixtures']
# app instance which is to be tested
album_site = accounts_photo_site
# we don't need edit_image view and don't create template for it
# so it should be excluded from testing
excluded_views = ['edit_image']
# id of object for which album is attached
album_for_id = 4
# if slugs are in use:
# album_for_id = 'my_object_slug'
# id's of various images: 2 images in album (second is nedded if tou want
# to test reordering) and one image in other album to test permission checks
image_in_album_id = 48
image2_in_album_id = 66
image_in_other_album_id = 42
If you don’t use fixtures you can override setUp method and create necessery objects there.