У меня две модели, Room и Image. Image - это универсальная модель, которую можно использовать с любой другой моделью. Я хочу предоставить пользователям форму для загрузки изображения, когда они публикуют информацию о комнате. Я написал код, который работает, но, боюсь, я сделал это тяжелым способом, и особенно способом, который нарушает DRY.
Надеялся, что кто-то, кто немного более знаком с формами django, сможет указать, где я ошибся.
Обновлять:
Я попытался пояснить, почему я выбрал этот дизайн, в комментариях к текущим ответам. Обобщить:
Я не просто поставил ImageField на модель Room, потому что хотел, чтобы с моделью Room было связано несколько изображений. Я выбрал общую модель изображения, потому что хотел добавить изображения к нескольким различным моделям. Альтернативами, которые я рассматривал, были несколько внешних ключей в одном классе Image, который казался беспорядочным, или несколько классов Image, которые, как я думал, загромождают мою схему. Я не прояснил это в своем первом посте, поэтому извиняюсь за это.
Поскольку ни один из ответов до сих пор не касался того, как сделать это немного более СУХИМ, я придумал свое собственное решение, которое заключалось в том, чтобы добавить путь загрузки в качестве атрибута класса в модель изображения и ссылаться на него каждый раз, когда это необходимо.
# Models
class Image(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
image = models.ImageField(_('Image'),
height_field='',
width_field='',
upload_to='uploads/images',
max_length=200)
class Room(models.Model):
name = models.CharField(max_length=50)
image_set = generic.GenericRelation('Image')
# The form
class AddRoomForm(forms.ModelForm):
image_1 = forms.ImageField()
class Meta:
model = Room
# The view
def handle_uploaded_file(f):
# DRY violation, I've already specified the upload path in the image model
upload_suffix = join('uploads/images', f.name)
upload_path = join(settings.MEDIA_ROOT, upload_suffix)
destination = open(upload_path, 'wb+')
for chunk in f.chunks():
destination.write(chunk)
destination.close()
return upload_suffix
def add_room(request, apartment_id, form_class=AddRoomForm, template='apartments/add_room.html'):
apartment = Apartment.objects.get(id=apartment_id)
if request.method == 'POST':
form = form_class(request.POST, request.FILES)
if form.is_valid():
room = form.save()
image_1 = form.cleaned_data['image_1']
# Instead of writing a special function to handle the image,
# shouldn't I just be able to pass it straight into Image.objects.create
# ...but it doesn't seem to work for some reason, wrong syntax perhaps?
upload_path = handle_uploaded_file(image_1)
image = Image.objects.create(content_object=room, image=upload_path)
return HttpResponseRedirect(room.get_absolute_url())
else:
form = form_class()
context = {'form': form, }
return direct_to_template(request, template, extra_context=context)






Почему бы вам просто не использовать ImageField? Не вижу необходимости в классе Image.
# model
class Room(models.Model):
name = models.CharField(max_length=50)
image = models.ImageField(upload_to = "uploads/images/")
# form
from django import forms
class UploadFileForm(forms.Form):
name = forms.CharField(max_length=50)
image = forms.FileField()
Взгляните на Основные загрузки файлов и Как использовать поля изображения и файла?
Я использовал отдельный класс изображений, потому что мне нужно произвольное количество изображений в модели Room. Для этого, я думаю, мне нужен как минимум отдельный класс модели с внешним ключом для Room. Он общий, потому что мне нужно произвольное количество изображений на более чем одной модели.
Вам не обязательно использовать класс Image. Как предлагается ДЗПМ, преобразовать поле изображения в ImageField. Вам также необходимо внести некоторые изменения в представление.
Вместо использования обработчика загрузки вы можете создать объект Image с загруженными данными и присоединить объект Image к объекту Room.
Чтобы сохранить объект Image, вам нужно сделать что-то вроде этого в представлении:
from django.core.files.base import ContentFile
if request.FILES.has_key('image_1'):
image_obj = Image()
image_obj.file.save(request.FILES['image_1'].name,\
ContentFile(request.FILES['image_1'].read()))
image_obj.save()
room_obj.image_set.create(image_obj)
room_obj.save()
Кроме того, я думаю, что вместо GenericRelation вы должны использовать ManyToManyField, и в этом случае синтаксис для добавления изображения в комнату немного изменится.
Мне не нужно, чтобы одно изображение было связано с несколькими комнатами, поэтому я думаю, что m2m излишний, простой внешний ключ должен подойти ... верно, ребята? Ребята?
Да, тогда используйте ForeignKey. Обновите модели в вопросе, и я расскажу, как написать представление.
Я ценю это, но привязка класса Image к одной модели - это не то, что я хочу здесь. Вопрос был в том, как сделать это СУХИМ или использовать соглашение, чтобы сделать его чище. Я решил проблему DRY (см. Обновление), а также переместил логику создания изображения из представления в метод form.save (). Работает для меня.
Как насчет использования двух форм на странице: одной для комнаты и одной для изображения?
Вам просто нужно сделать общие поля внешнего ключа формы изображения необязательными и заполнить их значения в представлении после сохранения комнаты.
Django поддерживает ваш вариант использования, по крайней мере, до определенного момента:
Общие встроенные наборы форм были введены в набор изменений [8279]. См. изменения в модульных тестах, чтобы увидеть, как они используются.
С общими встроенными наборами форм вы также сможете отображать несколько уже сохраненных изображений для существующих комнат в вашей форме.
Кажется, что встроенные наборы форм ожидают существующего родительского экземпляра в аргументе instance=. Интерфейс администратора позволяет вам заполнять строки перед сохранением родительского экземпляра, поэтому должен быть способ добиться этого. Просто я сам никогда не пробовал.
В определенный момент, когда возникла проблема, я смотрел на общие встроенные наборы форм. Они действительно кажутся мне именно тем, что я хочу использовать, но в то время я просто не мог заставить их работать. Спасибо за чаевые.
Я обнаружил, что эта страница ищет решение той же проблемы.
Вот моя информация - надеюсь, поможет.
МОДЕЛИ: Изображение, Обзор, Производитель, Профиль
Я хочу, чтобы «Обзор», «Производитель», «Профиль» были связаны с моделью «Изображение». Но у вас должна быть возможность иметь несколько изображений для каждого объекта. (То есть в одном обзоре может быть 5 изображений, в другом - 3 и т. д.)
Первоначально я сделал
images = ManyToManyField(Image)
в каждой из других моделей. Это работает нормально, но отстой для администратора (поле выбора со списком). Однако это может быть решением для вас. Мне не нравится то, что я пытаюсь сделать.
Еще одна вещь, над которой я сейчас работаю, - это наличие нескольких внешних ключей.
class Image(models.Model):
description = models.TextField(blank=True)
image = models.ImageField(upload_to = "media/")
user_profile = models.ForeignKey(UserProfile)
mfgr = models.ForeignKey(Manufacturer)
review = models.ForeignKey(Review)
но как ты сказал. Это выглядит довольно небрежно, и мне это просто не нравится.
Еще одна вещь, которую я только что обнаружил, но не полностью обернул (и не уверен, насколько она прозрачна после реализации), - это общие отношения (или общие внешние ключи), которые могут быть решением. Хорошо, когда я все это пойму. Потребность больше кофеина.
http://www.djangoproject.com/documentation/models/generic_relations/
Дайте мне знать, если вы разберетесь с этим или что-то из этого поможет. Спасибо!
Дайте мне знать, поможет ли это или у вас есть другие решения.
Хорошо, я понял это, прочитав еще немного ... Я чувствую, что вы хотите сделать именно то, что сделал я, так что вот оно.
Для этого я буду использовать GenericForeignKeys.
Сначала импорт для models.py
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
Теперь добавьте следующее в свою модель изображения
class Image(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey()
Это позволяет этой модели быть просто универсальным внешним ключом для любого количества моделей. Затем добавьте следующее ко всем моделям, для которых вы хотите иметь похожие изображения.
images = generic.GenericRelation(Image)
Теперь в admin.py нужно добавить следующие вещи.
from django.contrib.contenttypes.generic import GenericTabularInline
class ImageInline(GenericTabularInline):
model = Image
extra = 3
ct_field_name = 'content_type'
id_field_name = 'object_id'
А затем включите его в объявление администратора
class ReviewAdmin(admin.ModelAdmin):
inlines = [ImageInline]
Вот и все. Здесь отлично работает. Надеюсь, это поможет человеку! .Адам.
Если вы читаете мой исходный вопрос, это именно то, что я сделал с точки зрения схемы (вернитесь и посмотрите на мою модель изображения). Мой вариант использования был для чего-то за пределами администратора, но если бы это было в админке, да, это более или менее то, что я бы сделал.
Используйте две формы, одну для комнаты и одну для изображения:
класс Image (models.Model)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
image = models.ImageField(upload_to='')
класс UploadImage (forms.ModelForm):
class Meta:
model = Image
fields = ('image')
класс Room (models.Model):
name = models.CharField(max_length=50)
images = models.ManyToManyField(Image)
класс RoomForm (forms.ModelForm):
class Meta:
model = Room
в представлениях
если request.method == "POST":
##2 form, una per l'annuncio ed una per la fotografia
form = RoomForm(request.POST)
image_form = UploadImage(request.POST, request.FILES)
#my_logger.debug('form.is_valid() : ' + str(form.is_valid()))
if form.is_valid() and image_form.is_valid():
##save room
room = room.save()
##save image
image = image_form.save()
##ManyToMany
room.images = [image]
room.save()
Где код вашей модели?