Defining the Domain Model¶
The first change we’ll make to our stock paster-generated application will be
to define a domain model constructor representing a wiki page. We’ll
do this inside our models.py
file.
The source code for this tutorial stage can be browsed at http://github.com/Pylons/pyramid/tree/1.2-branch/docs/tutorials/wiki2/src/models/.
Making Edits to models.py
¶
Note
There is nothing automagically special about the filename
models.py
. A project may have many models throughout its
codebase in arbitrarily-named files. Files implementing models
often have model
in their filenames (or they may live in a
Python subpackage of your application package named models
) ,
but this is only by convention.
The first thing we want to do is remove the stock MyModel
class from the
generated models.py
file. The MyModel
class is only a sample and
we’re not going to use it.
Next, we’ll remove the sqlalchemy.Unicode
import and replace it
with sqlalchemy.Text
.
1 | from sqlalchemy import Text
|
Then, we’ll add a Page
class. Because this is a SQLAlchemy
application, this class should inherit from an instance of
sqlalchemy.ext.declarative.declarative_base
. Declarative
SQLAlchemy models are easier to use than directly-mapped ones.
1 2 3 4 5 6 7 8 9 10 | class Page(Base):
""" The SQLAlchemy declarative model class for a Page object. """
__tablename__ = 'pages'
id = Column(Integer, primary_key=True)
name = Column(Text, unique=True)
data = Column(Text)
def __init__(self, name, data):
self.name = name
self.data = data
|
As you can see, our Page
class has a class level attribute
__tablename__
which equals the string 'pages'
. This means that
SQLAlchemy will store our wiki data in a SQL table named pages
. Our Page
class will also have class-level attributes named id
, name
and
data
(all instances of sqlalchemy.Column
). These will map to
columns in the pages
table. The id
attribute will be the primary key
in the table. The name
attribute will be a text attribute, each value of
which needs to be unique within the column. The data
attribute is a text
attribute that will hold the body of each page.
We’ll also remove our populate
function. We’ll inline the populate step
into initialize_sql
, changing our initialize_sql
function to add a
FrontPage object to our database at startup time.
1 2 3 4 5 6 7 8 9 10 11 12 13 | def initialize_sql(engine):
DBSession.configure(bind=engine)
Base.metadata.bind = engine
Base.metadata.create_all(engine)
try:
transaction.begin()
session = DBSession()
page = Page('FrontPage', 'This is the front page')
session.add(page)
transaction.commit()
except IntegrityError:
# already created
transaction.abort()
|
Here, we’re using a slightly different binding syntax. It is otherwise
largely the same as the initialize_sql
in the paster-generated
models.py
.
Our DBSession
assignment stays the same as the original generated
models.py
.
Looking at the Result of all Our Edits to models.py
¶
The result of all of our edits to models.py
will end up looking
something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | import transaction
from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import Text
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session
from sqlalchemy.orm import sessionmaker
from zope.sqlalchemy import ZopeTransactionExtension
DBSession = scoped_session(sessionmaker(
extension=ZopeTransactionExtension()))
Base = declarative_base()
class Page(Base):
""" The SQLAlchemy declarative model class for a Page object. """
__tablename__ = 'pages'
id = Column(Integer, primary_key=True)
name = Column(Text, unique=True)
data = Column(Text)
def __init__(self, name, data):
self.name = name
self.data = data
def initialize_sql(engine):
DBSession.configure(bind=engine)
Base.metadata.bind = engine
Base.metadata.create_all(engine)
try:
transaction.begin()
session = DBSession()
page = Page('FrontPage', 'This is the front page')
session.add(page)
transaction.commit()
except IntegrityError:
# already created
transaction.abort()
|
Viewing the Application in a Browser¶
We can’t. At this point, our system is in a “non-runnable” state; we’ll need to change view-related files in the next chapter to be able to start the application successfully. If you try to start the application, you’ll wind up with a Python traceback on your console that ends with this exception:
ImportError: cannot import name MyModel
This will also happen if you attempt to run the tests.