Давно пользуюсь галереей 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.
Галерея работает, но нет предела совершенству!
Возможно я что-то упустил, поэтому если есть замечания - пишите.
Комментарии
'/home/user/project/public/static/',
) - но тогда у вас не получится сделать collectstatic, потому что этот путь совпадает с STATIC_ROOT. Я сейчас разбираюсь с этими настройками и нифига не понимаю :(
'/home/user/project/public/static/',
)
Сделал альбомы, добавив class Albom(models.Model): и в Photo albom = models.ForeignKey('auto.Albom'). Все нормально, но когда делаю, чтобы в админке галлереи вкладывались в альбомы class PhotoInline(admin.TabularInline): Перестает отображаться поле 'get_mini_html'. Если альбомы и галлереи отображать независимо, то поле есть. Как это можно пофиксить? Спасибо!
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/
Файлы сохраняются в /media/gallery/
почему media и чем он отличается от /static/ лучше в доках джанги почитать.
Если же Вам сохранять надо именно в статику (что не очень хорошо по сображениям безопасности) поиграйте с путями в settings.py и/или с алиасами в настройках http сервера.
Спасибо!
Делайте модель связанную с фотографиями OneToMany и по аналогии с вышеописанным кодом делайте каталоги.
Намекните, пожалуйста, как все-таки сделать альбомы. Т.е. как добавить возможность создания нескольких галлерей с разными имена?
Спасибо!
почитаю
Про то как реализован счетчик просмотров я писал ранее в посте "Django подсчет количества просмотров".
Ссылка справа в "Самых читаемых", вторая.
сам начал изучать django
хотелось бы почитать, как вы реализовали функцию, количество просмотров поста.
спасибо
:)