Включите дополнительное поле в inlineformset_factory, где имеете дело с ImageField
Используя Django, я создаю эскиз изображения, загруженного с помощью стандартного ImageField, добавляя путь к таблице в отдельном поле "thumbnail" рядом с обычным полем imagefile. Я пытаюсь выяснить, как получить доступ к пути миниатюр, где отображается объект поля изображения в пользовательском шаблоне с использованием набора форм, чтобы я мог его отобразить.
Я предполагаю, что мне нужно добавить 'form=CarImageForm' в inlineformset_factory, а затем изменить свой файл forms.py, но у меня возникают проблемы с разработкой, как это сделать или даже с правильным подходом. Для ясности я не включил свои попытки сделать это в примеры кода ниже.
Моя конечная цель - вернуть уменьшенное изображение, которое ссылается на исходное изображение - уже отображается через поле Imagefile.
Заранее спасибо!
шаблон это:
<form method="post" action="" enctype="multipart/form-data">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset.forms %}
{% for field in form %}
{{ field.label }}: {{ field }}<br>
{% endfor %}
{% endfor %}
<p><input type="submit" value="Enter"/></p>
forms.py:
class CarForm(ModelForm):
class Meta:
model = Car
exclude = ['owner', 'uploaded']
views.py:
# Edit an existing record
@login_required
def edit_existing(request, object_id=False):
try:
car = Car.objects.get(pk=object_id)
except Car.DoesNotExist:
raise Http404
ImageFormSet = inlineformset_factory(Car, CarImage, extra=1, max_num=1)
if request.method == 'POST':
form = forms.CarForm(request.POST, instance=car)
formset = ImageFormSet(request.POST, request.FILES, instance=car)
if formset.is_valid() and form.is_valid():
# Handle form.save() to include user id
new_car = form.save(commit=False)
new_car.owner = request.user
new_car.save()
# Formset - contains the attached images
formset.save()
return HttpResponseRedirect(new_car.get_absolute_url())
else:
form = forms.CarForm(instance=car)
formset = ImageFormSet(instance=car)
return render_to_response('edit_existing.html',
{'form': form, 'formset': formset},
context_instance=RequestContext(request))
models.py:
class Car(models.Model):
make = models.CharField(max_length=64)
model = models.CharField(max_length=64)
owner = models.ForeignKey(User,editable=False)
uploaded = models.DateField(default=datetime.date.today,editable=False)
def get_absolute_url(self):
return reverse('vehicle_admin.views.car_detail', args=[str(self.id)])
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)
class CarImage(models.Model):
car = models.ForeignKey(Car)
imagefile = models.ImageField(upload_to=orig_car_id_folder)
thumbnail = models.ImageField(upload_to=thumb_car_id_folder, editable=False)
# PIL tips from
# https://snipt.net/danfreak/generate-thumbnails-in-django-with-pil/
# http://www.mechanicalgirl.com/post/image-resizing-file-uploads-doing-it-easy-way/
def save(self):
import os
from PIL import Image
from cStringIO import StringIO
from django.core.files.uploadedfile import SimpleUploadedFile
THUMBNAIL_SIZE = (75, 75)
super(CarImage, self).save() # Use the commit=False param here?
image = Image.open(self.imagefile.path)
if image.mode not in ('L', 'RGB'):
image = image.convert('RGB')
image.thumbnail(THUMBNAIL_SIZE, Image.ANTIALIAS)
temp_handle = StringIO()
image.save(temp_handle, 'png')
temp_handle.seek(0)
name_ext = os.path.splitext(os.path.split(self.imagefile.name)[-1])
suf = SimpleUploadedFile(name_ext[0],
temp_handle.read(), content_type='image/png')
self.thumbnail.save(suf.name+'.png', suf, save=False)
super(CarImage, self).save()
2 ответа
Я думаю, что это основной упущение с моей стороны.
Моя проблема была установка editable=False в моей модели CarImage для поля 'thumbnail'. Это хорошо для первоначальной отправки данных, но вызывает проблемы, когда редактирование записи, так как это поле автоматически исключается из формы. (См. Мои фрагменты кода выше для получения дополнительной информации.)
Моим следующим шагом будет настройка пользовательской формы для моего представления add_new (я изначально не включал это представление, но именно это я использовал для включения начальной загрузки данных), например так:
class InitialCarImageForm(ModelForm):
class Meta:
model = CarImage
exclude = ['thumbnail']
Я предполагаю, что правильный подход - использовать editable=False, но если я не совершу серьезную ошибку, применяя этот подход, он, вероятно, будет работать для меня.
Я новичок на сайте, поэтому, пожалуйста, дайте мне знать, если правильный ответ на мой вопрос - это правильный способ закрыть его.
Если вы хотите попробовать использовать мою библиотеку django-extra-views, вы можете сделать что-то вроде этого:
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from extra_views import UpdateWithInlinesView, InlineFormSet
class CarForm(ModelForm):
def __init__(self, *args, **kwargs):
self.user = self.kwargs.pop('user')
super(CarForm, self).__init__(*args, **kwargs)
def save(self, commit=True)
instance = super(CarForm, self).save(commit=False)
instance.user = self.user
if commit:
instance.save()
return instance
class Meta:
model = Car
exclude = ['owner', 'uploaded']
CarImageInline(InlineFormSet):
model = CarImage
extra = 1
max_num = 1
UpdateCarView(UpdateWithInlinesView):
pk_url_kwarg = 'object_id'
inlines = [CarImageInline]
template_name = 'edit_existing.html'
form_class = CarForm
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(UpdateCarView, self).dispatch(*args, **kwargs)
def get_form_kwargs(self):
kwargs = super(UpdateCarView, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def get_success_url(self):
return self.object.get_absolute_url()
Ключевые отличия:
- Форма становится ответственной за назначение пользователя автомобилю, форма захватывает пользователя, когда он создается (см. Get_form_kwargs). Причина для этого заключается в том, что в представлениях на основе классов переопределение form_valid (или в этом случае forms_valid) немного неуклюже, поэтому я предпочитаю помещать вещи в форму.
- Вместо получения
formset
в вашем контексте вы получаетеinlines
, который является списком форм. Это потому, что технически вы можете иметь столько встроенных наборов форм, сколько захотите, даже если в этом случае у вас есть только один, он фактически такой же, как работает администратор Django. - Переопределение
dispatch
применитьlogin_required
Декоратор неуклюжий, к счастью, это что-то, что легко абстрагируется в миксин, см. django-скобки для некоторых готовых.