将一般图像字段添加到django中的ModelForm上

时间:2009-01-22 03:45:49

标签: python django django-forms

我有两个模型,RoomImageImage是一个可以应用于任何其他模型的通用模型。我想为用户提供一个表单,用于在发布有关房间的信息时上传图像。我已经编写了有效的代码,但我担心我已经采取了严厉的措施,特别是以违反DRY的方式。

希望对django形式更熟悉的人可以指出我出错的地方。

更新

我试图澄清为什么我在评论中选择这个设计来回答当前的答案。总结一下:

我没有简单地在ImageField模型上放置Room,因为我想要一个与Room模型相关联的多个图像。我选择了一个通用的Image模型,因为我想将图像添加到几个不同的模型中。我考虑的替代方案是单个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 个答案:

答案 0 :(得分:4)

为什么不使用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()

查看Basic file uploadsHow do I use image and file fields?

答案 1 :(得分:2)

您不必使用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,在这种情况下,将图像添加到会议室的语法会略有变化。

答案 2 :(得分:0)

如何在页面上使用两个表单:一个用于房间,一个用于图像?

您只需要将图像格式的通用外键字段设置为不需要,并在保存房间后在视图中填写它们的值。

答案 3 :(得分:0)

Django确实至少支持你的用例:

  • formsets显示重复的表单
  • 模型表单集处理重复的模型表单
  • 内联表单集将模型表单集绑定到实例的相关对象
  • 通用内联表单集对通用关系执行相同的操作

changeset [8279]中引入了通用内联表单集。请参阅the changes to unit tests以了解它们的使用方式。

使用通用内联表单集,您还可以为表单中的现有会议室显示多个已保存的图像。

内联表单集似乎期望instance=参数中存在父实例。管理界面确实允许您在保存父实例之前填写内联,因此必须有一种方法来实现它。我自己从未尝试过。

答案 4 :(得分:0)

我发现这个页面寻找解决同样问题的方法。

这是我的信息 - 希望有所帮助。

模特:图片,评论,制造商,资料

我希望Review,Manufacturer,Profile与Image模型有关系。但是你必须能够为每个对象提供多个图像。 (即,一个评论可以有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/

如果您对此进行整理或任何有帮助,请告诉我。谢谢!

请告诉我这是否有帮助,或者您有不同的解决方案。

答案 5 :(得分:0)

好的,我想通过更多的阅读来解决这个问题......我觉得你想要做的就是我所做的,所以在这里。

我将使用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。

答案 6 :(得分:0)

使用两种形式,一种用于房间,一种用于图像:

类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')

class Room(models.Model):

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

class 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()