Добавление универсального поля изображения в ModelForm в Django - PullRequest
3 голосов
/ 22 января 2009

У меня есть две модели, Room и Image. Image - это общая модель, которая может быть привязана к любой другой модели. Я хочу дать пользователям форму для загрузки изображения, когда они публикуют информацию о комнате. Я написал код, который работает, но я боюсь, что я сделал это трудным путем, и особенно таким образом, который нарушает DRY.

Надеялся, что кто-то, кто немного более знаком с формами Джанго, сможет указать, где я ошибся.

Обновление:

Я попытался уточнить, почему я выбрал этот дизайн в комментариях к текущим ответам. Подведем итог:

Я не просто поместил ImageField на модель 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)

Ответы [ 7 ]

4 голосов
/ 22 января 2009

Почему бы вам просто не использовать 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()

Взгляните на Основные загрузки файлов и Как использовать поля изображений и файлов?

2 голосов
/ 22 января 2009

Вам не нужно использовать класс Image. Как предложено DZPM , преобразуйте поле изображения в 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, и в этом случае синтаксис добавления изображения в комнату изменится незначительно.

0 голосов
/ 08 марта 2010

Используйте две формы, одну для комнаты и одну для изображения:

Изображение класса (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')

классная комната (models.Model):

name = models.CharField(max_length=50)
images = models.ManyToManyField(Image) 

класс RoomForm (forms.ModelForm):

class Meta:
    model = Room

в просмотрах

if 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()
0 голосов
/ 15 февраля 2009

Хорошо, я понял это, прочитав еще немного ... Я чувствую, что вы хотите сделать именно то, что я сделал, вот оно.

Я буду использовать 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]

И это все. Здесь отлично работает. Надеюсь, это поможет человеку! .adam.

0 голосов
/ 15 февраля 2009

Я нашел, что эта страница ищет решение этой же проблемы.

Вот моя информация - надеюсь, поможет некоторым.

МОДЕЛИ: изображение, обзор, производитель, профиль

Я хочу, чтобы обзор, производитель, профиль имели отношение к модели изображения. Но вы должны иметь возможность иметь несколько изображений на объект. (То есть один обзор может иметь 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)

но, как вы сказали. Это выглядит довольно неряшливо, и мне просто не нравится.

Еще одна вещь, которую я только что обнаружил, но мой мозг не полностью обернут (и не уверен, насколько прозрачной она является после реализации, это Generic Relationships (или Generic Foreign Keys), которая может быть решением. все. Нужно больше кофеина.

http://www.djangoproject.com/documentation/models/generic_relations/

Дайте мне знать, если вы разберетесь с этим или что-то из этого поможет. Спасибо!

Дайте мне знать, если это поможет или у вас есть другие решения.

0 голосов
/ 10 февраля 2009

Django поддерживает ваш вариант использования, по крайней мере, до определенного момента:

  • формы отображают повторяющиеся формы
  • модельные наборы обрабатывают повторные модельные формы
  • встроенные наборы форм связывают наборы моделей с соответствующими объектами экземпляра
  • универсальные встроенные наборы форм делают то же самое для родовых отношений

Общие наборы встроенных форм были введены в changeset [8279] . См. изменения в модульных тестах , чтобы увидеть, как они используются.

С помощью встроенных наборов встроенных форм вы также сможете отображать несколько уже сохраненных изображений для существующих комнат в вашей форме.

Встроенные наборы форм, похоже, ожидают существующего родительского экземпляра в аргументе instance=. Интерфейс администратора позволяет заполнять строки перед сохранением родительского экземпляра, поэтому для этого должен быть способ. Я просто никогда не пробовал это сам.

0 голосов
/ 10 февраля 2009

Как насчет использования двух форм на странице: одна для комнаты и одна для изображения?

Вам просто нужно будет сделать необязательными поля общего внешнего ключа формы изображения и заполнить их значения в представлении после сохранения комнаты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...