arnar -

great program, im working through it.

the first thing i notice, is that the initial INSERTs didnt seem quite
right.  when i added this code beneath the initial
j2.pages[..].add_version() calls:

print j2.pages
print j2.pages[0].versions
print j2.pages[1].versions
sys.exit()

it was printing this:

[<__main__.Page object at 0x106f0d0>, <__main__.Page object at
0x106f0d0>, <__main__.Page object at 0x106f170>, <__main__.Page object
at 0x106f170>, <__main__.Page object at 0x106f130>, <__main__.Page
object at 0x106f130>]
[<__main__.PageVersion object at 0x106f190>, <__main__.PageVersion
object at 0x106f1d0>, <__main__.PageVersion object at 0x106f2d0>,
<__main__.PageVersion object at 0x106f310>]
[<__main__.PageVersion object at 0x106f190>, <__main__.PageVersion
object at 0x106f1d0>, <__main__.PageVersion object at 0x106f2d0>,
<__main__.PageVersion object at 0x106f310>]

which is 6 pages !  I identified the cause of this as being a "double
append" of a backreference operation, because you are both setting the
parent "job" of "page" in the page constructor, as well as appending
the "page" to the "job" in the job.create_page() method.  I commented
out the "self.pages.append(p)" call in job.create_page to fix this.
another way to address this would be to have the "pages" collection be
a Set instead of a plain list.  (earlier versions of SA enforced
uniqueness on collections, but this is no longer the case)

next I noticed the "somejob" name was not spelled correctly in the
initial Job creation.  that is the cause of the first stack trace.

fixing that, we had a deeper error in the UOW.  Because the "Page"
class' primary key is derived from "jobno", when it was getting deleted
it was also getting the "jobno" attribute set to "None".  the resulting
"post_update" call for its "PageVersion" was then failing because it no
longer knew the primary key of the parent Page object.  I made a
modification to the "attribute synchronization" so that it will never
set a primary key-holding attribute of an instance to None.  get rev
1981 of the trunk for that.

then it worked in SQLite.  this had to be tested in postgres, which is
strict about foreign key enforcement.  to test in postgres, the
composite foreign keys had to be specified properly, using
ForeignKeyConstraint.  after that, the whole program completes.
attached to this email.


# encoding: utf-8

import os
from datetime import datetime

from sqlalchemy import *

#db = create_engine('sqlite://', echo=True)
db = create_engine('postgres://scott:[EMAIL PROTECTED]/test', echo=True)
__meta__ = BoundMetaData(db)

def constructor(fun):
    def decorated(self, *args, **kw):
        assert hasattr(self, 'c')
        for key,value in kw.items():
            if hasattr(self.c, key):
                setattr(self, key, value)
                del kw[key]
        fun(self, *args, **kw)
    return decorated


# Jobs eru job frá rippanum (ekki bókhaldsforritum). Sjá jobtickets

jobs = Table("jobs", __meta__,
                Column("jobno", Unicode(15), primary_key=True),
                Column("created", DateTime, nullable=False,
default=datetime.now),
                Column("deleted", Boolean, nullable=False,
default=False))

class Job(object):

    @constructor
    def __init__(self, jobno=None):
        if jobno:
            self.jobno = jobno

    def create_page(self, pagename, *args, **kwargs):
        p = Page(job=self, pagename=pagename, *args, **kwargs)
        #self.pages.append(p)
        return p

mapper(Job, jobs)


pageversions = Table("pageversions", __meta__,
                Column("jobno", Unicode(15), primary_key=True),
                Column("pagename", Unicode(30), primary_key=True),
                Column("version", Integer, primary_key=True,
default=1),
                Column("created", DateTime, nullable=False,
default=datetime.now),
                Column("md5sum", String(32)),
                Column("width", Integer, nullable=False, default=0),
                Column("height", Integer, nullable=False, default=0),
                ForeignKeyConstraint(["jobno", "pagename"],
["pages.jobno", "pages.pagename"])
                )

class PageVersion(object):

    @constructor
    def __init__(self, page=None, version=None):
        if page:
            self.page = page
        if version:
            self.version = version

    @property
    def filename(self):
        if self.version == 1:
            return self.page.pagename + '.jpg'
        else:
            return "%s.v%02d.jpg" % (self.page.pagename, self.version)

mapper(PageVersion, pageversions)


pages = Table("pages", __meta__,
                Column("jobno", Unicode(15), ForeignKey("jobs.jobno"),
primary_key=True),
                Column("pagename", Unicode(30), primary_key=True),
                Column("created", DateTime, nullable=False,
default=datetime.now),
                Column("deleted", Boolean, nullable=False,
default=False),
                Column("current_version", Integer))

class Page(object):

    @constructor
    def __init__(self, job=None, pagename=None):
        if job:
            self.job = job
        if pagename:
            self.pagename = pagename
        self.currentversion = PageVersion(self, 1)

    def __repr__(self):
        return "Page jobno:%s pagename:%s %s" % (self.jobno,
self.pagename, getattr(self, '_instance_key', None))
    def add_version(self):
        self.currentversion = PageVersion(self,
self.currentversion.version+1)
        comment = self.add_comment()
        comment.closeable = False
        comment.content = u'Ný útgáfa rippuð'
        return self.currentversion

    def get_version(self, versionno):
        return self.versions[versionno-1]

    def add_comment(self, user_name=None):
        nextnum = max([-1] + [c.comment_id for c in self.comments]) + 1
        newcomment = PageComment()
        newcomment.comment_id = nextnum
        self.comments.append(newcomment)
        newcomment.created_by = user_name
        newcomment.created_version = self.currentversion.version
        return newcomment

    def get_comment(self, comment_id):
        return self.comments[comment_id]

    def get_next_page(self):
        myindex = self.job.pages.index(self)
        if myindex < len(self.job.pages)-1:
            return self.job.pages[myindex+1]
        else:
            return None

    def get_previous_page(self):
        myindex = self.job.pages.index(self)
        if myindex > 0:
            return self.job.pages[myindex-1]
        else:
            return None

mapper(Page, pages, properties={
    'job': relation(Job, backref=backref('pages', cascade="all,
delete-orphan", order_by=pages.c.pagename)),
    'currentversion': relation(PageVersion,
                    foreignkey=pages.c.current_version,

primaryjoin=and_(pages.c.jobno==pageversions.c.jobno,

pages.c.pagename==pageversions.c.pagename,

pages.c.current_version==pageversions.c.version),
                    post_update=True),
    'versions': relation(PageVersion, cascade="all, delete-orphan",

primaryjoin=and_(pages.c.jobno==pageversions.c.jobno,

pages.c.pagename==pageversions.c.pagename),
                    order_by=pageversions.c.version,
                    backref=backref('page', lazy=False,

primaryjoin=and_(pages.c.jobno==pageversions.c.jobno,

pages.c.pagename==pageversions.c.pagename)))
})


pagecomments = Table("pagecomments", __meta__,
    Column("jobno", Unicode(15), primary_key=True),
    Column("pagename", Unicode(30), primary_key=True),
    Column("comment_id", Integer, primary_key=True),
    Column("created", DateTime, nullable=False, default=datetime.now),
    Column("created_version", Integer, nullable=False),
    Column("created_by", Unicode(255)),
    Column("closed", DateTime),
    Column("closed_version", Integer),
    Column("closed_by", Unicode(255)),
    Column("content", Unicode),
    Column("closeable", Boolean, nullable=False, default=True),  #
notað f. log-entries
    ForeignKeyConstraint(["jobno", "pagename"], ["pages.jobno",
"pages.pagename"])

)

class PageComment(object):

    @constructor
    def __init__(self):
        super(PageComment,self).__init__()
        self.closeable = True

    def close(self, user_name=None):
        if not self.closeable:
            raise Exception("This comment is marked as unclosable.")
        self.closed = datetime.now()
        self.closed_by = user_name
        self.closed_version = self.page.currentversion.version

mapper(PageComment, pagecomments, properties={
    'page': relation(Page,
primaryjoin=and_(pages.c.jobno==pagecomments.c.jobno,

pages.c.pagename==pagecomments.c.pagename),
                        backref=backref("comments", cascade="all,
delete-orphan",

primaryjoin=and_(pages.c.jobno==pagecomments.c.jobno,

pages.c.pagename==pagecomments.c.pagename),

order_by=pagecomments.c.comment_id))
})

__meta__.drop_all()
__meta__.create_all()
import sys

j1 = Job('somejob')
j1.create_page('page1')
j1.create_page('page2')
j1.create_page('page3')

j2 = Job('somejob2')
j2.create_page('page1')
j2.create_page('page2')
j2.create_page('page3')

j2.pages[0].add_version()
j2.pages[0].add_version()
j2.pages[1].add_version()
print j2.pages
print j2.pages[0].versions
print j2.pages[1].versions
s = create_session(echo_uow=True)

s.save(j1)
s.save(j2)
s.flush()

print "\n\n-------------------\n\n"
s.clear()
j = s.query(Job).get_by(jobno='somejob')
oldp = list(j.pages)
j.pages = []

s.flush()

print "\n\n-------------------\n\n"

s.clear()
j = s.query(Job).get_by(jobno='somejob2')
j.pages[1].current_version = 12
s.delete(j)
s.flush()


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to [EMAIL PROTECTED]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at http://groups.google.com/group/sqlalchemy
-~----------~----~----~----~------~----~------~--~---

Reply via email to