sqlalchemypyramiddeform

Image uploads with Pyramid and SQLAlchemy


How one should do image file uploads with Pyramid, SQLAlchemy and deform? Preferably so that one can easily get image thumbnail tags in the templates. What configuration is needed (store images on the file system backend, so on).


Solution

  • This question is by no means specifically asking one thing. Here however is a view which defines a form upload with deform, tests the input for a valid image file, saves a record to a database, and then even uploads it to amazon S3. This example is shown under the links to the various documentation I have referenced. To upload a file with deform see the documentation.

    If you want to learn how to save image files to disk, see this article see the official documentation

    Then if you want to learn how to save new items with SQLAlchemy see the SQLAlchemy tutorial.

    If you want to ask a better question where a more precise detailed answer can be given for each section, then please do so.

    @view_config(route_name='add_portfolio_item', 
    renderer='templates/user_settings/deform.jinja2',
    permission='view')
    def add_portfolio_item(request):
    
        user = request.user
        # define store for uploaded files
        class Store(dict):
            def preview_url(self, name):
                return ""
    
        store = Store()
        # create a form schema
        class PortfolioSchema(colander.MappingSchema):
            description = colander.SchemaNode(colander.String(),
                validator = Length(max=300),
                widget = text_area,
                title = "Description, tell us a few short words desribing your picture")
            upload = colander.SchemaNode(
                    deform.FileData(),
                    widget=widget.FileUploadWidget(store))
    
        schema = PortfolioSchema()
        myform = Form(schema, buttons=('submit',), action=request.url)
    
        # if form has been submitted
        if 'submit' in request.POST:
    
            controls = request.POST.items()
            try:
                appstruct = myform.validate(controls)
            except ValidationFailure, e:
                return {'form':e.render(), 'values': False}
            # Data is valid as far as colander goes
    
            f = appstruct['upload']
            upload_filename = f['filename']
            extension = os.path.splitext(upload_filename)[1]
            image_file = f['fp']
    
            # Now we test for a valid image upload
            image_test = imghdr.what(image_file)
            if image_test == None:
                error_message = "I'm sorry, the image file seems to be invalid is invalid"
                return {'form':myform.render(), 'values': False, 'error_message':error_message,
                'user':user}
    
    
            # generate date and random timestamp
            pub_date = datetime.datetime.now()
            random_n = str(time.time())
    
            filename = random_n + '-' + user.user_name + extension
            upload_dir = tmp_dir
            output_file = open(os.path.join(upload_dir, filename), 'wb')
    
            image_file.seek(0)
            while 1:
                data = image_file.read(2<<16)
                if not data:
                    break
                output_file.write(data)
            output_file.close()
    
            # we need to create a thumbnail for the users profile pic
            basewidth = 530
            max_height = 200
            # open the image we just saved
            root_location = open(os.path.join(upload_dir, filename), 'r')
            image = pilImage.open(root_location)
            if image.size[0] > basewidth:
                # if image width greater than 670px
                # we need to recduce its size
                wpercent = (basewidth/float(image.size[0]))
                hsize = int((float(image.size[1])*float(wpercent)))
                portfolio_pic = image.resize((basewidth,hsize), pilImage.ANTIALIAS)
            else:
                # else the image can stay the same size as it is
                # assign portfolio_pic var to the image
                portfolio_pic = image
    
            portfolio_pics_dir = os.path.join(upload_dir, 'work')
    
            quality_val = 90
            output_file = open(os.path.join(portfolio_pics_dir, filename), 'wb')
            portfolio_pic.save(output_file, quality=quality_val)
    
            profile_full_loc = portfolio_pics_dir + '/' + filename
    
            # S3 stuff
            new_key = user.user_name + '/portfolio_pics/' + filename
            key = bucket.new_key(new_key)
            key.set_contents_from_filename(profile_full_loc)
            key.set_acl('public-read')
    
            public_url = key.generate_url(0, query_auth=False, force_http=True)
    
            output_dir = os.path.join(upload_dir)
            output_file = output_dir + '/' + filename
            os.remove(output_file)
            os.remove(profile_full_loc)
    
            new_image = Image(s3_key=new_key, public_url=public_url,
             pub_date=pub_date, bucket=bucket_name, uid=user.id,
             description=appstruct['description'])
            DBSession.add(new_image)
            # add the new entry to the association table.
            user.portfolio.append(new_image)
    
            return HTTPFound(location = route_url('list_portfolio', request))
    
        return dict(user=user, form=myform.render())