Problema al guardar la imagen en Django usando CreateView e inlineformset_factory
Frecuentes
Visto 2,324 equipos
1
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.
1 Respuestas
7
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 es la respuesta que estás buscando? Examinar otras preguntas etiquetadas django django-class-based-views django-generic-views or haz tu propia pregunta.
No hay comentarios durante casi una semana, así que acepto esto como respuesta. - jayuu