I have two models, Room and Image.  Image is a generic model that can tack onto any other model.  I want to give users a form to upload an image when they post information about a room.  I've written code that works, but I'm afraid I've done it the hard way, and specifically in a way that violates DRY.
Was hoping someone who's a little more familiar with django forms could point out where I've gone wrong.
Update:
I've tried to clarify why I chose this design in comments to the current answers. To summarize:
I didn't simply put an ImageField on the Room model because I wanted more than one image associated with the Room model.  I chose a generic Image model because I wanted to add images to several different models.  The alternatives I considered were were multiple foreign keys on a single Image class, which seemed messy, or multiple Image classes, which I thought would clutter my schema.  I didn't make this clear in my first post, so sorry about that.
Seeing as none of the answers so far has addressed how to make this a little more DRY I did come up with my own solution which was to add the upload path as a class attribute on the image model and reference that every time it's needed.
# 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)
Why don't you just use ImageField? I don't see the need for the Image class.
# 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()
Take a look at Basic file uploads and How do I use image and file fields?
You don't have to use the Image class. As DZPM suggested, convert the image field to an ImageField. You also need to make some changes to the view.
Instead of using an upload handler, you can create a Image object with the uploaded data and attach the Image object to the Room object.
To save the Image object you need to do something like this in the view:
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()
Also, I think instead of the GenericRelation, you should use a ManyToManyField, in which case the syntax for adding an Image to a Room will change slightly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With