Problema al guardar la imagen en Django usando CreateView e inlineformset_factory

Usando el CreateView genérico basado en clases, intento cargar una imagen a través de un widget ClearableFileInput, que es el widget predeterminado que usa inlineformset_factory, pero está fallando.

El form.save() funciona bien en mi opinión, es el specimage_form.save() el que parece fallar. Si imprimo self.request.FILES durante el envío, parece que el archivo seleccionado está en la memoria, pero al incluir una declaración de impresión en la función SpecImage save(), está claro que nunca se llama a esta función.

Es posible cargar una imagen usando el sitio de administración usando líneas en cuyo caso veo la declaración de impresión.

Aquí está mi código: agradezco cualquier consejo u orientación. Gracias de antemano..

modelos.py

class Spec(models.Model):
    car = models.ForeignKey('vehicles_dvla_listpoint.AutoLookup')
    owner = models.ForeignKey(User)
    uploaded = models.DateField(default=date.today, editable=False)

    def get_absolute_url(self):
        return reverse('car_create')

    def __unicode__(self):
        return "{0}".format(self.car)

class SpecImage(models.Model):
    def orig_car_id_folder(instance, filename):
        return 'uploads/images/orig/{0}/{1}'.format(instance.car_id, filename)
    def thumb_car_id_folder(instance, filename):
        return 'uploads/images/thumb/{0}/{1}'.format(instance.car_id, filename)

    car = models.ForeignKey(Spec)

    orig_image = models.ImageField(
        upload_to=orig_car_id_folder, 
        verbose_name='Upload Image',
    )

    thumbnail = models.ImageField(
        upload_to=thumb_car_id_folder,
        null=True,
        blank=True,
    )

    def save(self, force_update=False, force_insert=False): 
        print "here ...." # << Don't see this where submitting outside of the admin
        import os
        from PIL import Image
        from cStringIO import StringIO
        from django.core.files.uploadedfile import SimpleUploadedFile
        # Set thumbnail size
        THUMBNAIL_SIZE = (75, 75)
        # Process original image using PIL
        image = Image.open(self.orig_image)           
        # Convert to RGB if necessary
        if image.mode not in ('L', 'RGB'):
            image = image.convert('RGB')
        # PIL already has constraint proportions
        # Also use Image.ANTIALIAS to make the image look better
        image.thumbnail(THUMBNAIL_SIZE, Image.ANTIALIAS)
        # Save thumbnail - to disk?
        temp_handle = StringIO()
        image.save(temp_handle, 'png')
        temp_handle.seek(0)
        # Save to thumbnail field (in table)
        # Prepare file name - just name & strip .ext 
        name_ext = os.path.splitext(os.path.split(self.orig_image.name)[-1])
        suf = SimpleUploadedFile(name_ext[0],
                temp_handle.read(), content_type='image/png')
        self.thumbnail.save(suf.name+'.png', suf, save=False)
        # Save this photo instance (again)
        super(SpecImage, self).save()

urls.py

urlpatterns = patterns('',
    url(r'^add/$', CarCreate.as_view(), name='car_create'),
    url(r'^thanks/$', TemplateView.as_view(template_name='thanks.html')),
)

formularios.py

import autocomplete_light
from django.forms.models import inlineformset_factory

from .models import Spec, SpecImage

class SpecForm(autocomplete_light.ModelForm):
    class Meta:
        model = Spec

SpecImageFormSet = inlineformset_factory(Spec, SpecImage, extra=1)

vistas.py

class CarCreate(CreateView):
    template_name = 'spec_form_inlines.html'
    model = Spec
    form_class = SpecForm
    success_url = '/car/thanks/'

    def form_valid(self, form):
        context = self.get_context_data()
        specimage_form = context['specimage_form']
        if specimage_form.is_valid():
            self.object = form.save()
            specimage_form.instance = self.object
#            specimage_form.instance = self.request.FILES
            specimage_form.save()
            return HttpResponseRedirect('/car/thanks/')
        else:
            return self.render_to_response(self.get_context_data(form=form))

    def form_invalid(self, form):
        return self.render_to_response(self.get_context_data(form=form))

    def get_context_data(self, **kwargs):
        context = super(CarCreate, self).get_context_data(**kwargs)
        if self.request.POST:
            context['specimage_form'] = SpecImageFormSet(self.request.POST)
        else:
            context['specimage_form'] = SpecImageFormSet()
    return context

spec_form_inlines.html

<html>
<body>

<h1>Add Book</h1>

<form enctype="multipart/form-data" method="post" action="">
    {% csrf_token %}

    {{ form.as_p }}

    {{ specimage_form.management_form }}
    {% for form in specimage_form.forms %}
        {{ form.as_p }}
    {% endfor %}

    <input type="submit" value="Add Car" class="submit"/>
</form>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.js" type="text/javascript"></script>
{% include 'autocomplete_light/static.html' %}

</body>
</html>

Soy bastante nuevo con las Vistas basadas en clases. Logré lograr esto con las vistas de funciones la última vez, pero parecen una forma antigua de hacer las cosas y, a veces, no es una transición obvia. Cualquier consejo u orientación será muy bienvenido.

Gracias.

preguntado el 14 de febrero de 14 a las 01:02

1 Respuestas

Así que esto parece ser una solución bastante simple, pero me tomó un tiempo resolver el problema revisando los ejemplos en línea y leyendo los documentos de Django, así que lo dejo aquí en lugar de eliminar la pregunta.

Mi código inicial se basó en esta entrada del blog.

A menos que me esté perdiendo algo, a pesar de ser un ejemplo que incluye la carga de una imagen, lo que parece faltar es una referencia a request.FILES para vincular las imágenes. Entonces, el cambio simple que ahora parece funcionar es cambiar esta línea:

if self.request.POST:
  context['specimage_form'] = SpecImageFormSet(self.request.POST)

a este

if self.request.POST:
  context['specimage_form'] = SpecImageFormSet(self.request.POST, self.request.FILES)

Se puede encontrar un ejemplo adicional que usa render_to_response en lugar de establecer un elemento de contexto aquí

De manera similar, se debe hacer una referencia adicional a request.FILES para cualquier formset que tenga un ImageField.

Ambos enfoques parecen funcionar bien.

Más información sobre vincular imágenes a un formulario está en el Documentos de Django.

Respondido 15 Feb 14, 15:02

No hay comentarios durante casi una semana, así que acepto esto como respuesta. - jayuu

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.