Django галерея - проще некуда

    Давно пользуюсь галереей photologue, но как часто случается, чужой функционал неповоротлив и избыточен. А хочется чего-то легкого и понятного. Так как ничего подходящего мною в сети не найдено, решил последовать советам бывалых и написать свой собственный велосипед.

    Открыв свою настольную книжку Django - Разработка веб-приложений на Python, внимательно изучив и "протопав" все шаги своими руками 0_о, понимаю, что книга безбожно отстает. Предложенное решение мне откровенно не нравится.

    В связи с этим начинаю обрабатывать полумёртвый код "напильником".

    Сразу выделяю ряд непонятных мне и не сработавших в моей 1.4 джанге решений автора:

1. Разбиение галереи на альбомы в модели. Мне это не надо, я делаю простой велосипед.
2. Создание собственного класса на основе ImageField c добавлением создания и воспроизведения миниатюр загружаемых изображений. Прошу не пинать ногами, но я не понимаю зачем ради такого малого изменения класса создавать целое поле, тем более, что код явно устарел, мне не понятен до конца и просто не работает.
3. Предложенный вариант удаления записей из модели галереи не удаляет файлы с физического носителя. Гуру джанги говорят, что это правильное поведение, возможно, но не в моем случае.

    В итоге размышлений и некоторых танцевальных зарисовок с нашим народным инструментом, прихожу к рабочей версии модели:

# -*- coding:utf-8 -*- 
from django.db import models
from django.contrib import admin
# Импортируем класс для создания миниатюр из PIL. Ставится отдельно
from PIL import Image 
import os

# Изменение (filename, URL) вставкой '.mini' и изменение расширения на jpg
def _add_mini(s):
    parts = s.split(".")
    parts.insert(-1, "mini")
    if parts[-1].lower() not in ['jpeg', 'jpg']:
        parts[-1] = 'jpg'
    return ".".join(parts)

# Удаление миниатюры с физического носителя.
def _del_mini(p):
    mini_path = _add_mini(p)
    if os.path.exists(mini_path):
        os.remove(mini_path)

# Основной класс модели галереи
class Photo(models.Model):
    title = models.CharField(max_length=250)
    image = models.ImageField(upload_to='gallery')
    captions = models.CharField(max_length=250, blank=True)

    class Meta:
        verbose_name = ('Иллюстрация')
        verbose_name_plural = ('Иллюстрации')
        ordering = ['title']

    def __unicode__(self):
        return self.title

# Добавляем к свойствам объектов модели путь к миниатюре
    def _get_mini_path(self):
        return _add_mini(self.image.path)
    mini_path = property(_get_mini_path)

# Добавляем к свойствам объектов модели урл миниатюры	
    def _get_mini_url(self):
        return _add_mini(self.image.url)
    mini_url = property(_get_mini_url)

# Добавляем к свойствам объектов модели html код для отображения миниатюры
# Сделано в модели для упрощения вывода в админке. Смотрим далее.	
    def get_mini_html(self):
        html = '<a class="image-picker" href="%s"><img src="%s" alt="%s"/></a>'
        return html % (self.image.url, _add_mini(self.image.url), self.captions)
    mini_html = property(get_mini_html)
    get_mini_html.short_description = ('Иллюстрация')
    get_mini_html.allow_tags = True

# Создаем свою save
# Добавляем:
# - создание миниатюры
# - удаление миниатюры и основного изображения 
#   при попытке записи поверх существующей записи
    def save(self, force_insert=False, force_update=False, using=None):
        try:
            obj =  Photo.objects.get(id=self.id)
            if obj.image.path != self.image.path:
                _del_mini(obj.image.path)
                obj.image.delete()
        except:
            pass
        super(Photo, self).save()
        img = Image.open(self.image.path)
        img.thumbnail(
            (128, 128),
            Image.ANTIALIAS
        )
        img.save(self.mini_path, 'JPEG')

# Делаем свою delete с учетом миниатюры		
    def delete(self, using=None):
        try:
            obj = Photo.objects.get(id=self.id)
            _del_mini(obj.image.path)
            obj.image.delete()
        except (Photo.DoesNotExist, ValueError):
            pass
        super(Photo, self).delete()
        
    def get_absolute_url(self):
        return ('photo_detail', None, {'object_id': self.id})

# Класс для админки с отображением миниатюры в листе изображений (get_mini_html)
# и возможностью физического, пакетного удаления 
# изображений и миниатюр (full_delete_selected)
class PhotoAdmin(admin.ModelAdmin): admin.site.disable_action('delete_selected') def full_delete_selected(self, request, obj): for o in obj.all(): o.delete() full_delete_selected.short_description = 'Удалить выбранные иллюстрации' actions = ['full_delete_selected'] list_display = ('title', 'captions', 'get_mini_html') admin.site.register(Photo, PhotoAdmin)

    На этом собственно создание простейшей галереи и заканчивается. Далее в шаблонах используем модель как обычно с учетом созданых нами свойств объектов: mini_path, mini_url и если пригодится mini_html.

    Галерея работает, но нет предела совершенству!

    Возможно я что-то упустил, поэтому если есть замечания - пишите.

13.06.2012 8:17  Ключевые словаdjango , галерея

Автор блога создает, продвигает и поддерживает сайты для бизнеса

  • Быстрая и качественная разработка сайтов/приложений
  • Качественная и продуманная SEO подготовка
  • Продвижение через Яндекс.Директ и Гугл.Адвордс


Комментарии: [18]

12:55 14.09.2015  owlman

Рекомендую с данным рецептом использовать приложение django_cleanup, чтобы Ваши диски отчищались от графического хлама.

17:36 02.03.2013  owlman

Collectstatic я не пользуюсь. В сайтах на одном хостинге предпочитаю символьные ссылки.

13:14 02.03.2013  Влад

STATICFILES_DIRS = (
'/home/user/project/public/static/',
) - но тогда у вас не получится сделать collectstatic, потому что этот путь совпадает с STATIC_ROOT. Я сейчас разбираюсь с этими настройками и нифига не понимаю :(

12:27 01.03.2013  owlman

STATICFILES_DIRS = (
'/home/user/project/public/static/',
)

17:39 28.02.2013  Влад

Пардон, а в STATICFILES_DIRS у Вас что?

10:17 23.02.2013  owlman

Проверьте пути к файлам-изображениям. Я так понимаю картинки должны лежать в папках-альбомах. Пути к мини-образам также должны быть изменены. Я думаю проблема в этом.

14:13 22.02.2013  Влад

Здравствуйте!
Сделал альбомы, добавив class Albom(models.Model): и в Photo albom = models.ForeignKey('auto.Albom'). Все нормально, но когда делаю, чтобы в админке галлереи вкладывались в альбомы class PhotoInline(admin.TabularInline): Перестает отображаться поле 'get_mini_html'. Если альбомы и галлереи отображать независимо, то поле есть. Как это можно пофиксить? Спасибо!

15:58 21.02.2013  owlman

В settings.py:

MEDIA_ROOT = '/home/user/project/public/media'
MEDIA_URL = '/media/'
STATIC_ROOT = '/home/user/project/public/static/'
STATIC_URL = '/static/'

В настройках Апача аналогично:
http://owlman.net/django/django-apache2-nginx-mod_wsgi/

15:00 21.02.2013  Влад

А в Вашем случае в какую папку сохраняется? (в смысле, как она настроена в settings)

14:34 21.02.2013  owlman

Неправильно поняли. :)
Файлы сохраняются в /media/gallery/
почему media и чем он отличается от /static/ лучше в доках джанги почитать.

Если же Вам сохранять надо именно в статику (что не очень хорошо по сображениям безопасности) поиграйте с путями в settings.py и/или с алиасами в настройках http сервера.

13:13 21.02.2013  Влад

А как сделать, чтобы фото сохранялись в папку со статическими файлами? Как я понял upload_to='gallery' создает путь относительно папки представления (там где settings.py), а не от корня проекта. А статика лежит в корень/files/static/img
Спасибо!

10:09 19.02.2013  owlman

Намекаю :)
Делайте модель связанную с фотографиями OneToMany и по аналогии с вышеописанным кодом делайте каталоги.

19:28 12.02.2013  Влад

Здравствуйте!
Намекните, пожалуйста, как все-таки сделать альбомы. Т.е. как добавить возможность создания нескольких галлерей с разными имена?
Спасибо!

13:25 26.10.2012  owlman

Очерь рад, что Вам мой пост помог. Такие комментарии хорошо мотивируют придумывать и писать дальше. Спасибо. :)

11:53 26.10.2012  Сергей

Огромнейшее спасибо!!! Я как раз начал разбираться с django и в этом посте нашел многое, что сейчас изучаю: и про миниатюры, и ! самое главное! по нормальному отображению в админке!!! спасибо автору огроменное. Побольше бы таких постов для новичков. Ничего лишнего, как раз для изучения !!!

16:14 20.06.2012  Ильнур

спасибо большое.
почитаю

13:31 18.06.2012  owlman

Спасибо за отзыв.
Про то как реализован счетчик просмотров я писал ранее в посте "Django подсчет количества просмотров".
Ссылка справа в "Самых читаемых", вторая.

13:02 18.06.2012  Ильнур

очень интересный блог.

сам начал изучать django

хотелось бы почитать, как вы реализовали функцию, количество просмотров поста.

спасибо

:)


Добавить комментарий

Внимание! HTML код и ссылки в комментариях отключены.
Комментарии содержащие ссылки, публикуются после модерации.
Имя:


e-mail:



Комментарий:
 

  
   
Λ