Skip to content

Commit

Permalink
Pony ORM release 0.5.4
Browse files Browse the repository at this point in the history
  • Loading branch information
kozlovsky committed Sep 22, 2014
2 parents 4c30244 + 7835e03 commit 24c3bc3
Show file tree
Hide file tree
Showing 8 changed files with 267 additions and 13 deletions.
54 changes: 54 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,57 @@
# Pony ORM Release 0.5.4 (2014-09-22)

## New functions and methods:

* `pony.orm.serialization` module with the `to_dict()` and `to_json()` functions was added. Before this release you could use the `to_dict()` method of an entity instance in order to get a key-value dictionary structure for a specific entity instance. Sometimes you might need to serialize not only the instance itself, but also the instance's related objects. In this case you can use the `to_dict()` function from the pony.orm.serialization module.

* [`to_dict()`](http://doc.ponyorm.com/working_with_entity_instances.html#to_dict) - receives an entity instance or a list of instances and returns a dictionary structure which keeps the passed object(s) and immediate related objects

* [`to_json()`](http://doc.ponyorm.com/working_with_entity_instances.html#to_json) – uses `to_dict()` and returns JSON representation of the `to_dict()` result

* [`Query.prefetch()`](http://doc.ponyorm.com/queries.html#Query.prefetch) – allows to specify which related objects or attributes should be loaded from the database along with the query result . Example:

```python
select(s for s in Student).prefetch(Group, Department, Student.courses)
```

* [`obj.flush()`](http://doc.ponyorm.com/working_with_entity_instances.html#Entity.flush) – allows flush a specific entity to the database

* [`obj.get_pk()`](http://doc.ponyorm.com/working_with_entity_instances.html#Entity.get_pk) – return the primary key value for an entity instance

* [`py_check`](http://doc.ponyorm.com/entities.html#py_check) parameter for attributes added. This parameter allows you to specify a function which will be used for checking the value before it is assigned to the attribute. The function should return `True`/`False` or can raise `ValueError` exception if the check failed. Example:

```python
class Student(db.Entity):
name = Required(unicode)
gpa = Required(float, py_check=lambda val: val >= 0 and val <= 5)
```

## New types:

* `time` and `timedelta` – now you can use these types for attribute declaration. Also you can use `timedelta` and a combination of `datetime` + `timedelta` types inside queries.

## New hooks:

* `after_insert`, `after_update`, `after_delete` - these hooks are called when an object was inserted, updated or deleted in the database respectively ([link](http://doc.ponyorm.com/working_with_entity_instances.html#entity-hooks))

## New features:

* Added support for pymysql – pure Python MySQL client. Currently it is used as a fallback for MySQLdb interface

## Other changes and bug fixes

* `obj.order_by()` method is deprecated, use `Entity.select().order_by()` instead
* `obj.describe()` now displays composite primary keys
* Fixes #50: PonyORM does not escape _ and % in LIKE queries
* Fixes #51: Handling of one-to-one relations in declarative queries
* Fixes #52: An attribute without a column should not have rbits & wbits
* Fixes #53: Column generated at the wrong side of "one-to-one" relationship
* Fixes #55: `obj.to_dict()` should do flush at first if the session cache is modified
* Fixes #57: Error in `to_dict()` when to-one attribute value is None
* Fixes #70: EntitySet allows to add and remove None
* Check that the entity name starts with a capital letter and throw exception if it is not then raise the `ERDiagramError: Entity class name should start with a capital letter` exception


# Pony ORM Release 0.5.3 (2014-08-12)

This release fixes the setup.py problem that was found after the previous release was uploaded to PyPI.
Expand Down
2 changes: 1 addition & 1 deletion doc/database.txt
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ MySQL

db.bind('mysql', host='', user='', passwd='', db='')

Pony uses the MySQLdb driver for working with MySQL. See the `MySQLdb documentation <http://mysql-python.sourceforge.net/MySQLdb.html#functions-and-attributes>`_ for more information regarding this driver.
Pony tries to use the MySQLdb driver for working with MySQL. If this module cannot be imported, Pony tries to use pymysql. See the `MySQLdb <http://mysql-python.sourceforge.net/MySQLdb.html#functions-and-attributes>`_ and `pymysql <https://pypi.python.org/pypi/PyMySQL>`_ documentation for more information regarding these drivers.

Oracle
~~~~~~~~~~~~~~~~
Expand Down
23 changes: 16 additions & 7 deletions doc/entities.txt
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ Entity attributes are specified as class attributes inside the entity class usin
* Decimal
* datetime
* date
* time
* timedelta
* bool
* buffer - used for binary data (stored as BLOB in the database)
* LongStr - used for large strings (stored as CLOB in the database)
Expand All @@ -139,6 +141,9 @@ Attribute options

You can specify additional options during attribute definitions using positional and keyword arguments. Positional arguments depend on the attribute type.

Max length and precision
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

String types accept a positional argument which specifies the max length of this column in the database::

name = Required(unicode, 40) # VARCHAR(40)
Expand All @@ -153,6 +158,9 @@ For MySQL database the default value is 0. Before the MySQL version 5.6.4, the `

dt = Required(datetime, 6)

Other attribute options
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Additional attribute options which can be set as keyword arguments:

.. py:attribute:: unique
Expand Down Expand Up @@ -233,6 +241,14 @@ Additional attribute options which can be set as keyword arguments:

Allows you to specify the sequence name used for ``PrimaryKey`` attributes for Oracle database.

.. py:attribute:: py_check

This parameter allows you to specify a function which will be used for checking the value before it is assigned to the attribute. The function should return ``True`` or ``False``. Also it can raise the ``ValueError`` exception if the check failed. Example::

class Student(db.Entity):
name = Required(unicode)
gpa = Required(float, py_check=lambda val: val >= 0 and val <= 5)


Entity inheritance
----------------------
Expand Down Expand Up @@ -402,10 +418,3 @@ By default, for storing many-to-many relationships between ``Student`` and ``Cou
PrimaryKey(name, semester)

You can find more examples of mapping customization in `an example which comes with Pony ORM package <https://github.com/ponyorm/pony/blob/orm/pony/orm/examples/university.py>`_



Serializing entity instances
-------------------------------------------

Pony allows pickling entity instances, query results and collections. You might want to use it if you want to cache entity instances in an external cache (e.g. memcache). When Pony pickles entities it saves all attributes except collections in order to avoid pickling a large set of data. If you need to pickle a collection attribute, you must pickle it separately.
65 changes: 65 additions & 0 deletions doc/queries.txt
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,71 @@ Query object methods

Pagination is used when you need to display results of a query divided into multiple pages. The page numbering starts with page 1. This method returns a slice [start:stop] where ``start = (pagenum - 1) * pagesize``, ``stop = pagenum * pagesize``.

.. py:method:: prefetch

Allows you to specify which related objects or attributes should be loaded from the database along with the query result.

Usually there is no need to prefetch related objects. When you work with the query result within the ``@db_session``, Pony gets all related objects once you need them. Pony uses the most effective way for loading related objects from the database, avoiding the N+1 Query problem.

So, if you use Flask, the recommended approach is to use the ``@db_session`` decorator at the top level, at the same place where you put the Flask's ``app.route`` decorator:

.. code-block:: python

@app.route('/index')
@db_session
def index():
...
objects = select(...)
...
return render_template('template.html', objects=objects)

Or, even better, wrapping the wsgi application with the ``db_session`` decorator:

.. code-block:: python

app.wsgi_app = db_session(app.wsgi_app)

If for some reason you need to pass the selected instances along with related objects outside of the ``db_session``, then you can use this method. Otherwise, if you'll try to access the related objects outside of the ``db_session``, you might get the ``DatabaseSessionIsOver`` exception, e.g.::

DatabaseSessionIsOver: Cannot load attribute Customer[3].name: the database session is over

More information regarding working with the ``db_session`` can be found :ref:`here <db_session_ref>`.

You can specify entities and/or attributes as parameters. When you specify an entity, then all "to-one" and non-lazy attributes of corresponding related objects will be prefetched. The "to-many" attributes of an entity are prefetched only when specified explicitly.

If you specify an attribute, then only this specific attribute will be prefetched. You can specify attribute chains, e.g. `order.customer.address`. The prefetching works recursively - it applies the specified parameters to each selected object.

Examples::

from pony.orm.examples.presentation import *

Loading Student objects only, without prefetching::

students = select(s for s in Student)[:]

Loading students along with groups and departments::

students = select(s for s in Student).prefetch(Group, Department)[:]

for s in students: # no additional query to the DB will be sent
print s.name, s.group.major, s.group.dept.name

The same as above, but specifying attributes instead of entities::

students = select(s for s in Student).prefetch(Student.group, Group.dept)[:]

for s in students: # no additional query to the DB will be sent
print s.name, s.group.major, s.group.dept.name

Loading students and related courses ("many-to-many" relationship)::

students = select(s for s in Student).prefetch(Student.courses)

for s in students:
print s.name
for c in s.courses: # no additional query to the DB will be sent
print c.name


.. py:method:: random(limit)

Expand Down
5 changes: 5 additions & 0 deletions doc/transactions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ Example of using the ``db_session`` context manager:
.. note::
When you work with Python’s interactive shell you don’t need to worry about the database session, because it is maintained by Pony automatically.

If you'll try to access instance's attributes which were not loaded from the database outside of the ``db_session`` scope, you'll get the ``DatabaseSessionIsOver`` exception. For example::

DatabaseSessionIsOver: Cannot load attribute Customer[3].name: the database session is over

This happens because by this moment the connection to the database is already returned to the connection pool, transaction is closed and we cannot send any queries to the database.

When Pony reads objects from the database it puts those objects to the Identity Map. Later, when you update an object’s attributes, create or delete an object, the changes will be accumulated in the Identity Map first. The changes will be saved in the database on transaction commit or before calling the following methods: ``select()``, ``get()``, ``exists()``, ``execute()``.

Expand Down
127 changes: 124 additions & 3 deletions doc/working_with_entity_instances.txt
Original file line number Diff line number Diff line change
Expand Up @@ -480,28 +480,42 @@ Entity instance methods
'orders': [Order[1], Order[2]],
'password': u'***'}

If you need to serialize more than one entity instance, or if you need to serialize an instance with its related objects, you can use the :py:func`to_dict` function from the `pony.orm.serialization` module.

.. py:method:: flush()

Saves the changes made to this object to the database. Usually Pony saves changes automatically and you don't need to call this method yourself. One of the use cases when it might be needed is when you want to get the primary key value of a newly created object which has autoincremented primary key before commit.

Entity hooks
-----------------------------------

Sometimes you might need to perform an action before your entity instance is going to be created, updated or deleted in the database. For this purpose you can use entity hooks. You can write your own implementation for ``before_insert``, ``before_update`` and ``before_delete`` methods during the entity definition.
Sometimes you might need to perform an action before or after your entity instance is going to be created, updated or deleted in the database. For this purpose you can use entity hooks. You can write your own implementation for the following methods during the entity definition:

.. class:: Entity

.. py:method:: before_insert

Is called only for newly created objects before it will be inserted into the database.
Is called only for newly created objects before it is inserted into the database.

.. py:method:: before_update

Is called for entity instances before updating the instance in the database.

.. py:method:: before_delete

Is called before deletion an entity instance in the database.
Is called before deletion the entity instance in the database.

.. py:method:: after_insert

Is called after the row is inserted into the database.

.. py:method:: after_update

Is called after the instance updated in the database.

.. py:method:: after_delete

Is called after the entity instance is deleted in the database.

.. code-block:: python

Expand All @@ -518,6 +532,113 @@ Sometimes you might need to perform an action before your entity instance is goi
[u'First message', u'Hello, world!']



Serializing entity instances
-------------------------------------------


Serialization with pickle
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Pony allows pickling entity instances, query results and collections. You might want to use it if you want to cache entity instances in an external cache (e.g. memcache). When Pony pickles entity instances, it saves all attributes except collections in order to avoid pickling a large set of data. If you need to pickle a collection attribute, you must pickle it separately. Example:

.. code-block:: python

>>> from pony.orm.examples.estore import *
>>> products = select(p for p in Product if p.price > 100)[:]
>>> products
[Product[1], Product[2], Product[6]]
>>> import cPickle
>>> pickled_data = cPickle.dumps(products)

Now we can put the pickled data to a cache. Later, when we need our instances again, we can unpickle it:

.. code-block:: python

>>> products = cPickle.loads(pickled_data)
>>> products
[Product[1], Product[2], Product[6]]


Serialization to a dictionary and to JSON
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Another way to serialize entity instances is to use the :py:meth:`to_dict() <Entity.to_dict>` method of an entity instance or :py:func:`to_dict` and :py:func:`to_json` functions from the ``pony.orm.serialization`` module. The instance's :py:meth:`to_dict() <Entity.to_dict>` method returns a key-value dictionary structure for a specific entity instance. Sometimes you might need to serialize not only the instance itself, but also the instance's related objects. In this case you can use the ``to_dict()`` function described below.

.. py:function:: to_dict

This function is used for serializing entity instances to a dictionary. It can receive an entity instance or any iterator which returns entity instances. The function returns a multi-level dictionary which includes the objects passed to the function, as well as the immediate related objects. Here is a structure of the resulting dict:

.. code-block:: python

{
'entity_name': {
primary_key_value: {
attr: value,
...
},
...
},
...
}

Let’s use our online store model example (see the `ER diagram <https://editor.ponyorm.com/user/pony/eStore>`_) and print out the result of the ``to_dict()`` function. Note that you need to import the to_dict function separately:

.. code-block:: python

from pony.orm.examples.estore import *
from pony.orm.serialization import to_dict

print to_dict(Order[1])

{
'Order': {
1: {
'id': 1,
'state': u'DELIVERED',
'date_created': datetime.datetime(2012, 10, 20, 15, 22)
'date_shipped': datetime.datetime(2012, 10, 21, 11, 34),
'date_delivered': datetime.datetime(2012, 10, 26, 17, 23),
'total_price': Decimal('292.00'),
'customer': 1,
'items': ['1,1', '1,4'],
}
}
},
'Customer': {
1: {
'id': 1
'email': u'[email protected]',
'password': u'***',
'name': u'John Smith',
'country': u'USA',
'address': u'address 1',
}
},
'OrderItem': {
'1,1': {
'quantity': 1
'price': Decimal('274.00'),
'order': 1,
'product': 1,
},
'1,4': {
'quantity': 2
'price': Decimal('9.98'),
'order': 1,
'product': 4,
}
}
}

In the example above the result contains the serialized ``Order[1]`` instance as well as the immediate related objects: ``Customer[1]``, ``OrderItem[1, 1]`` and ``OrderItem[1, 4]``.


.. py:function:: to_json

This function uses the output of :py:func:`to_dict` and returns its JSON representation.


.. _entities_raw_sql_ref:

Using raw SQL
Expand Down
2 changes: 1 addition & 1 deletion pony/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import sys
from os.path import dirname

__version__ = '0.5.4-dev'
__version__ = '0.5.4'

def detect_mode():
try: import google.appengine
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import sys

name = "pony"
version = "0.5.4-dev"
version = "0.5.4"
description = "Pony Object-Relational Mapper"
long_description = """
Pony is an object-relational mapper. The most interesting feature of Pony is
Expand Down

0 comments on commit 24c3bc3

Please sign in to comment.