google-code-export / django-photologue

Automatically exported from code.google.com/p/django-photologue
BSD 3-Clause "New" or "Revised" License
0 stars 0 forks source link

enhancement: admin field/widget for related Photo fields in other models #108

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
I figured how to add a admin_thumbnail preview to models which add
ForeignKey(Photo) and ManyToManyPhoto(Photo). I got the tip from
django-tinymce (Joost Cassee). Does not depend on any external APIs.
I have no diff - my pl is too hacked.

######################## photologue/widgets.py (new)
{{{
from django import forms
from django.contrib.admin import widgets as admin_widgets
from django.utils.translation import ugettext as _
#from django.core.urlresolvers import reverse
from django.utils.safestring import mark_safe
from models import PhotoSize

LOADING = _('loading...')

def multiClick(name,to_from):
  # consider using reverse() here
  return u"""addEvent(%(to_from)s,"click",function(e){var option;
for (var i = 0; (option = %(to_from)s.options[i]); i++) { if
(option.selected){lastOpt=option;}}
%(name)s_preview.src="";%(name)s_preview.alt="%(loading)s";
%(name)s_preview.src="/photologue/photo/preview/"+lastOpt.value+"/";});
""" % {"name":name,"to_from":to_from,'loading':LOADING}

class ManyToManyImages(admin_widgets.FilteredSelectMultiple):
    """
    Appends image preview scripting to each ManyToManyField which relates
to Photo model.
    Requires 'filter_horizontal' or 'filter_vertical' in ModelAdmin
    """
    def render(self, name, value, attrs=None,choices=()):
        output = [super(ManyToManyImages,
self).render(name,value,attrs,choices)]
        try:
          thumbnail_admin = PhotoSize.objects.get(name='admin_thumbnail')
          size = thumbnail_admin.size
        except:
          size = [100,66]
        output.append(u"""<script type="text/javascript">addEvent(window,
"load", function(e){
var from_box= document.getElementById("id_%(name)s_from");
var to_box=document.getElementById("id_%(name)s_to"); var
%(name)s_preview=document.getElementById("%(name)s_preview");
%(from_box)s
%(to_box)s
});</script>
<img id="%(name)s_preview" src="" alt="%(alt)s" width="%(width)s"
height="%(height)s" border="1" align="right" />
""" % {
'name':name,'from_box':multiClick(name,'from_box'),
'to_box':multiClick(name,'to_box'),'alt':_('preview image'),
'width':size[0], 'height':size[1]
})
        return mark_safe(u''.join(output))

class
ForeignPhotoWidget(admin_widgets.ForeignKeyRawIdWidget):#(forms.TextInput):
    """
    A Widget for displaying ForeignKeys related to Photo objects in the
"raw_id" interface rather than
    in a <select> box. Requires 'raw_id_fields' in admin.ModelAdmin
    """
    def render(self, name, value, attrs=None):
        try:
            thumbnail_admin = PhotoSize.objects.get(name='admin_thumbnail')
            size = thumbnail_admin.size
        except:
            size = [100,66]
        output=[u"""<script type="text/javascript">addEvent(window, "load",
function (e) {
var box = document.getElementById("id_%(name)s");var %(name)s_preview =
document.getElementById("%(name)s_preview");
addEvent(%(name)s_preview,"click",function (e) {
  %(name)s_preview.src="";%(name)s_preview.alt="%(loading)s";
  %(name)s_preview.src="/photologue/photo/preview/"+box.value+"/";
});
});
</script><img id="%(name)s_preview" src="" alt="%(alt)s" width="%(width)s"
height="%(height)s" border="1" align="right" />
""" % {'name':name,'alt':_('preview image - click to
update'),'width':size[0],'height':size[1],'loading':LOADING}]
        output.append(super(ForeignPhotoWidget, self).render(name,
value,attrs))
        return mark_safe(u''.join(output))

class AdminForeignPhotoWidget(ForeignPhotoWidget,forms.Select):
  pass

class AdminManyToManyPhotosWidget(ManyToManyImages,forms.SelectMultiple):
  pass
}}}

#################### photologue/views.py
{{{
# it's a bit lazy, I suppose there could be a better alternative to a redirect

from models import Photo
from django.http import HttpResponseRedirect

def photo(request, id=None):
  img = Photo.objects.get(id=id)
  response = HttpResponseRedirect(img.get_admin_thumbnail_url())
  return response

}}}

#################### append to photologue/urls.py
{{{
urlpatterns += patterns('photologue.views',
    url(r'^photo/preview/(?P<id>[\d]*)/$','photo',name="photo_image"),
)
}}}

#################### append to photologue/models.py
{{{
from photologue import widgets as photo_widgets
from django.db.models.fields.related import ManyToOneRel

class ForeignPhotoField(models.ForeignKey):
    """
    A raw input field which looks up a related Photo.
    """
    def __init__(self, to_field=None, rel_class=ManyToOneRel, **kwargs):

super(ForeignPhotoField,self).__init__(Photo,to_field,rel_class,**kwargs)
    def formfield(self, **kwargs):
        defaults = {'widget': photo_widgets.ForeignPhotoWidget(self.rel)}
        defaults.update(kwargs)
        defaults['widget'] = photo_widgets.AdminForeignPhotoWidget(self.rel)
        return super(ForeignPhotoField, self).formfield(**defaults)

class ManyToManyPhotosField(models.ManyToManyField):
    def __init__(self, **kwargs):
        super(ManyToManyPhotosField,self).__init__(Photo,**kwargs)

    def formfield(self, **kwargs):
        defaults = {'widget':
photo_widgets.ManyToManyImages(self.verbose_name,False)}
        defaults.update(kwargs)
        defaults['widget'] =
photo_widgets.AdminManyToManyPhotosWidget(self.verbose_name,False)
        return super(ManyToManyPhotosField, self).formfield(**defaults)
}}}

Usage:
In your models, a field referring a m2m relationship to Photo would normally be
{{{
from photologue import Photo
.
.
images = models.ManyToManyField(Photo,...
}}}
replace with 
{{{
from photologue.models import ManyToManyPhotosField, Photo
.
.
images = ManyToManyPhotosField(... # yes, do drop the Photo reference
}}}

The same way, 
{{{
from photologue.models import Photo
.
.
image = models.ForeignKey(Photo,..
}}}

would be replaced with
{{{
from photologue.models import Photo, ForeignPhotoField
.
.
image = ForeignPhotoField(...
}}}

You could also implement it at ModelAdmin level:
{{{
class MyModelAdmin(admin.ModelAdmin):
    def formfield_for_manytomany(self, db_field, request=None, **kwargs):
        field = super(MyModelAdmin,
self).formfield_for_manytomany(db_field, request, **kwargs)
        if db_field.name == 'images':
            field.widget =
ManyToManyImages(db_field.verbose_name,(db_field.name in self.filter_vertical))
        return field
}}}

Original issue reported on code.google.com by cardhuen...@gmail.com on 29 Mar 2009 at 10:09

Attachments: