mappers.py :  » Template-Engines » Myghty » Myghty-1.1 » examples » zblog » lib » zblog » database » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Template Engines » Myghty 
Myghty » Myghty 1.1 » examples » zblog » lib » zblog » database » mappers.py
"""mapper.py - defines mappers for domain objects, mapping operations"""

import zblog.database.tables as tables
import zblog.domain.user as user
from zblog.domain.blog import *
from sqlalchemy import *
import sqlalchemy.util as util

# User mapper.  Here, we redefine the names of some of the columns
# to different property names.  normally the table columns are all
# sucked in automatically.
user.User.mapper = mapper(user.User, tables.users, properties={
    'id':tables.users.c.user_id,
    'name':tables.users.c.user_name,
    'group':tables.users.c.groupname,
    'crypt_password':tables.users.c.password,
})

# blog mapper.  this contains a reference to the user mapper,
# and also installs a "backreference" on that relationship to handle it
# in both ways. this will also attach a 'blogs' property to the user mapper.
Blog.mapper = mapper(Blog, tables.blogs, properties={
    'id':tables.blogs.c.blog_id,
    'owner':relation(user.User, lazy=False, backref='blogs'),
}, is_primary=True)

# override the 'blogs' property on the user mapper to be a "private" relation,
# which means the blogs only exist as children of that user.  remove the blog
# from the user's list, it gets deleted; delete the user, the blogs get deleted.
user.User.mapper.add_property('blogs', relation(Blog.mapper, private=True, lazy=True, backref='owner')) 

# topic mapper.  map all topic columns to the Topic class.
Topic.mapper = mapper(Topic, tables.topics)
        
# TopicAssocation mapper.  This is an "association" object, which is similar to
# a many-to-many relationship except extra data is associated with each pair 
# of related data.  because the topic_xref table doesnt have a primary key,
# the "primary key" columns of a TopicAssociation are defined manually here.
TopicAssociation.mapper = mapper(TopicAssociation,tables.topic_xref, 
                primary_key=[tables.topic_xref.c.post_id, tables.topic_xref.c.topic_id], 
                properties={
                    'topic':relation(Topic.mapper, lazy=False),
                })

# Post mapper, these are posts within a blog.  
# since we want the count of comments for each post, create a select that will get the posts
# and count the comments in one query.
posts_with_ccount = select(
    [c for c in tables.posts.c if c.key != 'body'] + [
        func.count(tables.comments.c.comment_id).label('comment_count')
    ],
    from_obj = [
        outerjoin(tables.posts, tables.comments)
    ],
    group_by=[
        c for c in tables.posts.c
    ]
    ) .alias('postswcount')

# then create a Post mapper on that query.  
# we have the body as "deferred" so that it loads only when needed,
# the user as a Lazy load, since the lazy load will run only once per user and
# its usually only one user's posts is needed per page,
# the owning blog is a lazy load since its also probably loaded into the identity map
# already, and topics is an eager load since that query has to be done per post in any
# case.
Post.mapper = mapper(Post, posts_with_ccount, properties={
    'id':posts_with_ccount.c.post_id,
    'body':deferred(tables.posts.c.body),
    'user':relation(user.User, lazy=True, backref='posts'),
    'blog':relation(Blog, lazy=True, backref='posts'),
    'topics':relation(TopicAssociation, lazy=False, private=True, association=Topic)
}, is_primary=True, order_by=[desc(posts_with_ccount.c.datetime)])

# override 'posts' property on Blog to be private, so that posts get deleted when the blog does.
Blog.mapper.add_property('posts', relation(Post.mapper, private=True, lazy=True, backref='blog'))

# override 'posts' property on User to be private, so all user posts in all blogs get
# removed when the user does.
user.User.mapper.add_property('posts', relation(Post.mapper, private=True, lazy=True, backref='user'))

# comment mapper.  This mapper is handling a hierarchical relationship on itself, and contains
# a lazy reference both to its parent comment and its list of child comments.
Comment.mapper = mapper(Comment, tables.comments, properties={
    'id':tables.comments.c.comment_id,
    'post':relation(Post.mapper, lazy=True, backref='comments'),
    'user':relation(user.User.mapper, lazy=False, backref='comments'),
    'parent':relation(Comment, primaryjoin=tables.comments.c.parent_comment_id==tables.comments.c.comment_id, foreignkey=tables.comments.c.comment_id, lazy=True, uselist=False),
    'replies':relation(Comment,primaryjoin=tables.comments.c.parent_comment_id==tables.comments.c.comment_id, lazy=True, uselist=True, private=True),
}, is_primary=True)

# override the "post" and "user" backreference-generated properties to be lazy properties
Post.mapper.add_property('comments', relation(Comment.mapper, private=True, lazy=True, backref='post'))
user.User.mapper.add_property('comments', relation(Comment.mapper, private=True, lazy=True, backref='user'))

# we define one special find-by for the comments of a post, which is going to make its own "noload"
# mapper and organize the comments into their correct hierarchy in one pass. hierarchical
# data normally needs to be loaded by separate queries for each set of children, unless you
# use a proprietary extension like CONNECT BY.
def find_by_post(post):
    """returns a hierarchical collection of comments based on a given criterion.  
    uses a mapper that does not lazy load replies or parents, and instead
    organizes comments into a hierarchical tree when the result is produced.
    """
    mapper = Comment.mapper.options(noload('replies'), noload('parent'))
    comments = mapper.select_by(post_id=post.id)
    result = []
    d = {}
    for c in comments:
        d[c.id] = c
        if c.parent_comment_id is None:
            result.append(c)
            c.parent=None
        else:
            parent = d[c.parent_comment_id]
            parent.replies.append(c)
            c.parent = parent
    return result

Comment.find_by_post = staticmethod(find_by_post)

# define a bunch of convenience methods on the objectstore.

def start_session():
    """clears the objectstore, so that when a new user request is handled, all data will be
    loaded from the database completely, and anything left over from the previous session
    is removed.  Clearing the objectstore is a thread-local operation."""
    objectstore.clear()
   
# keep track of transaction token in a thread local.
# this is a compatibility hack since SQLAlchemy recently
# changed its begin/commit style to return this tranasctional token
# and the code is not keeping track of it, so we track it here
# within our own begin/commit
trans = util.ThreadLocal()

def begin():
    """begins a transaction with the objectstore."""
    trans.t = objectstore.begin()

def commit():
    """commits a transaction with the objectstore.  everything modified since the last
    begin() is updated in the database."""
    print "\n\n------------------------------\n\n"
    trans.t.commit()
    
def delete(*obj):
    """marks an object (or objects) to be deleted upon the next commit()."""
    objectstore.delete(*obj) 
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.