mirror of
https://github.com/internetarchive/warcprox.git
synced 2025-01-18 13:22:09 +01:00
make ORM __getattr___ return None if attribute is missing; documentation improvements
This commit is contained in:
parent
ad0e1b1fd7
commit
4a9978fc46
@ -20,7 +20,7 @@ import rethinkdb as r
|
|||||||
import logging
|
import logging
|
||||||
import rethinkstuff
|
import rethinkstuff
|
||||||
|
|
||||||
class WatchedDict(dict):
|
class WatchedDict(dict, object):
|
||||||
def __init__(self, d, callback, field):
|
def __init__(self, d, callback, field):
|
||||||
self.callback = callback
|
self.callback = callback
|
||||||
self.field = field
|
self.field = field
|
||||||
@ -61,7 +61,7 @@ class WatchedDict(dict):
|
|||||||
# looks a little tricky
|
# looks a little tricky
|
||||||
raise Exception('not implemented')
|
raise Exception('not implemented')
|
||||||
|
|
||||||
class WatchedList(list):
|
class WatchedList(list, object):
|
||||||
def __init__(self, l, callback, field):
|
def __init__(self, l, callback, field):
|
||||||
self.callback = callback
|
self.callback = callback
|
||||||
self.field = field
|
self.field = field
|
||||||
@ -120,7 +120,6 @@ def watch(obj, callback, field):
|
|||||||
else:
|
else:
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
class classproperty(object):
|
class classproperty(object):
|
||||||
def __init__(self, fget):
|
def __init__(self, fget):
|
||||||
self.fget = fget
|
self.fget = fget
|
||||||
@ -129,10 +128,9 @@ class classproperty(object):
|
|||||||
|
|
||||||
class Document(dict, object):
|
class Document(dict, object):
|
||||||
'''
|
'''
|
||||||
Base class for ORM.
|
Base class for ORM. You should subclass this class for each of your
|
||||||
|
rethinkdb tables. You can add custom functionality in your subclass if
|
||||||
You should subclass this class for each of your rethinkdb tables. You can
|
appropriate.
|
||||||
add custom functionality in your subclass if appropriate.
|
|
||||||
|
|
||||||
This class keeps track of changes made to the object and any nested fields.
|
This class keeps track of changes made to the object and any nested fields.
|
||||||
After you have made some changes, call update() to persist them to the
|
After you have made some changes, call update() to persist them to the
|
||||||
@ -140,8 +138,21 @@ class Document(dict, object):
|
|||||||
|
|
||||||
Changes in nested fields result in updates to their first-level ancestor
|
Changes in nested fields result in updates to their first-level ancestor
|
||||||
field. For example, if your document starts as {'a': {'b': 'c'}}, then
|
field. For example, if your document starts as {'a': {'b': 'c'}}, then
|
||||||
you run d['a']['x'] = 'y', then the update will replace the whole 'a'
|
you run doc['a']['x'] = 'y', then the update will replace the whole 'a'
|
||||||
field. Nested field updates get too complicated any other way.
|
field. (Nested field updates get too complicated any other way.)
|
||||||
|
|
||||||
|
This class subclasses dict. Thus attributes can be accessed with
|
||||||
|
`doc['foo']` or `doc.get('foo')`, depending on what you want to happen if
|
||||||
|
the attribute is missing. In addition, this class overrides `__getattr__`
|
||||||
|
to point to `dict.get`, so that first level attributes can be accessed as
|
||||||
|
if they were member variables, e.g. `doc.foo`. If there is no attribute
|
||||||
|
foo, `doc.foo` returns None. (XXX is this definitely what we want?)
|
||||||
|
|
||||||
|
The default table name is the class name, lowercased. Subclasses can
|
||||||
|
specify different table name like so:
|
||||||
|
|
||||||
|
class Something(rethinkstuff.Document):
|
||||||
|
table = 'my_table_name'
|
||||||
'''
|
'''
|
||||||
|
|
||||||
@classproperty
|
@classproperty
|
||||||
@ -149,7 +160,7 @@ class Document(dict, object):
|
|||||||
'''
|
'''
|
||||||
Returns default table name, which is the class name, lowercased.
|
Returns default table name, which is the class name, lowercased.
|
||||||
|
|
||||||
Subclasses can override this default more simply:
|
Subclasses can override the table name like so:
|
||||||
|
|
||||||
class Something(rethinkstuff.Document):
|
class Something(rethinkstuff.Document):
|
||||||
table = 'my_table_name'
|
table = 'my_table_name'
|
||||||
@ -159,7 +170,7 @@ class Document(dict, object):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def load(cls, rethinker, pk):
|
def load(cls, rethinker, pk):
|
||||||
'''
|
'''
|
||||||
Retrieve an instance from the database.
|
Retrieves a document from the database, by primary key.
|
||||||
'''
|
'''
|
||||||
doc = cls(rethinker)
|
doc = cls(rethinker)
|
||||||
doc[doc.pk_field] = pk
|
doc[doc.pk_field] = pk
|
||||||
@ -169,11 +180,8 @@ class Document(dict, object):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def table_create(cls, rethinker):
|
def table_create(cls, rethinker):
|
||||||
'''
|
'''
|
||||||
Creates the table.
|
Creates the table. Subclasses may want to override this method to do
|
||||||
|
more things, such as creating secondary indexes.
|
||||||
Can be run on an instance of the class: `my_doc.table_create
|
|
||||||
Subclasses may want to override this method to do more things, such as
|
|
||||||
creating indexes.
|
|
||||||
'''
|
'''
|
||||||
rethinker.table_create(cls.table).run()
|
rethinker.table_create(cls.table).run()
|
||||||
|
|
||||||
@ -198,7 +206,7 @@ class Document(dict, object):
|
|||||||
self._updated(key)
|
self._updated(key)
|
||||||
|
|
||||||
__setattr__ = __setitem__
|
__setattr__ = __setitem__
|
||||||
__getattr__ = dict.__getitem__
|
__getattr__ = dict.get
|
||||||
|
|
||||||
def __delitem__(self, key):
|
def __delitem__(self, key):
|
||||||
dict.__delitem__(self, key)
|
dict.__delitem__(self, key)
|
||||||
@ -243,11 +251,18 @@ class Document(dict, object):
|
|||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
'''
|
'''
|
||||||
Saves
|
Persist changes to rethinkdb. Updates only the fields that have
|
||||||
|
changed. Performs insert rather than update if the document has no
|
||||||
|
primary key or if the primary key is absent from the database.
|
||||||
|
|
||||||
|
If there have been any changes to nested fields, updates the first
|
||||||
|
level attribute. For example, if foo['bar']['baz']['quux'] has changed,
|
||||||
|
all of foo['bar'] is replaced, but foo['something_else'] is not
|
||||||
|
touched.
|
||||||
'''
|
'''
|
||||||
should_insert = False
|
should_insert = False
|
||||||
try:
|
try:
|
||||||
self.pk_value # raise KeyError if unset
|
self[self.pk_field] # raises KeyError if missing
|
||||||
if self._updates:
|
if self._updates:
|
||||||
# r.literal() to replace, not merge with, nested fields
|
# r.literal() to replace, not merge with, nested fields
|
||||||
updates = {field: r.literal(self._updates[field])
|
updates = {field: r.literal(self._updates[field])
|
||||||
@ -288,7 +303,7 @@ class Document(dict, object):
|
|||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
'''
|
'''
|
||||||
Refresh from the database.
|
Refresh the document from the database.
|
||||||
'''
|
'''
|
||||||
d = self._r.table(self.table).get(self.pk_value).run()
|
d = self._r.table(self.table).get(self.pk_value).run()
|
||||||
if d is None:
|
if d is None:
|
||||||
|
2
setup.py
2
setup.py
@ -3,7 +3,7 @@ import codecs
|
|||||||
|
|
||||||
setuptools.setup(
|
setuptools.setup(
|
||||||
name='rethinkstuff',
|
name='rethinkstuff',
|
||||||
version='0.2.0.dev62',
|
version='0.2.0.dev63',
|
||||||
packages=['rethinkstuff'],
|
packages=['rethinkstuff'],
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Programming Language :: Python :: 2.7',
|
'Programming Language :: Python :: 2.7',
|
||||||
|
@ -411,10 +411,10 @@ def test_orm_pk(r):
|
|||||||
|
|
||||||
# new doc with something in it
|
# new doc with something in it
|
||||||
e = NonstandardPrimaryKey(r, {'some_field': 'something'})
|
e = NonstandardPrimaryKey(r, {'some_field': 'something'})
|
||||||
with pytest.raises(KeyError):
|
|
||||||
e.not_id
|
|
||||||
with pytest.raises(KeyError):
|
with pytest.raises(KeyError):
|
||||||
e['not_id']
|
e['not_id']
|
||||||
|
assert e.not_id is None
|
||||||
|
assert e.get('not_id') is None
|
||||||
e.save()
|
e.save()
|
||||||
assert e.not_id
|
assert e.not_id
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user