Browse files

adapt parts of documentation to use of Declarative

git-svn-id: http://projects.conceptive.be/camelot/svn/trunk@2549 8325946a-aa55-0410-8760-f0bc8c33efaa
  • Loading branch information...
1 parent c090daa commit 7eedf07e96d73b172c42d9e361575ca057b35a73 erikj committed May 29, 2012
View
3 camelot/bin/meta.py
@@ -259,8 +259,7 @@ def setup_model():
import camelot.model.memento
import camelot.model.fixture
import {{options.module}}.model
- from elixir import setup_all
- setup_all(create_tables=True)
+ metadata.create_all()
'''),
('setup.py', '''
View
7 camelot_example/main.py
@@ -19,23 +19,22 @@ class ExampleSettings( SimpleSettings ):
@staticmethod
def setup_model():
+ from camelot.core.sql import metadata
+ metadata.bind = settings.ENGINE()
import camelot.model.party
import camelot.model.authentication
import camelot.model.i18n
import camelot.model.fixture
import camelot.model.memento
import camelot_example.model
- from elixir import setup_all
- setup_all(create_tables=True)
+ metadata.create_all()
from camelot.model.authentication import update_last_login
update_last_login()
#
# Load sample data with the fixure mechanism
#
from camelot_example.fixtures import load_movie_fixtures
load_movie_fixtures()
- from camelot.core.sql import update_database_from_model
- #update_database_from_model()
#
# setup the views
#
View
34 camelot_example/model.py
@@ -6,17 +6,22 @@
import time
import datetime
-from sqlalchemy import sql
-from sqlalchemy.schema import Column, ForeignKey
-from sqlalchemy.orm import relationship, column_property
+# begin basic imports
+from camelot.core.orm import Entity
+from camelot.admin.entity_admin import EntityAdmin
+
+from sqlalchemy.schema import Column
import sqlalchemy.types
+# end basic imports
+
+from sqlalchemy.schema import ForeignKey
+from sqlalchemy.orm import relationship
import camelot.types
from camelot.admin.action import Action
from camelot.admin.entity_admin import EntityAdmin
-from camelot.core.orm import Entity, ManyToMany
-from camelot.core.sql import metadata
+from camelot.core.orm import ManyToMany
from camelot.core.utils import ugettext_lazy as _
from camelot.model.party import Person
from camelot.view import action_steps
@@ -28,6 +33,7 @@
from camelot_example.change_rating import ChangeRatingAction
from camelot_example.drag_and_drop import DropAction
+#
# Some helper functions that will be used later on
#
@@ -216,19 +222,13 @@ class Tag( Entity ):
__tablename__ = 'tags'
name = Column( sqlalchemy.types.Unicode(60), nullable = False )
- movies = ManyToMany( 'Movie',
- tablename = 'tags_movies__movies_tags',
- local_colname = 'tags_id',
- remote_colname = 'movies_id' )
- def __unicode__(self):
+ def __unicode__( self ):
return self.name
- class Admin(EntityAdmin):
+ class Admin( EntityAdmin ):
form_size = (400,200)
list_display = ['name']
- form_display = ['name', 'movies']
- lines_per_row = 2
# begin visitor report definition
class VisitorReport(Entity):
@@ -255,5 +255,13 @@ class Admin(EntityAdmin):
#
# Using a column_property, an sql query can be assigned to a field
#
+
+# begin column_property
+
+from sqlalchemy.orm import column_property
+from sqlalchemy import sql
+
Movie.total_visitors = column_property( sql.select( [sql.func.sum( VisitorReport.visitors) ],
VisitorReport.movie_id == Movie.id ) )
+
+# end column_property
View
25 doc/sphinx/source/doc/admin.rst
@@ -11,27 +11,20 @@ it easy to subclass a default Admin class and tune it to your needs.
.. image:: ../_static/admin_classes.png
-Camelot is able to visualize any python class, through the use of an ObjectAdmin
-class. However, subtypes exist that use introspection to facilitate the visualisation.
+Camelot is able to visualize any Python class, through the use of the :class:`camelot.admin.object_admin.ObjectAdmin`
+class. However, subclasses exist that use introspection to facilitate the visualisation.
-The EntityAdmin class is a subclass of ObjectAdmin that can be used to visualize
+The :class:`camelot.admin.object_admin.EntityAdmin` class is a subclass of `ObjectAdmin` that can be used to visualize
class mapped to a database using SQLAlchemy.
-Each class that is visualized within Camelot has an associated Admin
-class which specifies how the object or a list of objects should be visualized.
+Each class that is visualized within Camelot has an associated Admin class which specifies how the object or a list of objects should be visualized.
-Usually the Admin class is bound to the model class by defining it as an
-inner class of the model class::
+Usually the Admin class is bound to the model class by defining it as an inner class of the model class::
- class Movie(Entity):
- title = Field(Unicode(60), required=True)
-
- class Admin(EntityAdmin):
- verbose_name = _('Movies')
- list_display = ['title']
-
-Most of the behaviour of the Admin class can be customized by changing
-the class attributes like verbose_name or list_display.
+.. literalinclude:: ../../../../camelot_example/model.py
+ :pyobject: Tag
+
+Most of the behaviour of the Admin class can be customized by changing the class attributes like `verbose_name` or `list_display`.
.. toctree::
View
49 doc/sphinx/source/doc/calculated_fields.rst
@@ -45,46 +45,13 @@ summary builds on information in related records, having the database build the
reduces the need to transfer additional data from the database to the server.
To display fields in the table and the form view that are the result of a calculation
-done by the database, a ColumnProperty needs to be defined in the Elixir model. In this
-ColumnProperty, the sql query can be defined using SQLAlchemy statements. Then use the
-field attributes mechanism to specify which delegate needs to be used to render the field.
+done by the database, a `column_property` needs to be defined in the Declarative model. In this
+`column_property`, the sql query can be defined using SQLAlchemy statements. In this example, the `Movie` class gains the
+`total_visitors` attribute which contains the sum of all visitors that went to a movie.
-.. image:: ../_static/budget.png
+.. literalinclude:: ../../../../camelot_example/model.py
+ :start-after: begin column_property
+ :end-before: end column_property
-As an example we will create a budget with multiple budget lines, where the total budget
-is calculated by the database::
-
- from elixir.properties import ColumnProperty
- from camelot.view.controls import delegates
- from sqlalchemy import sql, and_
-
- class Budget(Entity):
- lines = OneToMany('BudgetLine')
-
- @ColumnProperty
- def total(self):
- return sql.select([sql.func.sum(BudgetLine.amount)],
- and_(BudgetLine.budget_id==self.id))
-
- class Admin(EntityAdmin):
- verbose_name = 'Budgets'
- list_display = [ 'total', 'lines']
- field_attributes = {'total':{'delegate':delegates.FloatDelegate}}
-
- class BudgetLine(Entity):
- budget = ManyToOne('Budget', required=True, ondelete='cascade', onupdate='cascade')
- amount = Field(Float(precision=2), default=0)
-
- class Admin(EntityAdmin):
- verbose_name = 'Budget lines'
- list_display = ['amount',]
-
-When the user presses F9, all data in the application is refreshed from the database, and thus
-all fields are recalculated.
-
-An explanation of the lambda function inside the ColumnProperty can be found in the ElixirColumnProperty_ and
-the SqlalchemyMappers_ documentation.
-
-.. _ElixirColumnProperty: http://elixir.ematia.de/apidocs/elixir.properties.ColumnProperty.html
-
-.. _SqlalchemyMappers: http://www.sqlalchemy.org/docs/04/mappers.html#advdatamapping_mapper_expressions
+It's important to notice that the value of this field is calculated when the object is fetched from the database. When the user presses F9,
+all data in the application is refreshed from the database, and thus all column properties are recalculated.
View
47 doc/sphinx/source/doc/models.rst
@@ -15,39 +15,48 @@ create views for objects mapped through SQLAlchemy.
SQLAlchemy comes with the *Declarative* extension to make it easy to define an ORM mapping using
the Active Record Pattern. This is used through the documentation and in the example code.
-
-.. index:: Elixir
-
An alternative to *Declarative* is `Elixir <http://elixir.ematia.de/trac/wiki/TutorialDivingIn>`_,
which was used in previous *Camelot* versions, and is still supported.
-.. literalinclude:: ../../../../camelot/model/authentication.py
- :pyobject: GeographicBoundary
+To use *Declarative*, threre are some base classes that should be imported:
+
+.. literalinclude:: ../../../../camelot_example/model.py
+ :start-after: begin basic imports
+ :end-before: end basic imports
-The code above defines the model for a `GeographicBoundary` class, a base class that will be used later
-on to subclass into `Countries` and `Cities`, and is part of the default :ref:`model-persons` data
-model of Camelot. This code has some things to notice :
+Those are :
- * `GeographicBoundary` is a subclass of `Entity`, `Entity` is the base class for all classes that are mapped
- to the database
+ * :class:`camelot.core.orm.Entity` is the base class for all classes that are mapped to the database
+
+ * :class:`camelot.admin.entity_admin.EntityAdmin` is the base class that describes how an `Entity` subclass should be represented in the GUI
+
+ * `Column` describes a column in the database and a field in the model
+
+ * `sqlalchemy.types` contains the various column types that can be used
+
+Next a model can be defined:
- * the `using_options` statement allows us to fine tune the ORM, in this case the `GeographicBoundary` class
- will be mapped to the `geographic_boundary` table
+.. literalinclude:: ../../../../camelot_example/model.py
+ :pyobject: Tag
- * The `Field` statement add fields of a certain type, in this case `Unicode`, to the `GeographicBoundary` class
- as well as to the `geographic_boundary` table
+The code above defines the model for a `Tag` class, an object with only a name that can be related to other
+ojbects later on. This code has some things to notice :
+
+ * `Tag` is a subclass of :class:`camelot.core.orm.Entity`,
+
+ * the `__tablename__` class attribute allows us to specify the name of the table in the database in which
+ the tags will be stored.
- * The `ColumnProperty` `full_name` is a more advanced feature of the ORM, when an `GeographicBoundary` object
- is read from the database, the ORM will ask the database to concatenate the fields code and name, and
- map the result to the `full_name` attribute of the object
+ * The `Column` statement add fields of a certain type, in this case `Unicode`, to the `Tag` class
+ as well as to the `tags` table
* The `__unicode__` method is implemented, this method will be called within Camelot whenever a textual
- representation of the object is needed, eg in a window title or a many to one widget
+ representation of the object is needed, eg in a window title or a many to one widget. It's good
+ practice to always implement the `__unicode__` method for all `Entity` subclasses.
When a new Camelot project is created, the :ref:`camelot-admin` tool creates an empty ``models.py`` file that
can be used as a place to start the model definition.
-
.. toctree::
fields.rst
View
89 doc/sphinx/source/doc/under_the_hood.rst
@@ -4,69 +4,66 @@
Under the hood
==============
-Setting up the model
-====================
+A lot of things happen when a Camelot application starts up.
+In this section we give a brief overview of those which might need to be adapted for more complex applications
-A lot of things happen under the hood when a model is defined using Elixir, and
-picked up by Camelot :
+Setting up the ORM
+==================
-Metadata
---------
-
-Each file that contains a part of the model definition should contain these lines :
+When the application starts up, the `setup_model` function in the `settings.py` file
+is called. In this function, all model files should be imported, to make sure the
+model has been completely setup. The importing of these files is enough to define
+the mapping between objects and tables.
-.. literalinclude:: ../../../../camelot/empty_project/model.py
- :start-after: begin meta data setup
- :end-before: end meta data setup
+.. literalinclude:: ../../../../new_project/settings.py
+ :pyobject: ENGINE
-They associate the Entities defined in this file with the default metadata. The
-metadata is a datastructure that contains information about the database in which
-the tables for the model will be created.
+The import of these model definitions should happen before the call to `create_all` to
+make sure all models are known before the tables are created.
+
+Setting up the Database
+=======================
Engine
------
-The settings.py file should contain a function named ENGINE that returns a
-connection to the database. This connection will be associated with the default
-metadata used in the model definition.
+The :file:`settings.py` file should contain a function named ENGINE that returns a
+connection to the database. Whenever a connection to the database is needed, this function will be called.
-.. literalinclude:: ../../../../camelot/empty_project/settings.py
+.. literalinclude:: ../../../../new_project/settings.py
:pyobject: ENGINE
-As such, all defined models are associated with this database.
+Metadata
+--------
-Setup model
------------
+*SQLAlchemy* defines the :class:`MetaData` class. A `MetaData` object contains all the information about a database schema, such
+as Tables, Columns, Foreign keys, etc. The :mod:`camelot.core.sql` contains the singleton `metadata` object which is the
+default :class:`MetaData` object used by Camelot.
+In the `setup_model` function, this `metadata` object is bound to the database engine.
-When the application starts up, the setup_model function in the settings.py file
-is called. In this function, all model files should be imported, to make sure the
-model has been completely setup.
-.. literalinclude:: ../../../../camelot/empty_project/settings.py
+.. literalinclude:: ../../../../new_project/settings.py
:pyobject: setup_model
-Working without the default model
-=================================
+In case an application works with multiple database schemas in parallel, this step needs to be adapted.
-Camelot comes with a default model for Persons, Organizations, History tracking, etc.
+Creating the tables
+-------------------
-You might want to turn this off, here's how to do so :
+By simply importing the modules which contain parts of the model definition, the needed table information
+is added to the `metadata` object. At the end of the `setup_model` function, the `create_all` method is called on the metadata, which
+will create the tables in the database if they don't exist yet.
-1. In your settings.py, remove the line 'import camelot.model' and the line
- 'from camelot.model.authentication import updateLastLogin', this will make sure
- no tables are created for the default Camelot model. Tables are only created for
- the models that have been imported before the call to 'setup_all()'
-
-.. literalinclude:: ../../../../camelot/empty_project/settings.py
+.. literalinclude:: ../../../../new_project/settings.py
:pyobject: setup_model
-
-2. Have a look in 'camelot/model/__init__.py' and copy the lines that do
- the initialization of the elixir session and metadata to the top of your
- own model file, imported first in the 'setup_model()' function.
+
+Working without the default model
+=================================
+
+Camelot comes with a default model for Persons, Organizations, History tracking, etc.
-.. literalinclude:: ../../../../camelot/model/__init__.py
- :start-after: begin session setup
- :end-before: end session setup
+To turn this off, simply remove the import statements of those modules from the
+`setup_model` method in `settings.py`.
Transactions
============
@@ -98,15 +95,15 @@ It is of course perfectly possible to reuse the whole model definition in those
GUI parts. The easiest way to do so is to leave the Camelot GUI application as it
is and then in the non GUI script, initialize the model first ::
-import settings
-settings.setup_model()
+ import settings
+ settings.setup_model()
From that point, all model manipulations can be done. Access to the session can
be obtained via any Entity subclass, such as Person ::
-session = Person.query.session
+ session = Person.query.session
After the manipulations to the model have been done, they can be flushed to the db ::
-session.flush()
+ session.flush()
2 doc/sphinx/source/doc/views.rst
@@ -20,7 +20,7 @@ The model to start from
In the example movie project, we can take three parts of the model : Person,
Movie and VisitorReport:
-.. literalinclude:: ../../../../camelot/model/authentication.py
+.. literalinclude:: ../../../../camelot/model/party.py
:start-after: begin short person definition
:end-before: end short person definition
View
4 install.txt
@@ -58,10 +58,10 @@ extracted::
tar xzvf Camelot-10.07.02.tar.gz
-**Trunk**
+**Master branch**
The latest and greatest version of the source can be checked out
-from the subversion repository::
+from the gitorious repository::
git clone git@gitorious.org:camelot/camelot.git
View
2 license.txt
@@ -1,4 +1,4 @@
- Camelot is Copyright (C) 2007-2011 Conceptive Engineering bvba.
+ Camelot is Copyright (C) 2007-2012 Conceptive Engineering bvba.
www.conceptive.be / project-camelot@conceptive.be
You may use, distribute and copy Camelot under the terms of GNU General
View
6 news.txt
@@ -1,10 +1,14 @@
Master
------
- * Port the ``camelot_example`` application to `Declarative`
+ * Add ``ColumnGroups`` in the list view.
+
+ * Port the ``camelot_example`` application and ``tutorial`` to `Declarative`
* See :ref:`migrate-11.12.30` for documentation on how to upgrade an
existing Camelot project.
+
+ * Tracking of changes goes through the ``ObjectAdmin``
* Cleanup of the default Camelot models :
View
2 test/test_bin.py
@@ -10,7 +10,7 @@ def test_create_new_project(self):
from camelot.bin.meta import CreateNewProject, templates, NewProjectOptions
new_project_action = CreateNewProject()
options = NewProjectOptions()
- options.source = tempfile.mkdtemp()
+ options.source = 'new_project'
new_project_action.start_project( options )
#
# validate the generated files

0 comments on commit 7eedf07

Please sign in to comment.