def test_multi_default_1(init_world, get_client): ''' friends - always childred 1 & 2 ''' init_world(family.dm) client = get_client() # 1. Add best_friend field. child = dm().object('child') child.add_field(Rel('friends', stores=child, multi=True, default=[1, 2])) # If this is PG storage, we need to update database schema if issubclass(type(world().storage), PGStorage): conn = world().storage._conn conn.cursor().execute('ALTER TABLE child ADD COLUMN friends integer[]') conn.commit() # 2. Post a child without friends data, status, headers = client.post('child', {'name': 'c'}) assert status == 201 assert data['friends'] == [1, 2] # 3. Post a child with friends data, status, headers = client.post('child', { 'name': 'c', 'friends': [2, 3] }) assert status == 201 assert data['friends'] == [2, 3]
def test_single_default_1(init_world, get_client): ''' Best Friend - always child 1 ''' init_world(family.dm) client = get_client() # 1. Add best_friend field. child = dm().object('child') child.add_field(Rel('best_friend', stores=child, multi=False, default=1)) # If this is PG storage, we need to update database schema if issubclass(type(world().storage), PGStorage): conn = world().storage._conn conn.cursor().execute( 'ALTER TABLE child ADD COLUMN best_friend integer REFERENCES child(id) DEFERRABLE' ) conn.commit() # 2. Post a child without best friend data, status, headers = client.post('child', {'name': 'c'}) assert status == 201 assert data['best_friend'] == 1 # 3. Post a child with best friend data, status, headers = client.post('child', { 'name': 'c', 'best_friend': 2 }) assert status == 201 assert data['best_friend'] == 2
def init_cookies_with_shelf_1(init_world): '''Add shelf to cookies datamodel. Shelf knows it's jars, jar has no idea about shelf ''' dm = deepcopy(cookies.dm) shelf = dm.create_object('shelf') shelf.add_field(Scalar('id', pkey=True, type_=int)) shelf.add_field(Rel('jars', stores=dm.object('jar'), multi=True)) init_world(dm) # If this is PG storage, we need to update database schema if issubclass(type(world().storage), PGStorage): conn = world().storage._conn conn.cursor().execute('CREATE TABLE shelf (id serial PRIMARY KEY, jars integer[])') conn.commit()
def init_cookies_with_shelf_2(init_world): '''Add shelf to cookies datamodel. Jar knows it's shelf, shelf has no idea about jars. ''' dm = deepcopy(cookies.dm) shelf = dm.create_object('shelf') shelf.add_field(Scalar('id', pkey=True, type_=int)) jar = dm.object('jar') jar.add_field(Rel('shelf', stores=shelf, multi=False)) init_world(dm) # If this is PG storage, we need to update database schema if issubclass(type(world().storage), PGStorage): conn = world().storage._conn conn.cursor().execute('CREATE TABLE shelf (id serial PRIMARY KEY)') conn.cursor().execute('ALTER TABLE jar ADD COLUMN shelf integer REFERENCES shelf(id)') conn.commit()
def test_single_multi_2(init_world, get_client): ''' Best Friend - new female named BLARGH (note: new anonymous child creates infinite recursion) ''' init_world(family.dm) client = get_client() # 1. Add best_friend field. New child's best friend is always child no 1. child = dm().object('child') child.add_field( Rel('best_friend', stores=dm().object('female'), multi=False, default={'name': 'BLARGH'})) # If this is PG storage, we need to update database schema if issubclass(type(world().storage), PGStorage): conn = world().storage._conn conn.cursor().execute( 'ALTER TABLE child ADD COLUMN best_friend integer REFERENCES child(id) DEFERRABLE' ) conn.commit() # 2. Post a child without best friend data, status, headers = client.post('child', {'name': 'c'}) assert status == 201 assert data['best_friend'] == 3 # 3. Check if name was saved data, status, headers = client.get('female', 3) assert status == 200 assert data['name'] == 'BLARGH' # 4. Post a child with best friend data, status, headers = client.post('child', { 'name': 'c', 'best_friend': 2 }) assert status == 201 assert data['best_friend'] == 2
def test_multi_default_2(init_world, get_client): ''' Friends - females named BLARGH and BLERGH ''' init_world(family.dm) client = get_client() # 1. Add friends field - two fresh females, BLARGH and BLERGH child = dm().object('child') child.add_field( Rel('friends', stores=dm().object('female'), multi=True, default=[{ 'name': 'BLARGH' }, { 'name': 'BLERGH' }])) # If this is PG storage, we need to update database schema if issubclass(type(world().storage), PGStorage): conn = world().storage._conn conn.cursor().execute('ALTER TABLE child ADD COLUMN friends integer[]') conn.commit() # 2. Post a child without best friend data, status, headers = client.post('child', {'name': 'c'}) assert status == 201 assert data['friends'] == [3, 4] # 3. Post a child with best friend data, status, headers = client.post('child', { 'name': 'c', 'friends': [1, 2] }) assert status == 201 assert data['friends'] == [1, 2]
# Create 'cookie' object. Each cookie has # * id, which is an integer primary key # * type, which is a string cookie = dm.create_object('cookie') cookie.add_field(Scalar('id', pkey=True, type_=int)) cookie.add_field(Scalar('type', type_=str)) # Create 'jar' object. The only field is id, # which is also an integer primary key jar = dm.create_object('jar') jar.add_field(Scalar('id', pkey=True, type_=int)) # Add a relational field to cookie. # This field ('jar') can hold at most one jar (and nothing else) cookie.add_field(Rel('jar', stores=jar, multi=False)) # Jar gets 'cookies' field, where any number of cookies can be held. jar.add_field(Rel('cookies', stores=cookie, multi=True)) # Until now, fields cookie.jar and jar.cookie could mean totally different things, # e.g. cookie.jar could be a jar with ingredients required to bake this cookie, # and jar.cookies could be a list of all cookies that could fit in the jar. # # Now we declare that this is the same relationship: if certain cookie has certain # jar on its 'jar' field, this jar also has this cookie among its 'cookies'. dm.connect(jar, 'cookies', cookie, 'jar') # dm object contains whole data model definition __all__ = [dm]
def _create_relationships(self): '''Create all relationships Note: Columns that are simultaneously PRIMARY KEY and FOREIGN KEY are forbidden. This simplification is requred by current DataModel, where relationship fields are not allowed to be primary keys. This might change in the future. Note 2: Each relationship is between "two tables", but table might reference itself, so this are not necesary two different tables. Possible relationships could be divided into two groups: A) Without 'join' table Relationships without additional 'join table' are defined by single FOREIGN KEY column. Owner of this column will be called 'child', table pointed by FK constraint - 'parent'. Possible relationships: CHILD PARENT CONSTRAINT 0 .. n 0 .. 1 FOREIGN KEY 0 .. n 1 .. 1 FOREIGN KEY NOT NULL 0 .. 1 0 .. 1 FOREIGN KEY UNIQUE 0 .. 1 1 .. 1 FOREIGN KEY NOT NULL UNIQUE B) With 'join' table All other relationships that could be clearly defined by database structure require additional join table (reminder: PRIMARY KEY FOREIGN KEY columns are forbidden). Such relationships are not handled it any clever way. Join table representes additional object, referenced in a "simple" way (described in A) by both other tables. ''' # For each foreign key field we create two Rel fields and connect them. # "Child" is - as above - object with FK field. for child_name, column_names in self._fk_columns.items(): for child_column_name in column_names: # relationship data, check Query.get_rel_data for description parent_name, child_card, parent_card, child_cascade = \ self._q.get_rel_data(self._name, child_name, child_column_name) # objects child = self._dm.object(child_name) parent = self._dm.object(parent_name) # "multi" fields are '*'/'+', single are '?' and '1' child_multi = child_card in ('*', '+') parent_multi = parent_card in ('*', '+') # name of the other side virtual (-> not a column) field parent_column_name = self._default_parent_field_name( child, parent, child_card, child_column_name) # create fields child.add_field( Rel(child_column_name, stores=parent, multi=parent_multi, cascade=child_cascade)) parent.add_field( Rel(parent_column_name, stores=child, multi=child_multi, cascade=False)) # and connect them self._dm.connect(child, child_column_name, parent, parent_column_name)
# OBJECTS & SCALAR FIELDS male = dm.create_object('male') male.add_field(Scalar('id', pkey=True, type_=int)) male.add_field(Scalar('name', type_=str)) female = dm.create_object('female') female.add_field(Scalar('id', pkey=True, type_=int)) female.add_field(Scalar('name', type_=str)) child = dm.create_object('child') child.add_field(Scalar('id', pkey=True, type_=int)) child.add_field(Scalar('name', type_=str)) # REL FIELDS male.add_field(Rel('wife', stores=female, multi=False)) # noqa: E241 male.add_field(Rel('children', stores=child, multi=True)) # noqa: E241 female.add_field(Rel('husband', stores=male, multi=False)) # noqa: E241 female.add_field(Rel('children', stores=child, multi=True)) # noqa: E241 child.add_field(Rel('father', stores=male, multi=False)) # noqa: E241 child.add_field(Rel('mother', stores=female, multi=False)) # noqa: E241 # CONNECTIONS dm.connect(male, 'wife', female, 'husband') # noqa: E241 dm.connect(child, 'father', male, 'children') # noqa: E241 dm.connect(child, 'mother', female, 'children') # noqa: E241 # CALC FIELDS def url(instance): id_ = instance.id()