def test_after_commit_delete(self, dummy_request): from kotti.resources import Document from kotti_es.sqla import _after_delete from kotti_es.sqla import _after_commit document = Document() document.request = dummy_request _after_delete(None, None, document) import mock with mock.patch('kotti_es.sqla.get_current_request') as \ get_current_request: get_current_request.return_value = dummy_request with mock.patch('kotti_es.sqla.get_client') as \ get_client: magic = mock.MagicMock() get_client.return_value = magic session = mock.Mock() session.configure_mock( **{ 'query.return_value.filter_by.' 'return_value.one.return_value': document }) _after_commit(session) magic.delete_object.assert_called_with(document, immediate=True)
def test_multi_rename(self, root): from kotti.resources import Document from kotti.views.edit.actions import NodeActions self.setUp() root["child1"] = Document(title="Child 1") root["child2"] = Document(title="Child 2") request = DummyRequest() request.POST = MultiDict() id1 = str(root["child1"].id) id2 = str(root["child2"].id) request.POST.add("children-to-rename", id1) request.POST.add("children-to-rename", id2) request.POST.add(id1 + "-name", "") request.POST.add(id1 + "-title", "Unhappy Child") request.POST.add(id2 + "-name", "happy-child") request.POST.add(id2 + "-title", "") request.POST.add("rename_nodes", "rename_nodes") NodeActions(root, request).rename_nodes() assert request.session.pop_flash("error") == ["Name and title are required."] request.POST.add(id1 + "-name", "unhappy-child") request.POST.add(id1 + "-title", "Unhappy Child") request.POST.add(id2 + "-name", "happy-child") request.POST.add(id2 + "-title", "Happy Child") request.POST.add("rename_nodes", "rename_nodes") NodeActions(root, request).rename_nodes() assert request.session.pop_flash("success") == ["Your changes have been saved."]
def populate(): """ Create the root node (:class:`~kotti.resources.Document`) and the 'about' subnode in the nodes tree if there are no nodes yet. """ lrm = LocalizerRequestMixin() lrm.registry = get_current_registry() # noinspection PyPropertyAccess lrm.locale_name = get_settings()["pyramid.default_locale_name"] localizer = lrm.localizer if DBSession.query(Node.id).count() == 0: localized_root_attrs = { k: localizer.translate(v) for k, v in _ROOT_ATTRS.items() } root = Document(**localized_root_attrs) root.__acl__ = SITE_ACL DBSession.add(root) localized_about_attrs = { k: localizer.translate(v) for k, v in _ABOUT_ATTRS.items() } root["about"] = Document(**localized_about_attrs) wf = get_workflow(root) if wf is not None: DBSession.flush() # Initializes workflow wf.transition_to_state(root, None, "public") populate_users()
def test_multi_delete(self, root): from kotti.resources import Document from kotti.resources import File from kotti.views.edit.actions import NodeActions root["child1"] = Document(title="Child 1") root["child2"] = Document(title="Child 2") root["file1"] = File(title="File 1") request = DummyRequest() request.POST = MultiDict() id1 = str(root["child1"].id) id2 = str(root["child2"].id) id3 = str(root["file1"].id) request.POST.add("delete_nodes", "delete_nodes") NodeActions(root, request).delete_nodes() assert request.session.pop_flash("info") == ["Nothing was deleted."] request.POST.add("children-to-delete", id1) request.POST.add("children-to-delete", id2) request.POST.add("children-to-delete", id3) NodeActions(root, request).delete_nodes() assert request.session.pop_flash("success") == [ "${title} was deleted.", "${title} was deleted.", "${title} was deleted.", ]
def populate(): """ Create the root node (:class:`~kotti.resources.Document`) and the 'about' subnode in the nodes tree if there are no nodes yet. """ lrm = LocalizerRequestMixin() lrm.registry = get_current_registry() lrm.locale_name = get_settings()['pyramid.default_locale_name'] localizer = lrm.localizer if DBSession.query(Node).count() == 0: localized_root_attrs = dict( [(k, localizer.translate(v)) for k, v in _ROOT_ATTRS.iteritems()]) root = Document(**localized_root_attrs) root.__acl__ = SITE_ACL DBSession.add(root) localized_about_attrs = dict( [(k, localizer.translate(v)) for k, v in _ABOUT_ATTRS.iteritems()]) root['about'] = Document(**localized_about_attrs) wf = get_workflow(root) if wf is not None: DBSession.flush() # Initializes workflow wf.transition_to_state(root, None, u'public') populate_users()
def test_multi_rename(self, root): from kotti.resources import Document from kotti.views.edit.actions import NodeActions self.setUp() root['child1'] = Document(title="Child 1") root['child2'] = Document(title="Child 2") request = DummyRequest() request.POST = MultiDict() id1 = str(root['child1'].id) id2 = str(root['child2'].id) request.POST.add('children-to-rename', id1) request.POST.add('children-to-rename', id2) request.POST.add(id1 + '-name', '') request.POST.add(id1 + '-title', 'Unhappy Child') request.POST.add(id2 + '-name', 'happy-child') request.POST.add(id2 + '-title', '') request.POST.add('rename_nodes', 'rename_nodes') NodeActions(root, request).rename_nodes() assert request.session.pop_flash('error') ==\ ['Name and title are required.'] request.POST.add(id1 + '-name', 'unhappy-child') request.POST.add(id1 + '-title', 'Unhappy Child') request.POST.add(id2 + '-name', 'happy-child') request.POST.add(id2 + '-title', 'Happy Child') request.POST.add('rename_nodes', 'rename_nodes') NodeActions(root, request).rename_nodes() assert request.session.pop_flash('success') ==\ ['Your changes have been saved.']
def test_set_modification_date(self, root, db_session, events): from time import sleep from kotti.resources import Document # create 2 documents d1 = root['d1'] = Document(title='One') d2 = root['d2'] = Document(title='Two') assert d1.position == 0 assert d2.position == 1 db_session.flush() md1 = d1.modification_date md2 = d2.modification_date # changing positions should not update modification_date sleep(1) d1.position = 1 d2.position = 0 db_session.flush() assert d1.position == 1 assert d2.position == 0 assert d1.modification_date == md1 assert d2.modification_date == md2 # changing anything else should update modification_date d1.title = 'Eins' d2.title = 'Zwei' db_session.flush() assert d1.modification_date != md1 assert d2.modification_date != md2
def test_multi_delete(self, root): from kotti.resources import Document from kotti.resources import File from kotti.views.edit.actions import NodeActions root['child1'] = Document(title="Child 1") root['child2'] = Document(title="Child 2") root['file1'] = File(title="File 1") request = DummyRequest() request.POST = MultiDict() id1 = str(root['child1'].id) id2 = str(root['child2'].id) id3 = str(root['file1'].id) request.POST.add('delete_nodes', 'delete_nodes') NodeActions(root, request).delete_nodes() assert request.session.pop_flash('info') ==\ ['Nothing was deleted.'] request.POST.add('children-to-delete', id1) request.POST.add('children-to-delete', id2) request.POST.add('children-to-delete', id3) NodeActions(root, request).delete_nodes() assert request.session.pop_flash('success') == \ ['${title} was deleted.', '${title} was deleted.', '${title} was deleted.']
def _add_document_from_file(filename, name, parent, title, package='kotti', directory='populate-content', acl=None): body = unicode(resource_string(package, os.path.join(directory, filename))) node = Document(name=name, parent=parent, title=title, body=body) if acl is not None: node.__acl__ = acl DBSession.add(node) return node
def test_index_document_without_request(solr): doc = Document(title='No request', body=u'There is no request',) doc.id = 33 add_document_handler(event=Mock(object=doc, request=None)) results = list(solr.query(title='No request')) assert len(results) == 1 assert results[0]['id'] == 'document-33' assert results[0]['path'] == '/'
def populate(): if DBSession.query(Node).count() == 0: root = Document(**_ROOT_ATTRS) root.__acl__ = SITE_ACL DBSession.add(root) root['about'] = Document(**_ABOUT_ATTRS) populate_users()
def test_after_delete3(self, dummy_request): from kotti.resources import Document document1 = Document() document1.request = dummy_request document2 = Document() document2.request = dummy_request from kotti_es.sqla import _after_delete _after_delete(None, None, document1) _after_delete(None, None, document2) assert len(getattr(dummy_request, '_index_list')) == 2
def test_after_delete2(self, dummy_request): from kotti.resources import Document document = Document() document.id = 1 document.request = dummy_request document.request._index_list = [] from kotti_es.sqla import _after_delete from kotti_es.sqla import DELETE_CODE _after_delete(None, None, document) assert getattr(document.request, '_index_list') == [(document, document.id, DELETE_CODE)]
def test_index_document_without_request(solr): doc = Document( title='No request', body=u'There is no request', ) doc.id = 33 add_document_handler(event=Mock(object=doc, request=None)) results = list(solr.query(title='No request')) assert len(results) == 1 assert results[0]['id'] == 'document-33' assert results[0]['path'] == '/'
def test_after_insert(self, dummy_request): from kotti.resources import Document document = Document() document.id = 1 document.request = dummy_request assert hasattr(document.request, '_index_list') is False from kotti_es.sqla import _after_insert_update from kotti_es.sqla import INSERT_CODE _after_insert_update(None, None, document) assert getattr(document.request, '_index_list') == [(document, document.id, INSERT_CODE)]
def test_after_delete2(self, dummy_request): from kotti.resources import Document document = Document() document.id = 1 document.request = dummy_request document.request._index_list = [] from kotti_es.sqla import _after_delete from kotti_es.sqla import DELETE_CODE _after_delete(None, None, document) assert getattr(document.request, '_index_list') == [ (document, document.id, DELETE_CODE) ]
def test_after_insert(self, dummy_request): from kotti.resources import Document document = Document() document.id = 1 document.request = dummy_request assert hasattr(document.request, '_index_list') is False from kotti_es.sqla import _after_insert_update from kotti_es.sqla import INSERT_CODE _after_insert_update(None, None, document) assert getattr(document.request, '_index_list') == [ (document, document.id, INSERT_CODE) ]
def test_index_document(solr, db_session): now = datetime.now() doc = Document(title='foo', body=u'bar!', modification_date=now) doc.id = 23 # we don't really add the object yet... request = Mock(resource_path=lambda _: '/path/') add_document_handler(event=Mock(object=doc, request=request)) results = list(solr.query(title='foo')) assert len(results) == 1 assert results[0]['id'] == 'document-23' assert results[0]['path'] == '/path/' # Solr's date values don't take microseconds into account... assert abs(results[0]['modification_date'] - now) < timedelta(milliseconds=1)
def test_delete_document(solr, db_session): doc = Document(title=u'delete-me', description=u'foo!') doc.id = 3 request = Mock(resource_path=lambda _: '/path/') add_document_handler(event=Mock(object=doc, request=request)) results = list(solr.query(title='delete-me')) assert len(results) == 1 assert results[0]['id'] == 'document-3' assert results[0]['description'] == u'foo!' delete_document_handler(event=Mock(object=doc, request=request)) results = list(solr.query(title='delete-me')) assert len(results) == 0
def populate(): if DBSession.query(Node).count() == 0: root = Document(**_ROOT_ATTRS) root.__acl__ = SITE_ACL DBSession.add(root) root['about'] = Document(**_ABOUT_ATTRS) if DBSession.query(Settings).count() == 0: settings = Settings(data={'kotti.db_version': get_version()}) DBSession.add(settings) populate_users()
def populate(): if DBSession.query(Node).count() == 0: root = Document(**_ROOT_ATTRS) root.__acl__ = SITE_ACL DBSession.add(root) root['about'] = Document(**_ABOUT_ATTRS) wf = get_workflow(root) if wf is not None: DBSession.flush() # Initializes workflow wf.transition_to_state(root, None, u'public') populate_users()
def test_insert_delete(self, es_client, root, dummy_request, db_session): import transaction from kotti.resources import Document document = Document(title=u'mytitle', description=u'mydescription', body=u'<span>mybody</span>') document.request = dummy_request root[u'mydoc'] = document # with body=None no exceptions should be raised (utils/html_to_text) transaction.commit() es_client.flush() assert len(es_client.es.search(q='mytitle')['hits']['hits']) == 1
def test_move_up(self, root): from kotti.resources import Document from kotti.views.edit.actions import NodeActions root["child1"] = Document(title="Child 1") root["child2"] = Document(title="Child 2") assert root["child1"].position < root["child2"].position request = DummyRequest() request.session["kotti.selected-children"] = [str(root["child2"].id)] NodeActions(root, request).up() assert request.session.pop_flash("success") == ["${title} was moved."] assert root["child1"].position > root["child2"].position
def test_move_up(self, root): from kotti.resources import Document from kotti.views.edit.actions import NodeActions root['child1'] = Document(title=u"Child 1") root['child2'] = Document(title=u"Child 2") assert root['child1'].position < root['child2'].position request = DummyRequest() request.session['kotti.selected-children'] = [str(root['child2'].id)] NodeActions(root, request).up() assert request.session.pop_flash('success') ==\ [u'${title} was moved.'] assert root['child1'].position > root['child2'].position
def populate_root_document(): if DBSession.query(Node).count() == 0: root = Document(name=u'', title=u'Front Page') root.__acl__ = SITE_ACL root.default_view = 'front-page' DBSession.add(root) url = JOB_CONTAINERS['url'] root[url] = Document(title=u'Job Containers', owner=u'admin') set_groups(u'admin', root[url], set([u'role:owner'])) wf = get_workflow(root) if wf is not None: DBSession.flush() # Initializes workflow wf.transition_to_state(root, None, u'public')
def test_update_document_triggers_reindexing(solr, db_session, request): get_root()['doc'] = Document(title=u'bar', description=u'bar!') db_session.flush() results = list(solr.query(title='bar')) assert len(results) == 1 assert results[0]['id'] == u'document-2' assert results[0]['description'] == 'bar!' assert results[0]['path'] == request.resource_path(get_root()['doc']) get_root()['doc'].description = u'blah!' db_session.flush() results = list(solr.query(title='bar')) assert len(results) == 1 assert results[0]['id'] == u'document-2' assert results[0]['description'] == 'blah!' assert results[0]['path'] == request.resource_path(get_root()['doc'])
def test_contenttypefactories_with_invalid_add_link(self, config): from kotti.resources import Document, File from kotti.resources import default_type_info from kotti.views.edit import content from kotti.views.edit.actions import content_type_factories class TestContent: type_info = default_type_info.copy( name="TestContent", title="Test Content", add_view=None, addable_to=["Document"], ) config.include(content) req = DummyRequest() root = Document("") with patch("kotti.views.edit.actions.get_settings") as gs: gs.return_value = {"kotti.available_types": [TestContent, Document, File]} res = content_type_factories(root, req) assert res["factories"] == [Document, File] TestContent.type_info.add_view = "add_document" res = content_type_factories(root, req) assert res["factories"] == [TestContent, Document, File]
def test_verify_adapter(self): from pyramid_es.interfaces import IElastic from kotti_es.elastic import BaseElasticKottiContent from zope.interface.verify import verifyObject from kotti.resources import Document adapter = BaseElasticKottiContent(Document()) assert verifyObject(IElastic, adapter)
def test_insert(self, es_client, root, dummy_request, db_session): import transaction from kotti.resources import Document document = Document(title=u'mydoc', description=u'mydescription',) document.request = dummy_request root[u'mydoc'] = document # with body=None no exceptions should be raised (utils/html_to_text) transaction.commit() es_client.flush() results = es_client.es.search(q='mydescription') _type = results['hits']['hits'][0]['_type'] assert _type == 'Document' from kotti.resources import get_root root = get_root() assert results['hits']['hits'][0]['_source']['path'] == root[u'mydoc'].path
def test_contenttypefactories_with_invalid_add_link(self, config): from kotti.resources import Document, File from kotti.resources import default_type_info from kotti.views.edit import content from kotti.views.edit.actions import content_type_factories class TestContent(object): type_info = default_type_info.copy( name='TestContent', title='Test Content', add_view=None, addable_to=['Document'], ) config.include(content) req = DummyRequest() root = Document('') with patch('kotti.views.edit.actions.get_settings') as gs: gs.return_value = {'kotti.available_types': [TestContent, Document, File]} res = content_type_factories(root, req) assert res['factories'] == [Document, File] TestContent.type_info.add_view = 'add_document' res = content_type_factories(root, req) assert res['factories'] == [TestContent, Document, File]
def make_document(self, root): from kotti.resources import Document doc = root['doc'] = Document() from kotti import DBSession DBSession.flush() DBSession.refresh(doc) return doc
def make_document(self): from kotti import DBSession from kotti.resources import Document content = self.root['document'] = Document() DBSession.flush() DBSession.refresh(content) return content
def test_put(self, config): config.include("kotti.rest") req = self._make_request(config, REQUEST_METHOD="PUT") req.body = json.dumps( {"data": {"type": "Document", "attributes": {"title": u"Title here", "body": u"Body here"}}} ) doc = Document(name="parent") view = self._get_view(doc, req) resp = view(doc, req) data = resp.json_body["data"] assert resp.status == "201 Created" assert data["attributes"]["title"] == u"Title here" assert data["id"] == "title-here" assert doc.keys() == ["title-here"]
def make_document(root): from kotti import DBSession from kotti.resources import Document content = root["document"] = Document() DBSession.flush() DBSession.refresh(content) return content
def test_populate_triggers_indexing(solr, db_session): get_root()['bar'] = Document(title=u'bar', description=u'blah!') db_session.flush() results = list(solr.query(title='bar')) assert len(results) == 1 assert results[0]['id'] == 'document-2' assert results[0]['description'] == u'blah!' assert results[0]['path'] == u'/bar/'
def make_document(self, root): from kotti import DBSession from kotti.resources import Document content = root['doc'] = Document(title=u'MyDocument') DBSession.flush() DBSession.refresh(content) return content
def test_delete(self, config, db_session): config.include("kotti.rest") parent = Document(name="parent") child = Document(name="child") parent["child"] = child req = self._make_request(config, REQUEST_METHOD="DELETE") req.body = json.dumps({"data": {"id": "child", "type": "Document"}}) db_session.add(parent) view = self._get_view(child, req) assert "child" in parent.keys() resp = view(child, req) assert resp.status == "204 No Content" assert "child" not in parent.keys()
def populate(): """ Create the root node (:class:`~kotti.resources.Document`) and the 'about' subnode in the nodes tree if there are no nodes yet. """ if DBSession.query(Node).count() == 0: root = Document(**_ROOT_ATTRS) root.__acl__ = SITE_ACL DBSession.add(root) root['about'] = Document(**_ABOUT_ATTRS) wf = get_workflow(root) if wf is not None: DBSession.flush() # Initializes workflow wf.transition_to_state(root, None, u'public') populate_users()
def test_forbidden_view_webtest(self, root, webtest, db_session): from kotti.resources import Document root[u'doc'] = doc = Document(title=u'document') db_session.flush() assert doc.state == 'private' resp = webtest.get('/doc', headers={'Accept': '*/json'}, status=403) assert resp.status_code == 403 assert 'substancek_cms_theme' in resp.body
def test_contenttypefactories_add_links(self, config): from kotti.views.edit.actions import content_type_factories from kotti.resources import Document, File from kotti.views.edit import content config.include(content) res = content_type_factories(Document(''), DummyRequest()) assert res['factories'] == [Document, File]
def populate(): """ Create the root node (Document) and the 'about' subnode in the nodes tree if there are no nodes yet. """ if DBSession.query(Node).count() == 0: root = Document(**_ROOT_ATTRS) root.__acl__ = SITE_ACL DBSession.add(root) root['about'] = Document(**_ABOUT_ATTRS) wf = get_workflow(root) if wf is not None: DBSession.flush() # Initializes workflow wf.transition_to_state(root, None, u'public') populate_users()
def populate(): """ Create the root node (:class:`~kotti.resources.Document`) and the 'about' subnode in the nodes tree if there are no nodes yet. """ lrm = LocalizerRequestMixin() lrm.registry = get_current_registry() lrm.locale_name = get_settings()['pyramid.default_locale_name'] localizer = lrm.localizer if DBSession.query(Node.id).count() == 0: pkgdir = os.path.dirname(__file__) pagesdir = os.path.join(pkgdir, 'static/pages') #import pdb ; pdb.set_trace() root_filename = os.path.join(pagesdir, 'index.md') root_atts = make_markdown_attrs('', root_filename, title='Welcome to XYZZY', description='Home Page') root_atts['body'] = markdown.markdown(root_atts['body']) root = Document(**root_atts) root.__acl__ = SITE_ACL DBSession.add(root) webatts = make_markdown_attrs('webdesign', os.path.join( pagesdir, 'webdesign/index.md'), title='Web Design', description='Pages on Web Design') root['webdesign'] = MarkDownDocument(**webatts) wpages = ['history', 'development', 'stylesheets', 'javascript-components'] for wp in wpages: wpfn = os.path.join(pagesdir, 'webdesign/%s.md' % wp) wptitle = ' '.join([p.capitalize() for p in wp.split('-')]) wpatts = make_markdown_attrs(wp, wpfn, title=wptitle) root['webdesign'][wp] = MarkDownDocument(**wpatts) wf = get_workflow(root) if wf is not None: DBSession.flush() # Initializes workflow wf.transition_to_state(root, None, u'public') populate_users()
def test_put(self, config): config.include('kotti.rest') req = self._make_request(config, REQUEST_METHOD='PUT') req.body = json.dumps({ 'data': { 'type': 'Document', 'attributes': { 'title': u"Title here", 'body': u"Body here" } } }) doc = Document(name='parent') view = self._get_view(doc, req) data = view(doc, req).json_body['data'] assert data['attributes']['title'] == u'Title here' assert data['id'] == 'title-here' assert doc.keys() == ['title-here']
def test_after_commit_delete(self, dummy_request): from kotti.resources import Document from kotti_es.sqla import _after_delete from kotti_es.sqla import _after_commit document = Document() document.request = dummy_request _after_delete(None, None, document) import mock with mock.patch('kotti_es.sqla.get_current_request') as \ get_current_request: get_current_request.return_value = dummy_request with mock.patch('kotti_es.sqla.get_client') as \ get_client: magic = mock.MagicMock() get_client.return_value = magic session = mock.Mock() session.configure_mock(**{ 'query.return_value.filter_by.' 'return_value.one.return_value': document}) _after_commit(session) magic.delete_object.assert_called_with(document, immediate=True)
def populate(): session = DBSession() if session.query(Node).count() == 0: root = Document(**_ROOT_ATTRS) root.__acl__ = SITE_ACL DBSession.add(root) root.default_view = u'app' wf = get_workflow(root) if wf is not None: DBSession.flush() # Initializes workflow wf.transition_to_state(root, None, u'public') populate_users() root_document = \ session.query(Content).filter(Content.parent_id==None).first() fruit_categories_folder = \ FruitCategoriesFolder(name=u"fruit_categories_folder", title=u"Fruit Categories Folder", in_navigation=True, parent=root_document) fruit_categories_folder.__acl__ = SITE_ACL session.add(fruit_categories_folder) workflow = get_workflow(fruit_categories_folder) if workflow: DBSession.flush() workflow.transition_to_state(fruit_categories_folder, None, u'public') else: print '################ NO WORKFLOW for ', fruit_categories_folder.title folder = \ session.query(Content).filter_by( name=u"fruit_categories_folder").first() fruit_category_instances = {} for fruit_category in fruit_categories: fruit_category_instances[fruit_category] = \ FruitCategory(name=fruit_categories[fruit_category]['name'], title=fruit_categories[fruit_category]['name'], parent=folder) for key in fruit_category_instances: fruit_category_instances[key].__acl__ = SITE_ACL session.add(fruit_category_instances[key]) workflow = get_workflow(fruit_category_instances[key]) if workflow: DBSession.flush() workflow.transition_to_state(fruit_category_instances[key], None, u'public') else: print '################ NO WORKFLOW for ', fruit_category_instances[key].title fruit_instances = {} for fruit_category in fruit_categories: fruit_category_obj = \ DBSession.query(FruitCategory).filter_by( title=fruit_category).first() for fruit_name in fruit_categories[fruit_category]['fruits']: fruit_instances[fruit_name] = \ Fruit(**fruit_data_args_dict(fruit_name, fruit_category_obj)) for key in fruit_instances: fruit_instances[key].__acl__ = SITE_ACL session.add(fruit_instances[key]) workflow = get_workflow(fruit_instances[key]) if workflow: DBSession.flush() workflow.transition_to_state(fruit_instances[key], None, u'public') else: print '################ NO WORKFLOW for ', fruit_instances[key].title # Images have filenames with format: apple.256.jpg. We will use # the largest, at 512 pixels, from choices of 32, 64, 128, 256, # and 512, and let Kotti handle sizing for thumbnails. size = 512 image_filename = "{0}.{1}.jpg".format(key, size) image_path = os.path.join(os.path.dirname(images.__file__), image_filename) image = open(image_path, 'rb').read() fruit_instances[key][image_filename] = \ Image(image, image_filename, u"image/jpeg", title=image_filename) fruit_instances[key][image_filename].__acl__ = SITE_ACL session.add(fruit_instances[key][image_filename]) session.flush() transaction.commit()