django-generic-images is a generic images pluggable django app.
This app provides image model (useful managers, methods and fields) that can be attached to any other Django model using generic relations. It also provides admin multi-image uploader (based on GearsUploader ) with client-side image resizing, animated progress bar and before-upload image previews.
Requirements: django 1.1 (or trunk).
django-composition is required if you want to use ImageCountField or UserImageCountField.
There is an image gallery app (django-photo-albums) based on django-generic-images.
$ pip install django-generic-images
or:
$ easy_install django-generic-images
or:
$ hg clone http://bitbucket.org/kmike/django-generic-images/
$ cd django-generic-images
$ python setup.py install
Then add ‘generic_images’ to your INSTALLED_APPS in settings.py and run
$ manage.py syncdb
If you want ImageCountField and UserImageCountField then follow installation instructions at http://bitbucket.org/daevaorn/django-composition/ to install django-composition.
For admin uploader to work generic_images folder from generic_images/media/ should be copied to project’s MEDIA_ROOT.
The idea is to provide an infrastructure for images that can be attached to any django model using generic relations.
Bases: generic_images.models.ReplaceOldImageModel, generic_utils.models.GenericModelBase
Abstract Image model that can be attached to any other Django model using generic relations.
Override this in proxy subclass to customize upload path. Default upload path is /media/images/<user.id>/<filename>.<ext> or /media/images/common/<filename>.<ext> if user is not set.
<filename> is returned by get_file_name() method. By default it is probable id of new image (it is predicted as it is unknown at this stage).
Simple abstract Model class with image field.
Bases: generic_images.models.BaseImageModel
Abstract Model class with image field. If the file for image is re-uploaded, old file is deleted.
Form for AttachedImage model to be used in inline admin
alias of _AttachedImageAdminForm
InlineModelAdmin for attached images. Adds multi-image uploader with progress bar, before-upload image previews and client-side resizing. Uploader is based on GearsUploader (http://bitbucket.org/kmike/gearsuploader/) project.
To make this work copy generic_images folder from generic_images/media/ to your MEDIA_ROOT. Then use AttachedImagesInline class for you inlines:
#admin.py
from django.contrib import admin
from generic_images.admin import AttachedImagesInline
class MyModelAdmin(admin.ModelAdmin):
inlines = [AttachedImagesInline]
admin.site.register(MyModel, MyModelAdmin)
Just before standard formset the following uploader is displayed:
Gears plugin is here
Message is displayed if Gears plugin is not available
User can select several files at once using Ctrl or Shift keys (Cmd on Mac) in standard OS file selection dialog. He can also remove images from selection by clicking on thumbnails. Several files can also be selected by opening file selection dialog several times.
User presses ‘Upload’ button and upload process begins
By default the ‘Resize ..’ checkbox is unchecked and the input field is blank. If it is unchecked then images are not resized before uploading. User can check it and set his max image width.
In order to set the default value and mark the checkbox as checked by default create customized AttachedImagesInline class using attachedimages_inline_factory() function. This function can also be used to change uploader language (language auto-discovering is not implemented):
from generic_images.admin import attachedimages_inline_factory
class MyModelAdmin(admin.ModelAdmin):
inlines = [attachedimages_inline_factory(lang='ru', max_width=1024)]
admin.site.register(MyModel, MyModelAdmin)
alias of _AttachedImagesInline
Bases: generic_utils.managers.GenericModelManager
Manager with helpful functions for attached images
Useful manager for models that have AttachedImage (or subclass) field and ‘injector=GenericIngector()’ manager.
Bases: django.forms.models.ModelForm
Simple form for AttachedImage model with image and caption fields.
django-generic-images provides fields for storing information about attached images count. Value is stored in model that images are attached to. Value is updated automatically when image is saved or deleted. Access to this value is much faster than additional “count()” queries.
Field with model’s attached images count. Value of this field is updated automatically when image is added or removed. Access to this field doesn’t produce additional ‘select count(*)’ query, data is stored in table.
Example 1:
from generic_images.fields import ImageCountField
class MyModel1(models.Model):
#... fields definitions
image_count = ImageCountField()
Example 2:
class MyModel2(models.Model):
#... fields definitions
image_count = ImageCountField(native=models.IntegerField(u'MyModel2 Images count', default=0))
Field that should be put into user’s profile (AUTH_PROFILE_MODULE). It will contain number of images that are attached to corresponding User.
This field is useful when you want to use something like ImageCountField for User model. It is not possible to add a field to User model without duck punching (monkey patching). UserImageCountField should be put into user’s profile (same model as defined in AUTH_PROFILE_MODULE). It will contain number of images that are attached to corresponding User. FK attribute to User model is considered 'user' by default, but this can be overrided using user_attr argument to UserImageCountField constructor. As with ImageCountField, UserImageCountField constructor accepts also native argument - an underlying field.
Recalculate all ImageCountField and UserImageCountField fields in object obj.
This should be used if auto-updating of these fields was disabled for some reason.
To disable auto-update when saving AttachedImage instance (for example when you need to save a lot of images and want to recalculate denormalised values only after all images are saved) use this pattern:
image = AttachedImage(...)
image.send_signal = False
image.save()
Base class for reusable apps. The approach is similar to django AdminSite. For usage case please check photo_albums app.
Make regex string for PluggableSite urlpatterns: prepend url with parent object’s url and app name.
See also: http://code.djangoproject.com/ticket/11559.
This method should return url patterns (like urlpatterns variable in urls.py). It is helpful to construct regex with make_regex() method. Example:
return patterns('photo_albums.views',
url(
self.make_regex('/'),
'show_album',
{'album_site': self},
name = 'show_album',
),
)
Use it in urls.py. Example:
urlpatterns += patterns('', url(r'^my_site/', include(my_pluggable_site.urls)),)
It is a function that returns decorator factory useful for PluggableSite views. This decorator factory returns decorator that do some boilerplate work and make writing PluggableSite views easier. It passes PluggableSite instance to decorated view, retreives and passes object that site is attached to and passes common context. It also passes and all the decorator factory’s keyword arguments.
For example usage please check photo_albums.views.
Btw, this decorator seems frightening for me. It feels that “views as PluggableSite methods” approach can easily make this decorator obsolete. But for now it just works.
Abstract base class for models that will be attached using generic relations.
RelatedInjector but for GenericForeignKey’s. Manager for selecting all generic-related objects in one (two) SQL queries. Selection is performed for a list of objects. Resulting data is aviable as attribute of original model. Only one instance per object can be selected. Example usage: select (and make acessible as user.avatar) all avatars for a list of user when avatars are AttachedImage’s attached to User model with is_main=True attributes.
Example:
from django.contrib.auth.models import User
from generic_images.models import AttachedImage
users = User.objects.all()[:10]
AttachedImage.injector.inject_to(users, 'avatar', is_main=True)
# i=0..9: users[i].avatar is AttachedImage objects with is_main=True.
# If there is no such AttachedImage (user doesn't have an avatar),
# users[i].avatar is None
One can reuse GenericInjector manager for other models that are supposed to be attached via generic relationship. It can be considered as an addition to GFKmanager and GFKQuerySet from djangosnippets for different use cases.
field_name is the attached object attribute name
All other kwargs will be passed as arguments to queryset filter function.
Example: you have a list of comments. Each comment has ‘user’ attribute. You want to fetch 10 comments and their authors with avatars. Avatars should be accessible as user.avatar:
comments = Comment.objects.all().select_related('user')[:10]
AttachedImage.injector.inject_to(comments, 'avatar', lambda obj: obj.user, is_main=True)
Manager with for_model method.
Manager that can emulate select_related fetching reverse relations using 1 additional SQL query.
field_name is the attached object attribute name
All other kwargs will be passed as arguments to queryset filter function.
For example, we need to prefetch user profiles when we display a list of comments:
# models.py
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
info = models.CharField(max_length=100)
objects = models.Manager()
injector = RelatedInjector(fk_field='user')
# views.py
def show_comments(request, obj_id):
...
comments = list(Comment.objects.for_model(obj).select_related('user'))
UserProfile.injector.inject_to(comments, '_profile_cache',
lambda comment: comment.user)
return direct_to_template('comment_list.html', {'comments': comments})
# in comment_list.html
{% for comment in comments %}
<h3>{{ comment.user }}</h3>
<h4>{{ comment.user.get_profile.info }}</h4>
{{ comment.comment|linebreaks }}
{% endfor %}
comment.user attribute will be selected using select_related and comment.user._profile_cache (exposed by get_profile method) will be selected by our injector. So there will be only 2 SQL queries for selecting all comments with users and user profiles.
TestCase for view testing