Defining the Domain Model¶
The first change we'll make to our stock pcreate
-generated application will
be to define a domain model constructor representing a wiki page.
We'll do this inside our models.py
file.
Edit models.py
¶
Note
There is nothing 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.
Open tutorial/tutorial/models.py
file and edit it to look like the
following:
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 | from sqlalchemy import (
Column,
Integer,
Text,
)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import (
scoped_session,
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)
|
The highlighted lines are the ones that need to be changed, as well as
removing lines that reference Index
.
The first thing we've done 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.
Then, we added a Page
class. Because this is a SQLAlchemy application,
this class inherits from an instance of
sqlalchemy.ext.declarative.declarative_base()
.
1 2 3 4 5 6 | 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)
|
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.schema.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.
Changing scripts/initializedb.py
¶
We haven't looked at the details of this file yet, but within the scripts
directory of your tutorial
package is a file named initializedb.py
.
Code in this file is executed whenever we run the initialize_tutorial_db
command, as we did in the installation step of this tutorial.
Since we've changed our model, we need to make changes to our
initializedb.py
script. In particular, we'll replace our import of
MyModel
with one of Page
and we'll change the very end of the script
to create a Page
rather than a MyModel
and add it to our
DBSession
.
Open tutorial/tutorial/scripts/initializedb.py
and edit it to look like
the following:
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 | import os
import sys
import transaction
from sqlalchemy import engine_from_config
from pyramid.paster import (
get_appsettings,
setup_logging,
)
from ..models import (
DBSession,
Page,
Base,
)
def usage(argv):
cmd = os.path.basename(argv[0])
print('usage: %s <config_uri>\n'
'(example: "%s development.ini")' % (cmd, cmd))
sys.exit(1)
def main(argv=sys.argv):
if len(argv) != 2:
usage(argv)
config_uri = argv[1]
setup_logging(config_uri)
settings = get_appsettings(config_uri)
engine = engine_from_config(settings, 'sqlalchemy.')
DBSession.configure(bind=engine)
Base.metadata.create_all(engine)
with transaction.manager:
model = Page(name='FrontPage', data='This is the front page')
DBSession.add(model)
|
Only the highlighted lines need to be changed, as well as removing the lines
referencing pyramid.scripts.common
and options
under the main
function.
Installing the project and re-initializing the database¶
Because our model has changed, in order to reinitialize the database, we need
to rerun the initialize_tutorial_db
command to pick up the changes you've
made to both the models.py file and to the initializedb.py file. See
Initializing the database for instructions.
Success will look something like this:
2015-05-24 15:34:14,542 INFO [sqlalchemy.engine.base.Engine:1192][MainThread] SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
2015-05-24 15:34:14,542 INFO [sqlalchemy.engine.base.Engine:1193][MainThread] ()
2015-05-24 15:34:14,543 INFO [sqlalchemy.engine.base.Engine:1192][MainThread] SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1
2015-05-24 15:34:14,543 INFO [sqlalchemy.engine.base.Engine:1193][MainThread] ()
2015-05-24 15:34:14,543 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] PRAGMA table_info("pages")
2015-05-24 15:34:14,544 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ()
2015-05-24 15:34:14,544 INFO [sqlalchemy.engine.base.Engine:1097][MainThread]
CREATE TABLE pages (
id INTEGER NOT NULL,
name TEXT,
data TEXT,
PRIMARY KEY (id),
UNIQUE (name)
)
2015-05-24 15:34:14,545 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ()
2015-05-24 15:34:14,546 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT
2015-05-24 15:34:14,548 INFO [sqlalchemy.engine.base.Engine:646][MainThread] BEGIN (implicit)
2015-05-24 15:34:14,549 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] INSERT INTO pages (name, data) VALUES (?, ?)
2015-05-24 15:34:14,549 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ('FrontPage', 'This is the front page')
2015-05-24 15:34:14,550 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT
View 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 (See 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.