1

I have to submit an image to an AWS S3 bucket for our one of our models and subsequently save its URI as one of the model's fields. My current approach involves overriding the default save_model method within the ModelAdmin. I haven't gotten around to submitting the image yet, as I'm testing the override code alone.

Everything is saving just dandy except when creating a new instance and also when the user clicks "Save and continue editing". When the page tries to load again, a ValueError is raised: invalid literal for int() with base 10: 'None' from django trying to load the url ``/lift/organization/None/` instead of the primary key.

I assumed it would have something to do with the return value, but upon inspecting the source code it doesn't appear to be the case (I've also tried returning the object and the object's id). My last attempt to solve was using a redirect, which doesn't seem to have any effect.

The main problem is that I want to use the Organization's primary key as the association between the Model instance and the image within the S3 bucket so this is only a problem when the user is creating a new Organization.

Here's the code. I assume the issue is that I am not saving the model via the super method in this instance, but I'm not sure what .

obj = obj if change else lift.models.Organization(**form.cleaned_data)
            if not change:
                obj.save()
                return redirect('/lift/organization/{}/'.format(obj.id))
            super(OrganizationAdmin, self).save_model(request, obj, form, change)

UPDATE: Even in this circumstance, the model is saved correctly, it's just re-loading the page with the new model's data that is the issue.

RatherBKnitting
  • 411
  • 3
  • 14
  • Post the full traceback. But I'm guessing somewhere in your code you've `int(some_value)`. And it's rasing `ValueError` when `some_value` is `None`. Gonna need the full traceback to pinpoint what's causing it. Or [this answer](https://stackoverflow.com/a/10628018/1925257) might work. – xyres Jul 12 '17 at 22:49

1 Answers1

1

The save_model method does not have a return value and therefore returning anything from the overridden method will not have any effect.

I was approaching this problem from the wrong angle, mostly due to not knowing that the save_model method mutates to the obj variable. In understanding this I was able to come up with the following solution:

Within the overridden save_model method:

f = request.FILES.get('image', None)
if f:
    # Add the UploadedFile object as an attribute to the obj.
    obj.image_to_upload_to_s3 = f
super(OrganizationAdmin, self).save_model(request, obj, form, change)

This solved the issue of Django not being able to correctly get the url for the page to load. Notice I am no longer explicitly calling obj.save() within save_model.

Then, within the post_save signal, I added the following:

if 'instance' in kwargs:
    organization = kwargs['instance']
    if hasattr(organization, 'image_to_upload_to_s3'):
        put_to_s3_function(organization)
        url = derive_s3_url(organization)
        # Make sure to delete the attribute, or you'll recurse forever.
        delattr(organization, 'image_to_upload_to_s3')
        organization.image_url = url
        organization.save(update_fields=['image_url'])

Life is good.

RatherBKnitting
  • 411
  • 3
  • 14