コード例 #1
0
def test_validate():
    Base, User, Foo, Bar, Baz = models()

    bar = Bar()
    bar['lock_version'] = '2'
    bar['chks'] = ['A', 'B']
    assert not bar.is_valid()
    assert bar.errors == {
        'chks': [errors.INVALID],
        'created_by': [errors.UNSELECTED],
        'foo_id': [errors.UNSELECTED],
        'rad': [errors.UNSELECTED],
        'updated_by': [errors.UNSELECTED]}

    bar = instantiate(Bar, id=1, lock_version=1)
    bar['lock_version'] = '2'
    assert not bar.is_valid()
    assert bar.errors == {None: [errors.CONFLICT]}

    bar = instantiate(Bar, id=1, lock_version=2)
    bar['lock_version'] = '2'
    bar['foo_id'] = '10'
    bar['rad'] = '1'
    bar['name'] = '?'

    Base.DB.execute = rs2csr(qw('? ?'), (1, 1))
    assert not bar.is_valid()
    assert bar.errors == {'name': [errors.TAKEN], 'rad': [errors.TAKEN]}
    assert last_x() == (
        'SELECT '
        'EXISTS (SELECT 1 FROM bars t1 WHERE foo_id = %s AND rad = %s),\n'
        'EXISTS (SELECT 1 FROM bars t1 WHERE name = %s)',
        [10, True, '?'], 1
    )

    bar.errors.clear()
    bar['name'] = ''
    bar.validate_column('name', len=(None, 2))
    assert bar.errors == {'name': [errors.BLANK]}

    bar.errors.clear()
    bar.validate_column('name', len=(None, 2), nn=False)
    assert not bar.errors

    bar['name'] = 'abc'
    bar.validate_column('name', len=(None, 2))
    assert bar.errors == {'name': [errors.TOO_LONG(2, 3)]}

    bar['xxx'] = '10'
    bar.errors.clear()
    assert not bar.validate_item('xxx', type=int, range=(1, 5))
    assert bar.errors == {'xxx': [errors.BAD_FORMAT]}

    bar.errors.clear()
    assert not bar.validate_item(
        'xxx', type=int, range=(1, 5), cast=Util.str2int)
    assert bar.errors == {'xxx': [errors.TOO_GREAT(5)]}
コード例 #2
0
def test_restore_attrs():
    Base, User, Foo, Bar, Baz = models()
    _ks = {'name', 'foo', 'foo_id'}

    def _d(x):
        return {k: v for k, v in x.__dict__.items() if k in _ks}

    foo = instantiate(Foo, id=1)
    bar = Bar()
    assert _d(bar) == {'name': '?'}
    bar.name = 'bar'
    bar.foo = foo
    assert _d(bar) == {'foo': foo, 'foo_id': 1, 'name': 'bar'}

    assert bar.name == 'bar'
    bar.restore_attrs()
    assert _d(bar) == {'name': '?'}
    assert bar.name == '?'

    bar = instantiate(Bar, id=2, foo_id=1, name='bar')
    assert bar.foo_id == 1
    assert bar.name == 'bar'
    assert _d(bar) == {'foo_id': 1}

    bar.foo_id = 1
    bar.name = 'bar'
    assert _d(bar) == {'foo_id': 1, 'name': 'bar'}

    bar.restore_attrs()
    assert _d(bar) == {}
    assert bar.foo_id == 1
    assert bar.name == 'bar'
    assert _d(bar) == {'foo_id': 1}

    bar.foo = None
    assert _d(bar) == {'foo': None, 'foo_id': None}
    assert bar.foo_id is None
    bar.restore_attrs()
    assert _d(bar) == {}
    assert bar.foo_id == 1

    bar.foo = foo
    assert _d(bar) == {'foo': foo, 'foo_id': 1}
    assert bar.foo_id == 1
    bar.restore_attrs()
    assert _d(bar) == {'foo': foo}
    assert bar.foo_id == 1

    bar.foo = foo2 = instantiate(Foo, id=2)
    assert _d(bar) == {'foo': foo2, 'foo_id': 2}
    assert bar.foo_id == 2
    bar.restore_attrs()
    assert _d(bar) == {}
    assert bar.foo_id == 1
コード例 #3
0
def test_dict():
    Base, User, Foo, Bar, Baz = models()
    foo = instantiate(Foo, id=1, a=2, b=3)
    foo['b'] = 4
    foo['c'] = '5'
    assert foo.dict() == {'id': 1}
    assert foo.dict(True) == {'id': 1, 'a': 2, 'b': 4, 'c': '5'}
コード例 #4
0
def test_attr_in_db():
    Base, User, Foo, Bar, Baz = models()
    bar = instantiate(Bar, id=1, foo_id=2, chks=3)
    bar['foo'] = Foo(id=3)
    bar['chks'] = ['1', '4']
    assert bar.foo_id == 3
    assert bar.chks == [bar.CHKS.A, bar.CHKS.C]
    assert bar.attr_in_db('foo_id') == 2
    assert bar.attr_in_db('chks') == [bar.CHKS.A, bar.CHKS.B]
    assert bar.chks == [bar.CHKS.A, bar.CHKS.C]
コード例 #5
0
def test_delete():
    Base, User, Foo, Bar, Baz = models()
    bar = instantiate(Bar, id=100)
    assert bar.id == 100
    bar.id = 200
    assert bar.id == 200

    args = []
    Base.DB.execute = lambda *a: args.append(a) or 1
    assert bar.delete() == 1
    assert args == [('DELETE t1 FROM bars t1 WHERE id = %s', (100,), int)]

    with pytest.raises(ColumnNotLoaded) as e:
        Bar().delete()
    assert e.value.args == ('id',)
コード例 #6
0
def test_choices():
    class Base(model.Model):
        DB = database.Database()

    class Foo(Base):
        DB_TABLE = 'foos'
        abc = INT(
            choices=XEnum(
                qw('value label title'),
                A=(1, 'AA', 'aaa'),
                B=(2, 'BB', 'bbb'),
                C=(4, 'CC', 'ccc'),
            ),
            multiple=True,
            default=3,
            null=True,
        )
        efg = INT(choices={
            1: 'ONE',
            2: 'TWO',
            4: 'FOUR'
        },
                  multiple=True,
                  default=2,
                  null=True)
        xyz = VARCHAR(choices=XEnum(X='x', Y='y', Z='z'),
                      default='x',
                      null=True)
        flag = BOOL(choices={
            True: 'ON',
            False: 'OFF'
        },
                    default=True,
                    null=True)

    assert '%r' % Foo.ABC.A == 'Foo.ABC.A'

    foo = Foo()
    assert foo.abc_choices() == ['AA', 'BB']
    assert foo.abc_choices('title') == ['aaa', 'bbb']
    assert foo.efg_choices() == ['TWO']
    assert foo.xyz is Foo.XYZ.X
    assert foo.xyz_choice() == 'X'
    assert foo.flag_choice() == 'ON'
    assert foo.is_valid()

    foo['abc'] = ['A', 'AA', '2']
    foo['efg'] = ['4', '3', 'TWO']
    foo['xyz'] = 'Z'
    foo['flag'] = 'f'
    assert foo.abc == ['A', 'AA', Foo.ABC.B]
    assert foo.abc_choices() == ['BB']
    assert foo.abc_choices('title') == ['bbb']
    assert foo.efg == [4, '3', 'TWO']
    assert foo.efg_choices() == ['FOUR']
    assert foo.xyz == 'Z'
    assert foo.xyz_choice() is None
    assert foo.flag == 'f'
    assert foo.flag_choice() is None
    assert not foo.is_valid()
    assert foo.errors == {
        'abc': [errors.INVALID],
        'efg': [errors.INVALID],
        'xyz': [errors.BAD_FORMAT],
        'flag': [errors.BAD_FORMAT],
    }

    foo = instantiate(Foo, abc=None, efg=None, xyz=None, flag=None)
    assert foo.abc is None
    assert foo.abc_choices() == []
    assert foo.efg is None
    assert foo.efg_choices() == []
    assert foo.xyz is None
    assert foo.xyz_choice() is None
    assert foo.flag is None
    assert foo.flag_choice() is None
    foo['flag'] = ''
    assert foo.is_valid()
    assert not foo.is_changed()

    foo = instantiate(Foo, abc=0, efg=0, xyz='x', flag=0)
    assert foo.abc == foo.efg == []
    assert foo.xyz is foo.XYZ.X
    assert foo.flag is False

    foo['flag'] = '1'
    assert foo.flag is True
    assert foo.is_changed()
    foo['flag'] = '0'
    assert not foo.is_changed()

    foo['xyz'] = ''
    assert foo.xyz is None
    assert foo.is_changed()

    foo['xyz'] = 'y'
    assert foo.xyz is Foo.XYZ.Y

    foo = instantiate(Foo, abc=7, efg=7, xyz='z', flag=1)
    assert foo.abc == [Foo.ABC.A, Foo.ABC.B, Foo.ABC.C]
    assert foo.efg == [1, 2, 4]
    assert foo.xyz is foo.XYZ.Z
    assert foo.flag is True

    foo['abc'] = foo['efg'] = ''
    assert foo.abc == ''
    assert foo.efg == ''
    assert not foo.is_valid()
    assert foo.errors == {
        'abc': [errors.BAD_FORMAT],
        'efg': [errors.BAD_FORMAT],
    }

    foo['abc'] = foo['efg'] = []
    assert foo.abc == []
    assert foo.attr_in_db('abc') == [Foo.ABC.A, Foo.ABC.B, Foo.ABC.C]
    assert foo.efg == []
    assert foo.attr_in_db('efg') == [1, 2, 4]
    assert foo.is_valid()
    assert foo.is_changed(txn={}) == {'abc': 0, 'efg': 0}

    foo['abc'] = foo['efg'] = None
    assert foo.abc is foo.efg is None
    assert foo.is_valid()
    assert foo.is_changed(txn={}) == {'abc': None, 'efg': None}

    with pytest.raises(TypeError) as e:

        class E1(Base):
            DB_TABLE = 'xs'
            flag = BOOL(choices={1: 1}, multiple=True)

    assert e.value.args == ('E1.flag: multiple and db2py are incompossible', )

    with pytest.raises(TypeError) as e:

        class E2(Base):
            DB_TABLE = 'xs'
            abc = INT(choices=XEnum(
                A=(1, 'AA'),
                B=(3, 'BB'),
            ), multiple=True)

    assert e.value.args == ('E2.abc: invalid bit E2.ABC.B', )

    with pytest.raises(TypeError) as e:

        class E3(Base):
            DB_TABLE = 'xs'
            abc = INT(choices={1: 'AA', 3: 'BB'}, multiple=True)

    assert e.value.args == ('E3.abc: invalid bit 3', )

    with pytest.raises(TypeError) as e:

        class E4(Base):
            DB_TABLE = 'xs'
            flag = BOOL(choices=XEnum(T=1))

    assert e.value.args == ('E4.flag: XEnum and db2py are incompossible', )

    a = Foo.ABC.A
    assert a == 1
    assert a.name == 'A'
    assert a.value == 1
    assert a.label == 'AA'
    assert a.title == 'aaa'
    assert dict(Foo.ABC.items()) == {1: 'AA', 2: 'BB', 4: 'CC'}
    assert dict(Foo.ABC.items('title')) == {1: 'aaa', 2: 'bbb', 4: 'ccc'}
    assert [*Foo.ABC] == [Foo.ABC.A, Foo.ABC.B, Foo.ABC.C]

    assert Foo.abc.choices is Foo.ABC
    assert Foo.flag.choices == {True: 'ON', False: 'OFF'}

    foo = instantiate(Foo, abc=3, flag=1)
    assert foo.abc == [foo.ABC.A, foo.ABC.B] == [1, 2]
    assert foo.flag is True
    assert foo.flag_choice() == 'ON'

    foo['abc'] = ['2', '4']
    assert foo.abc == [foo.ABC.B, foo.ABC.C] == [2, 4]
    assert foo.attr_in_db('abc') == [foo.ABC.A, foo.ABC.B] == [1, 2]
    assert foo.abc == [foo.ABC.B, foo.ABC.C] == [2, 4]

    foo['flag'] = '0'
    assert foo.flag is False

    assert foo.is_changed(txn={}) == {'abc': 6, 'flag': False}

    foo = Foo()
    assert foo.abc == [foo.ABC.A, foo.ABC.B]
    assert foo.flag is True
    assert foo.is_valid()

    foo['abc'] = ['1', '2']
    foo['flag'] = 1
    assert foo.abc == [foo.ABC.A, foo.ABC.B] == [1, 2]
    assert foo.flag is True
    assert foo.is_valid()

    foo['flag'] = False
    assert foo.flag is False

    foo = Foo()
    foo['xyz'] = ['X']
    assert not foo.is_valid()
    assert foo.errors == {'xyz': [errors.BAD_FORMAT]}
コード例 #7
0
def test_BELONGS_TO():
    class Base(model.Model):
        DB = database.Database()

    class Foo(Base):
        DB_TABLE = 'foos'
        id = INT(auto_increment=True, primary_key=True)

    class Bar(Base):
        DB_TABLE = 'bars'
        id = INT(auto_increment=True, primary_key=True)
        foo_id = BELONGS_TO(Foo)

    assert '%r' % Bar.id == 'Bar.id'
    assert '%r' % Bar.foo_id == 'Bar.foo_id'

    bar = Bar()
    assert bar.foo_id is None
    assert bar.__dict__ == {}
    assert 'foo_id' not in bar

    foo = bar.foo = instantiate(Foo, id=10)
    assert bar.__dict__ == {'foo': foo, 'foo_id': 10}
    assert 'foo_id' in bar
    assert bar.foo_id == 10
    assert bar.foo is foo

    foo.id = 11
    assert bar.__dict__ == {'foo': foo, 'foo_id': 10}
    assert bar.foo_id == 10
    assert bar.is_changed(txn={}) == {'foo_id': 10}
    assert bar.__dict__ == {'foo': foo, 'foo_id': 10}

    bar.foo_id = 11
    assert bar.__dict__ == {'foo': foo, 'foo_id': 11}
    assert bar.foo_id == 11
    assert bar.foo is foo

    bar.foo_id = 12
    assert bar.__dict__ == {'foo_id': 12}
    assert 'foo' not in bar.__dict__
    assert bar.foo_id == 12

    bar = Bar()
    assert bar.foo_id is None
    foo = bar.foo = Foo()
    foo.id = 13
    assert bar.foo_id == 13
    assert bar.__dict__ == {'foo': foo}

    bar = Bar()
    assert not bar.is_valid()
    assert 'foo_id' not in bar

    bar = Bar()
    foo = bar.foo = Foo()
    assert bar.__dict__ == {'foo': foo}
    assert 'foo_id' not in bar
    assert bar.is_valid()

    del bar.__dict__['foo']
    assert not bar.is_valid()
    del bar.__dict__['errors']

    bar.foo = None
    assert bar.__dict__ == {'foo': None, 'foo_id': None}
    assert not bar.is_valid()

    bar.foo_id = 'abc'
    assert bar.foo is None
    assert not bar.is_valid()

    assert Bar.foo_id.db_type() == 'INT'

    class DatePk(Base):
        DB_TABLE = 'date_pks'
        id = DATE(primary_key=True)

    class StrPk(Base):
        DB_TABLE = 'str_pks'
        id = VARCHAR(primary_key=True)

    class Foo(Base):
        DB_TABLE = 'foos'
        id = SERIAL()
        date_pk_id = BELONGS_TO(DatePk)
        str_pk_id = BELONGS_TO(StrPk)

    assert Foo.ddl_sqls() == [
        'CREATE TABLE foos (\n'
        '  id SERIAL PRIMARY KEY,\n'
        '  date_pk_id DATE NOT NULL,\n'
        '  str_pk_id VARCHAR(255) NOT NULL,\n'
        '  FOREIGN KEY (date_pk_id) REFERENCES date_pks (id),\n'
        '  FOREIGN KEY (str_pk_id) REFERENCES str_pks (id)\n'
        ')', 'CREATE INDEX foos_date_pk_id ON foos (date_pk_id)',
        'CREATE INDEX foos_str_pk_id ON foos (str_pk_id)'
    ]

    foo = Foo().set_items(date_pk_id='2020-01-27', str_pk_id='aaa')
    assert foo.date_pk_id == Date('2020-01-27')
    assert foo.is_valid()
コード例 #8
0
def test_Column():
    class Base(model.Model):
        DB = database.Database()

    class Foo(Base):
        DB_TABLE = 'foos'
        id = INT(auto_increment=True, primary_key=True)
        name = VARCHAR()

    # __get__
    assert '%r' % Foo.id == 'Foo.id'
    assert '%r' % Foo.name == 'Foo.name'

    foo = Foo()
    assert foo.id is None
    assert 'id' not in foo.__dict__
    assert foo.name is None

    foo.id = 101
    assert foo.id == 101
    assert foo.__dict__['id'] == 101

    foo = instantiate(Foo, id=100, name='foo')
    assert set(foo.__dict__) == {'.k2i', '.dbvs'}
    assert foo.id == 100
    assert foo.name == 'foo'
    assert set(foo.__dict__) == {'.k2i', '.dbvs'}

    foo = instantiate(Foo)
    with pytest.raises(ColumnNotLoaded):
        foo.id

    cnt = [0]

    def db2py(v):
        cnt[0] += 1
        return v + 1

    Foo.id.db2py = db2py

    foo = instantiate(Foo, id=100)
    assert cnt == [0]
    assert foo.id == 101
    assert cnt == [1]
    assert 'id' in foo.__dict__
    assert foo.id == 101
    assert cnt == [1]

    foo = instantiate(Foo, id=100)
    foo.id = 10
    assert 'id' in foo.__dict__
    assert foo.id == 10
    assert cnt == [1]

    Foo.id.db2py = None

    # __init__
    with pytest.raises(TypeError):
        INT(foo=1)

    # _in_
    foo = Foo()
    assert 'id' not in foo
    foo.id = 100
    assert 'id' in foo
    foo = instantiate(Foo, id=100)
    assert 'id' in foo

    # is_changed
    foo = Foo()
    assert not foo.is_changed()
    assert not foo.is_changed('id')
    assert foo.__dict__ == {}

    foo.id = 10
    foo.name = 'f'
    assert foo.is_changed()
    assert foo.is_changed(txn={}) == dict(id=10, name='f')

    foo = instantiate(Foo, id=10)
    assert not foo.is_changed()
    foo.id = 10
    assert not foo.is_changed()
    foo.id = 11
    assert foo.is_changed()
    foo.name = 'f'
    assert foo.is_changed(txn={}) == dict(id=11, name='f')

    Foo.id.py2db = lambda x: x + 1
    assert foo.is_changed('id', txn={}) == {'id': 12}
    assert foo.id == 11
    Foo.id.py2db = None

    class Bar(Base):
        DB_TABLE = 'bars'
        foo_id = BELONGS_TO(Foo)

    foo = instantiate(Foo, id=100, name='foo')
    assert set(foo.__dict__) == {'.k2i', '.dbvs'}
    assert foo.id == 100
    assert foo.name == 'foo'
    assert set(foo.__dict__) == {'.k2i', '.dbvs', 'id'}

    foo = Foo()
    bar = Bar(foo=foo)
    assert bar.is_changed(txn={}) == dict(foo_id=None)
    foo.id = 1
    assert bar.is_changed(txn={}) == dict(foo_id=1)

    # to_be_valid
    assert not Foo().is_valid()
    assert Foo(name='?').is_valid()

    Foo.id.auto_increment = False
    assert not Foo(name='?').is_valid()
    assert Foo(id=1, name='?').is_valid()

    assert not Foo(id=1).is_valid()
    Foo.name.validate['nn'] = False
    assert Foo(id=1).is_valid()

    foo = Foo.instantiate({}, (), None, None)
    assert foo.is_valid()
    foo.id = None
    assert not foo.is_valid()

    Foo.id.auto_increment = True
    Foo.name.validate['nn'] = True

    assert Foo.ddl_sqls() == [
        "CREATE TABLE foos (\n"
        "  id INT NOT NULL PRIMARY KEY,\n"
        "  name VARCHAR(255) NOT NULL\n)"
    ]
コード例 #9
0
def test_save():
    Base, User, Foo, Bar, Baz = models()

    args = []
    Base.DB.execute_insert = lambda *a: args.append(a) or 100
    assert Foo().save() == 1
    assert args == [('INSERT INTO foos () VALUES ()', (), 'id')]

    args.clear()
    bar = Bar()
    assert bar.save() == 1
    assert args == [(
        'INSERT INTO bars (created_at, updated_at, lock_version, name) '
        'VALUES (%s, %s, %s, %s)',
        (bar.created_at, bar.updated_at, 1, '?'), 'id')]
    assert bar.__dict__ == {
        '.k2i': bar.SHARED_INDEX,
        '.dbvs': [
            100, None, None, bar.created_at, None, bar.updated_at, 1,
            '?', None, None],
        'created_at': bar.created_at,
        'updated_at': bar.updated_at,
        'name': '?',
    }

    args.clear()
    Base.DB.execute = lambda *a: args.append(a) or 1
    baz = Baz(name='!')
    baz.insert()
    assert args == [('INSERT INTO bazs (name) VALUES (%s)', ('!',))]

    at = DateTime('2019-10-28 18:00:00')
    u10 = User(id=10)
    bar = instantiate(
        Bar, id=1, updated_by=10, updated_at=at, lock_version=1, name='A')
    assert bar.id == 1
    bar.updater = u10
    assert not bar.is_changed()

    args.clear()
    Base.DB.execute = lambda *a: args.append(a) or 1
    assert bar.save(by=User(id=11)) is bar.NO_CHANGES
    assert bar.NO_CHANGES and True
    assert not args
    assert bar.updated_by == 10
    assert bar.__dict__['updater'] is u10
    assert bar.updated_at == at
    assert bar.lock_version == 1
    assert not bar.is_changed()

    bar['id'] = '2'
    bar['name'] = 'B'
    assert bar.is_changed()
    assert bar.save(by=User(id=11), lock=False) == 1
    assert args == [(
        'UPDATE bars t1 SET id = %s, name = %s, updated_at = %s, '
        'updated_by = %s WHERE id = %s',
        (2, 'B', bar.updated_at, 11, 1), int)]
    assert bar.updated_by == 11
    assert 'updater' not in bar.__dict__
    assert bar.updated_at != at
    assert bar.lock_version == 1
    assert not bar.is_changed()

    args.clear()
    bar['lock_version'] = '1'
    assert bar.save(by=12) is bar.NO_CHANGES
    assert not args

    bar['lock_version'] = '2'
    assert bar.save(by=12) == 0
    assert bar.errors == {None: [errors.CONFLICT]}

    assert not args
    bar['name'] = 'C'
    bar['lock_version'] = '1'
    assert bar.save(by=12, timestamp=True) == 1
    assert args == [(
        'UPDATE bars t1 SET name = %s, lock_version = %s, updated_at = %s, '
        'updated_by = %s WHERE id = %s AND lock_version = %s',
        ('C', 2, bar.updated_at, 12, 2, 1), int)]
    assert bar.updated_by == 12
    assert bar.lock_version == 2
    with pytest.raises(TypeError) as e:
        bar.name = '!'
        bar.save(by='?')
    assert e.value.args == (
        "keyword argument 'by' must be int or User, not str",)

    args.clear()
    bar = instantiate(Bar, id=1)
    assert bar.__dict__ == {'.k2i': {'id': 0}, '.dbvs': (1,)}
    bar['name'] = 'D'
    assert bar.save(lock=True) == 1
    assert args == [(
        'UPDATE bars t1 SET name = %s, updated_at = %s, '
        'lock_version = (lock_version + 1) %% 100 WHERE id = %s',
        ('D', bar.updated_at, 1), int)]
    assert bar.updated_at is not None
    assert bar.__dict__ == {
        'name': 'D',
        '.k2i': {'id': 0, 'name': 1, 'updated_at': 2},
        '.dbvs': [1, 'D', bar.updated_at],
    }

    foo = instantiate(Foo, id=1)
    assert foo.save() is foo.NO_CHANGES
    assert foo.save(force=True) is foo.NO_CHANGES
コード例 #10
0
def test_BelongsTo():
    Foo, Bar, Bar2, Baz = models()
    assert Bar.foo.join_key == 'foo'
    assert Bar.foo.reverse_b2 is False
    assert Bar2.foo.reverse_b2 is False
    assert Bar2.bar.reverse_b2 is False
    assert Bar.foo.simple_joins == (('id', 'foo_id'), )

    # __get__
    assert '%r' % Bar.foo == 'Bar.foo'
    bar = Bar()
    assert bar.foo is None
    assert bar.__dict__ == {}

    foo = Foo()
    bar = Bar(foo=foo)
    assert bar.foo is foo

    bar = Bar(foo_id=2)
    assert 'foo' not in bar
    with pytest.raises(NotImplementedError):
        bar.foo
    assert 'foo' not in bar

    DB = Foo.DB
    DB.execute = r2csr(id=2)
    DB.find_cache = {}
    foo = bar.foo
    del DB.execute
    assert foo.id == 2
    assert bar.__dict__['foo'] is foo
    assert 'foo' in bar

    # __set__
    bar.foo_id = None
    assert 'foo' not in bar.__dict__
    assert bar.__dict__['foo_id'] is None
    assert bar.foo is None
    assert 'foo' not in bar

    with pytest.raises(TypeError) as e:
        bar.foo = 2
    assert e.value.args == ('must be Foo, not int', )

    bar.foo_id = 2
    assert 'foo' not in bar
    assert bar.foo is foo
    assert bar.__dict__['foo'] is foo

    # foo = instantiate(Foo, id=2)
    bar = instantiate(Bar, id=3, foo_id=2)
    bar2 = Bar2(foo=foo, bar=bar)
    assert 'foo' not in bar
    assert bar.foo is foo

    bar = instantiate(Bar, id=3, foo_id=2)
    bar2 = Bar2(bar=bar, foo=foo)
    assert 'foo' not in bar
    assert bar.foo is foo

    bar2 = Bar2(foo_id=2, bar_id=3)
    DB.execute = r2csr(id=3)
    assert bar2.bar.id == 3
    del DB.execute

    bar2 = Bar2(foo_id=foo.id)
    bar = bar2.bar = Bar(foo=foo)
    assert 'foo' not in bar2
    assert bar2.foo is foo
    assert 'foo' in bar2
    bar2.foo = Foo()
    assert 'foo_id' not in bar2

    assert Bar.bazs.query().sql() == SQL(
        'SELECT * FROM bazs bz WHERE bz.bar_id = ba.id')

    assert Bar.bazs.as_('t1').sql() == SQL(
        'SELECT * FROM bazs t1 WHERE t1.bar_id = ba.id')

    assert Bar.bazs.query('bz').sql() == SQL(
        'SELECT * FROM bazs WHERE bar_id = bz.id')