Skip to content

Commit

Permalink
make Image model swappable using a Meta.swappable option
Browse files Browse the repository at this point in the history
  • Loading branch information
skirsdeda authored and stefanfoulis committed Jul 25, 2017
1 parent 60c15fe commit dba731c
Show file tree
Hide file tree
Showing 21 changed files with 141 additions and 124 deletions.
2 changes: 1 addition & 1 deletion docs/extending_filer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ In your own application, you need to create a Video model. This model has to inh
pass # for now...
When a file is uploaded, :py:meth:`filer.admin.clipboardadmin.ClipboardAdmin.ajax_upload` loops over the different classes in ``filer.settings.FILER_FILE_MODELS`` and calls its ``matches_file_type()`` to see if the file matches a known filename extension.
When a file is uploaded, :py:meth:`filer.admin.clipboardadmin.ClipboardAdmin.ajax_upload` loops over the different models in ``filer.settings.FILER_FILE_MODELS`` and calls its ``matches_file_type()`` to see if the file matches a known filename extension.

When a match is found, the filer will create an instance of that class for the file.

Expand Down
9 changes: 7 additions & 2 deletions filer/admin/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
# -*- coding: utf-8 -*-

from django.contrib import admin

from ..models import Clipboard, File, Folder, FolderPermission, ThumbnailOption
from ..settings import FILER_IMAGE_MODEL
from ..utils.loader import load_model
from .clipboardadmin import ClipboardAdmin
from .fileadmin import FileAdmin
from .folderadmin import FolderAdmin
from .imageadmin import ImageAdmin
from .thumbnailoptionadmin import ThumbnailOptionAdmin
from .permissionadmin import PermissionAdmin
from ..models import FolderPermission, Folder, File, Clipboard, Image, ThumbnailOption
from .thumbnailoptionadmin import ThumbnailOptionAdmin

Image = load_model(FILER_IMAGE_MODEL)


admin.site.register(Folder, FolderAdmin)
Expand Down
4 changes: 2 additions & 2 deletions filer/admin/clipboardadmin.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
handle_request_files_upload,
handle_upload,
)
from ..utils.loader import load_object
from ..utils.loader import load_model

NO_FOLDER_ERROR = "Can't find folder to upload. Please refresh and try again"
NO_PERMISSIONS_FOR_FOLDER = (
Expand Down Expand Up @@ -95,7 +95,7 @@ def ajax_upload(request, folder_id=None):

# find the file type
for filer_class in filer_settings.FILER_FILE_MODELS:
FileSubClass = load_object(filer_class)
FileSubClass = load_model(filer_class)
# TODO: What if there are more than one that qualify?
if FileSubClass.matches_file_type(filename, upload, request):
FileForm = modelform_factory(
Expand Down
6 changes: 4 additions & 2 deletions filer/admin/folderadmin.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,11 @@
Folder,
FolderPermission,
FolderRoot,
Image,
ImagesWithMissingData,
UnsortedImages,
tools,
)
from ..settings import FILER_PAGINATE_BY
from ..settings import FILER_IMAGE_MODEL, FILER_PAGINATE_BY
from ..thumbnail_processors import normalize_subject_location
from ..utils.compatibility import (
capfirst,
Expand All @@ -45,6 +44,7 @@
unquote,
)
from ..utils.filer_easy_thumbnails import FilerActionThumbnailer
from ..utils.loader import load_model
from .forms import CopyFilesAndFoldersForm, RenameFilesForm, ResizeImagesForm
from .patched.admin_utils import get_deleted_objects
from .permissions import PrimitivePermissionAwareModelAdmin
Expand All @@ -59,6 +59,8 @@
userperms_for_request,
)

Image = load_model(FILER_IMAGE_MODEL)


class AddFolderPopupForm(forms.ModelForm):
folder = forms.HiddenInput()
Expand Down
5 changes: 4 additions & 1 deletion filer/admin/imageadmin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
from django.utils.translation import ugettext as _
from django.utils.translation import string_concat, ugettext_lazy

from ..models import Image
from ..settings import FILER_IMAGE_MODEL
from ..thumbnail_processors import normalize_subject_location
from ..utils.loader import load_model
from .fileadmin import FileAdmin

Image = load_model(FILER_IMAGE_MODEL)


class ImageAdminForm(forms.ModelForm):
subject_location = forms.CharField(
Expand Down
4 changes: 2 additions & 2 deletions filer/fields/image.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import

from ..models import Image
from .. import settings
from .file import AdminFileFormField, AdminFileWidget, FilerFileField


Expand All @@ -15,4 +15,4 @@ class AdminImageFormField(AdminFileFormField):

class FilerImageField(FilerFileField):
default_form_class = AdminImageFormField
default_model_class = Image
default_model_class = settings.FILER_IMAGE_MODEL
6 changes: 4 additions & 2 deletions filer/management/commands/import_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@

from ...models.filemodels import File
from ...models.foldermodels import Folder
from ...models.imagemodels import Image
from ...settings import FILER_IS_PUBLIC_DEFAULT
from ...settings import FILER_IMAGE_MODEL, FILER_IS_PUBLIC_DEFAULT
from ...utils.compatibility import upath
from ...utils.loader import load_model

Image = load_model(FILER_IMAGE_MODEL)


class FileImporter(object):
Expand Down
44 changes: 21 additions & 23 deletions filer/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,27 +143,25 @@ class Migration(migrations.Migration):
field=models.ForeignKey(related_name='filer_clipboards', verbose_name='user', to=settings.AUTH_USER_MODEL),
preserve_default=True,
),
]
if not FILER_IMAGE_MODEL:
operations.append(
migrations.CreateModel(
name='Image',
fields=[
('file_ptr', models.OneToOneField(serialize=False, auto_created=True, to='filer.File', primary_key=True, parent_link=True)),
('_height', models.IntegerField(null=True, blank=True)),
('_width', models.IntegerField(null=True, blank=True)),
('date_taken', models.DateTimeField(verbose_name='date taken', null=True, editable=False, blank=True)),
('default_alt_text', models.CharField(max_length=255, null=True, verbose_name='default alt text', blank=True)),
('default_caption', models.CharField(max_length=255, null=True, verbose_name='default caption', blank=True)),
('author', models.CharField(max_length=255, null=True, verbose_name='author', blank=True)),
('must_always_publish_author_credit', models.BooleanField(default=False, verbose_name='must always publish author credit')),
('must_always_publish_copyright', models.BooleanField(default=False, verbose_name='must always publish copyright')),
('subject_location', models.CharField(default=None, max_length=64, null=True, verbose_name='subject location', blank=True)),
],
options={
'verbose_name': 'image',
'verbose_name_plural': 'images',
},
bases=('filer.file',),
)
migrations.CreateModel(
name='Image',
fields=[
('file_ptr', models.OneToOneField(serialize=False, auto_created=True, to='filer.File', primary_key=True, parent_link=True)),
('_height', models.IntegerField(null=True, blank=True)),
('_width', models.IntegerField(null=True, blank=True)),
('date_taken', models.DateTimeField(verbose_name='date taken', null=True, editable=False, blank=True)),
('default_alt_text', models.CharField(max_length=255, null=True, verbose_name='default alt text', blank=True)),
('default_caption', models.CharField(max_length=255, null=True, verbose_name='default caption', blank=True)),
('author', models.CharField(max_length=255, null=True, verbose_name='author', blank=True)),
('must_always_publish_author_credit', models.BooleanField(default=False, verbose_name='must always publish author credit')),
('must_always_publish_copyright', models.BooleanField(default=False, verbose_name='must always publish copyright')),
('subject_location', models.CharField(default=None, max_length=64, null=True, verbose_name='subject location', blank=True)),
],
options={
'swappable': 'FILER_IMAGE_MODEL',
'verbose_name': 'image',
'verbose_name_plural': 'images',
},
bases=('filer.file',),
)
]
18 changes: 7 additions & 11 deletions filer/migrations/0004_auto_20160328_1434.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,17 @@

from django.db import migrations, models

from filer.settings import FILER_IMAGE_MODEL


class Migration(migrations.Migration):

dependencies = [
('filer', '0003_thumbnailoption'),
]

if not FILER_IMAGE_MODEL:
# Only attempt to alter the Image field if we're actually using it.
operations = [
migrations.AlterField(
model_name='image',
name='subject_location',
field=models.CharField(blank=True, default='', max_length=64, verbose_name='subject location'),
),
]
operations = [
migrations.AlterField(
model_name='image',
name='subject_location',
field=models.CharField(blank=True, default='', max_length=64, verbose_name='subject location'),
),
]
17 changes: 7 additions & 10 deletions filer/migrations/0006_auto_20160623_1627.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,17 @@
from django.db import migrations, models
import django.db.models.deletion

from filer.settings import FILER_IMAGE_MODEL


class Migration(migrations.Migration):

dependencies = [
('filer', '0005_auto_20160623_1425'),
]

if not FILER_IMAGE_MODEL:
operations = [
migrations.AlterField(
model_name='image',
name='file_ptr',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='%(app_label)s_%(class)s_file', serialize=False, to='filer.File'),
),
]
operations = [
migrations.AlterField(
model_name='image',
name='file_ptr',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='%(app_label)s_%(class)s_file', serialize=False, to='filer.File'),
),
]
78 changes: 33 additions & 45 deletions filer/models/imagemodels.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,54 +10,42 @@
from django.utils.timezone import get_current_timezone, make_aware, now
from django.utils.translation import ugettext_lazy as _

from .. import settings as filer_settings
from ..utils.compatibility import GTE_DJANGO_1_10
from ..utils.loader import load_object
from .abstract import BaseImage

logger = logging.getLogger("filer")


if not filer_settings.FILER_IMAGE_MODEL:
# This is the standard Image model
class Image(BaseImage):
date_taken = models.DateTimeField(_('date taken'), null=True, blank=True,
editable=False)
author = models.CharField(_('author'), max_length=255, null=True, blank=True)
must_always_publish_author_credit = models.BooleanField(_('must always publish author credit'), default=False)
must_always_publish_copyright = models.BooleanField(_('must always publish copyright'), default=False)

class Meta(object):
app_label = 'filer'
verbose_name = _('image')
verbose_name_plural = _('images')
if GTE_DJANGO_1_10:
default_manager_name = 'objects'

def save(self, *args, **kwargs):
if self.date_taken is None:
try:
exif_date = self.exif.get('DateTimeOriginal', None)
if exif_date is not None:
d, t = exif_date.split(" ")
year, month, day = d.split(':')
hour, minute, second = t.split(':')
if getattr(settings, "USE_TZ", False):
tz = get_current_timezone()
self.date_taken = make_aware(datetime(
int(year), int(month), int(day),
int(hour), int(minute), int(second)), tz)
else:
self.date_taken = datetime(
int(year), int(month), int(day),
int(hour), int(minute), int(second))
except Exception:
pass
if self.date_taken is None:
self.date_taken = now()
super(Image, self).save(*args, **kwargs)

else:
# This is just an alias for the real model defined elsewhere
# to let imports works transparently
Image = load_object(filer_settings.FILER_IMAGE_MODEL)
# This is the standard Image model which can be swapped for a custom model using FILER_IMAGE_MODEL setting
class Image(BaseImage):
date_taken = models.DateTimeField(_('date taken'), null=True, blank=True,
editable=False)
author = models.CharField(_('author'), max_length=255, null=True, blank=True)
must_always_publish_author_credit = models.BooleanField(_('must always publish author credit'), default=False)
must_always_publish_copyright = models.BooleanField(_('must always publish copyright'), default=False)

class Meta(BaseImage.Meta):
swappable = 'FILER_IMAGE_MODEL'

def save(self, *args, **kwargs):
if self.date_taken is None:
try:
exif_date = self.exif.get('DateTimeOriginal', None)
if exif_date is not None:
d, t = exif_date.split(" ")
year, month, day = d.split(':')
hour, minute, second = t.split(':')
if getattr(settings, "USE_TZ", False):
tz = get_current_timezone()
self.date_taken = make_aware(datetime(
int(year), int(month), int(day),
int(hour), int(minute), int(second)), tz)
else:
self.date_taken = datetime(
int(year), int(month), int(day),
int(hour), int(minute), int(second))
except Exception:
pass
if self.date_taken is None:
self.date_taken = now()
super(Image, self).save(*args, **kwargs)
7 changes: 4 additions & 3 deletions filer/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@

logger = logging.getLogger(__name__)

FILER_IMAGE_MODEL = getattr(settings, 'FILER_IMAGE_MODEL', False)
# This represents the actual Image model (as opposed to FILER_IMAGE_MODEL global setting which could be
# undefined)
FILER_IMAGE_MODEL = getattr(settings, 'FILER_IMAGE_MODEL', 'filer.Image')
FILER_DEBUG = getattr(settings, 'FILER_DEBUG', False) # When True makes
FILER_SUBJECT_LOCATION_IMAGE_DEBUG = getattr(settings, 'FILER_SUBJECT_LOCATION_IMAGE_DEBUG', False)
FILER_WHITESPACE_COLOR = getattr(settings, 'FILER_WHITESPACE_COLOR', '#FFFFFF')
Expand Down Expand Up @@ -52,8 +54,7 @@
# classes that I should check for when adding files
FILER_FILE_MODELS = getattr(
settings, 'FILER_FILE_MODELS',
(FILER_IMAGE_MODEL if FILER_IMAGE_MODEL else 'filer.models.imagemodels.Image',
'filer.models.filemodels.File'))
(FILER_IMAGE_MODEL, 'filer.File'))

DEFAULT_FILE_STORAGE = getattr(settings, 'DEFAULT_FILE_STORAGE', 'django.core.files.storage.FileSystemStorage')

Expand Down
4 changes: 2 additions & 2 deletions filer/test_utils/test_app/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

class Migration(migrations.Migration):

if FILER_IMAGE_MODEL:
if FILER_IMAGE_MODEL.startswith('custom_image'):
dependencies = [
('filer', '0001_initial'),
('custom_image', '0001_initial'),
Expand All @@ -27,7 +27,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('folder', filer.fields.folder.FilerFolderField(related_name='test_folder', to='filer.Folder')),
('general', filer.fields.file.FilerFileField(related_name='test_file', to='filer.File')),
('image', filer.fields.image.FilerImageField(related_name='test_image', to=FILER_IMAGE_MODEL or 'filer.Image')),
('image', filer.fields.image.FilerImageField(related_name='test_image', to=FILER_IMAGE_MODEL)),
],
options={
},
Expand Down
Loading

0 comments on commit dba731c

Please sign in to comment.