Too lax unique constraints for Thumbnail coped with "on demand" thumbnail generation may leave database in inconsistent state where two thumbnail for the same size are saved. We should be able to prevent that from happening by generating all thumbnails when we save the image. Should fix #24, but I can't figure out a way to actually test it.tags/v1.1.0^2
@@ -59,7 +59,7 @@ class UserResource(ModelResource): | |||||
def filter_generator_for(size): | def filter_generator_for(size): | ||||
def wrapped_func(bundle, **kwargs): | def wrapped_func(bundle, **kwargs): | ||||
return Thumbnail.objects.get_or_create_at_size(bundle.obj.pk, size, **kwargs) | |||||
return bundle.obj.get_by_size(size) | |||||
return wrapped_func | return wrapped_func | ||||
@@ -1,10 +1,11 @@ | |||||
import requests | import requests | ||||
from cStringIO import StringIO | from cStringIO import StringIO | ||||
from django.conf import settings | |||||
from django.core.files.uploadedfile import InMemoryUploadedFile | from django.core.files.uploadedfile import InMemoryUploadedFile | ||||
from django.db import models | |||||
from django.db import models, transaction | |||||
from django_images.models import Image as BaseImage | |||||
from django_images.models import Image as BaseImage, Thumbnail | |||||
from taggit.managers import TaggableManager | from taggit.managers import TaggableManager | ||||
from ..users.models import User | from ..users.models import User | ||||
@@ -19,7 +20,13 @@ class ImageManager(models.Manager): | |||||
buf.write(response.content) | buf.write(response.content) | ||||
obj = InMemoryUploadedFile(buf, 'image', file_name, | obj = InMemoryUploadedFile(buf, 'image', file_name, | ||||
None, buf.tell(), None) | None, buf.tell(), None) | ||||
return Image.objects.create(image=obj) | |||||
# create the image and its thumbnails in one transaction, removing | |||||
# a chance of getting Database into a inconsistent state when we | |||||
# try to create thumbnails one by one later | |||||
image = self.create(image=obj) | |||||
for size in settings.IMAGE_SIZES.keys(): | |||||
Thumbnail.objects.get_or_create_at_size(image.pk, size) | |||||
return image | |||||
class Image(BaseImage): | class Image(BaseImage): | ||||
@@ -3,6 +3,7 @@ from django.contrib.auth.models import Permission | |||||
from django.core.files.images import ImageFile | from django.core.files.images import ImageFile | ||||
from django.db.models.query import QuerySet | from django.db.models.query import QuerySet | ||||
from django.test import TestCase | from django.test import TestCase | ||||
from django_images.models import Thumbnail | |||||
import factory | import factory | ||||
from taggit.models import Tag | from taggit.models import Tag | ||||
@@ -33,8 +34,15 @@ class TagFactory(factory.Factory): | |||||
class ImageFactory(factory.Factory): | class ImageFactory(factory.Factory): | ||||
FACTORY_FOR = Image | |||||
image = factory.LazyAttribute(lambda a: ImageFile(open(TEST_IMAGE_PATH, 'rb'))) | image = factory.LazyAttribute(lambda a: ImageFile(open(TEST_IMAGE_PATH, 'rb'))) | ||||
@factory.post_generation() | |||||
def create_thumbnails(self, create, extracted, **kwargs): | |||||
for size in settings.IMAGE_SIZES.keys(): | |||||
Thumbnail.objects.get_or_create_at_size(self.pk, size) | |||||
class PinFactory(factory.Factory): | class PinFactory(factory.Factory): | ||||
submitter = factory.SubFactory(UserFactory) | submitter = factory.SubFactory(UserFactory) | ||||
@@ -1,9 +1,11 @@ | |||||
from django.http import HttpResponseRedirect | from django.http import HttpResponseRedirect | ||||
from django.conf import settings | |||||
from django.core.urlresolvers import reverse | from django.core.urlresolvers import reverse | ||||
from django.views.generic import CreateView | from django.views.generic import CreateView | ||||
from django_images.models import Image | from django_images.models import Image | ||||
from braces.views import JSONResponseMixin, LoginRequiredMixin | from braces.views import JSONResponseMixin, LoginRequiredMixin | ||||
from django_images.models import Thumbnail | |||||
from .forms import ImageForm | from .forms import ImageForm | ||||
@@ -20,6 +22,8 @@ class CreateImage(JSONResponseMixin, LoginRequiredMixin, CreateView): | |||||
def form_valid(self, form): | def form_valid(self, form): | ||||
image = form.save() | image = form.save() | ||||
for size in settings.IMAGE_SIZES.keys(): | |||||
Thumbnail.objects.get_or_create_at_size(image.pk, size) | |||||
return self.render_json_response({ | return self.render_json_response({ | ||||
'success': { | 'success': { | ||||
'id': image.id | 'id': image.id | ||||