From 887b44aa5f5cbd6fe2afe294e2bc124e947a2a5d Mon Sep 17 00:00:00 2001 From: ducdetronquito Date: Tue, 17 Jan 2017 11:50:57 +0100 Subject: [PATCH] [Refact] Merged Manager into Model. --- plume/plume.py | 103 ++++++++++++++------------------------ tests/test_fields.py | 4 +- tests/test_manager.py | 32 ------------ tests/test_model.py | 20 +++----- tests/test_updatequery.py | 7 ++- tests/utils.py | 4 +- 6 files changed, 54 insertions(+), 116 deletions(-) delete mode 100644 tests/test_manager.py diff --git a/plume/plume.py b/plume/plume.py index 11b0fed..46ef92e 100644 --- a/plume/plume.py +++ b/plume/plume.py @@ -58,7 +58,7 @@ class SQLiteAPI: NE = '!=' NOT = 'NOT' - opposites = { + invert = { EQ: NE, IN: ' '.join((NOT, IN)), EXISTS: ' '.join((NOT, EXISTS)), @@ -67,7 +67,7 @@ class SQLiteAPI: @classmethod def create_table(cls, name, fields): query = ( - cls.CREATE, cls.TABLE, cls.IF, cls.invert_operator(cls.EXISTS), + cls.CREATE, cls.TABLE, cls.IF, cls.invert[cls.EXISTS], name.lower(), cls.to_csv(fields, bracket=True), ) return ' '.join(query) @@ -98,10 +98,6 @@ def insert_into(cls, table_name, field_names): ) return ' '.join(query) - @classmethod - def invert_operator(cls, operator): - return cls.opposites[operator] - @classmethod def select(cls, tables, fields=None, where=None, count=None, offset=None): query = [ @@ -172,7 +168,7 @@ def __iand__(self, other): return Expression(self, SQLiteAPI.AND, Node.check(other)) def __invert__(self): - self.op = SQLiteAPI.invert_operator(self.op) + self.op = SQLiteAPI.invert[self.op] return self def __le__(self, other): @@ -387,56 +383,6 @@ def set(self, *args): return self -class Manager: - __slots__ = ('_model',) - - def __init__(self, model): - self._model = model - - def __get__(self, instance, owner): - # A Model Manager is only accessible as a class attribute. - if instance is None: - return self - - def create(self, **kwargs): - """Return an instance of the related model.""" - field_names = [fieldname for fieldname in self._model._fieldnames if fieldname in kwargs] - values = [kwargs[fieldname] for fieldname in self._model._fieldnames if fieldname in kwargs] - - values = [value.pk if isinstance(value, Model) else value for value in values] - - query = SQLiteAPI.insert_into(self._model.__name__, field_names) - - last_row_id = self._model._db.insert_into(query, values) - kwargs = {field: value for field, value in zip(field_names, values)} - kwargs['pk'] = last_row_id - instance = self._model(**kwargs) - - return instance - - def delete(self, *args): - return DeleteQuery(model=self._model).where(*args) - - def select(self, *args): - return SelectQuery(model=self._model).select(*args) - - def update(self, *args): - return UpdateQuery(model=self._model).set(*args) - - def where(self, *args): - return SelectQuery(model=self._model).where(*args) - - - -class RelatedManager(Manager): - __slots__ = () - - def __get__(self, instance, owner): - # A RelatedManager is only accessible as an instance attribute. - if instance is not None: - return self - - class BaseModel(type): def __new__(cls, clsname, bases, attrs): fieldnames = set() @@ -469,18 +415,10 @@ def __new__(cls, clsname, bases, attrs): # Create the new class. new_class = super().__new__(cls, clsname, bases, attrs) - #Add a Manager instance as an attribute of the Model class. - setattr(new_class, 'objects', Manager(new_class)) - # Each field of the class knows its related model class name. for fieldname in new_class._fieldnames: getattr(new_class, fieldname).model_name = clsname.lower() - # Add a Manager to each related Model. - for attr_name, attr_value in related_fields: - setattr(attr_value.related_model, attr_value.related_field, RelatedManager(new_class)) - - return new_class @@ -604,7 +542,7 @@ def __get__(self, instance, owner): if instance is not None: related_pk_value = getattr(instance._values, self.name) related_pk_field = getattr(self.related_model, 'pk') - return self.related_model.objects.where(related_pk_field == related_pk_value)[0] + return self.related_model.where(related_pk_field == related_pk_value)[0] else: return self @@ -648,6 +586,39 @@ def __str__(self): def __eq__(self, other): return self._values == other._values + @classmethod + def create(cls, **kwargs): + """Return an instance of the related model.""" + field_names = [fieldname for fieldname in cls._fieldnames if fieldname in kwargs] + values = [kwargs[fieldname] for fieldname in cls._fieldnames if fieldname in kwargs] + + values = [value.pk if isinstance(value, Model) else value for value in values] + + query = SQLiteAPI.insert_into(cls.__name__, field_names) + + last_row_id = cls._db.insert_into(query, values) + kwargs = {field: value for field, value in zip(field_names, values)} + kwargs['pk'] = last_row_id + instance = cls(**kwargs) + + return instance + + @classmethod + def delete(cls, *args): + return DeleteQuery(model=cls).where(*args) + + @classmethod + def select(cls, *args): + return SelectQuery(model=cls).select(*args) + + @classmethod + def update(cls, *args): + return UpdateQuery(model=cls).set(*args) + + @classmethod + def where(cls, *args): + return SelectQuery(model=cls).where(*args) + class Database: __slots__ = ('db_name', '_connection') diff --git a/tests/test_fields.py b/tests/test_fields.py index d9e21d6..c022de5 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -98,8 +98,8 @@ def test_is_slotted(self): def test_store_pk_of_a_model_instance(self): db = Database(DB_NAME) db.register(Trainer, Pokemon) - james = Trainer.objects.create(pk=1, name='James', age=21) - meowth = Pokemon.objects.create(name='Meowth', level=3, trainer=james) + james = Trainer.create(pk=1, name='James', age=21) + meowth = Pokemon.create(name='Meowth', level=3, trainer=james) assert meowth.trainer.pk == 1 def test_for_create_table_query_sql_output_a_list_of_keywords(self): diff --git a/tests/test_manager.py b/tests/test_manager.py deleted file mode 100644 index 7a18def..0000000 --- a/tests/test_manager.py +++ /dev/null @@ -1,32 +0,0 @@ -from plume.plume import Database, Manager, Model, SelectQuery -from utils import DB_NAME, Pokemon, Trainer - -import pytest - - -class TestManagerAPI: - - def test_is_slotted(self): - with pytest.raises(AttributeError): - Manager(Model).__dict__ - - def test_is_accessible_as_Model_class_attribute(self): - assert Trainer.objects is not None - - def test_is_not_accessible_as_Model_instance_attribute(self): - with pytest.raises(AttributeError): - Trainer().objects - - def test_filter_returns_a_queryset(self): - assert isinstance(Trainer.objects.where(), SelectQuery) - - def test_select_returns_a_queryset(self): - assert isinstance(Trainer.objects.select(), SelectQuery) - - - def test_create_needs_named_parameters(self): - with pytest.raises(TypeError): - Trainer.objects.create('name', 'age') - - - diff --git a/tests/test_model.py b/tests/test_model.py index 7753a3a..9c789e8 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -1,4 +1,4 @@ -from plume.plume import Database, Manager, Model +from plume.plume import Database, Model from utils import DB_NAME, Pokemon, Trainer @@ -17,10 +17,6 @@ def test_fieldnames_class_attribute_contains_primary_key_field_name(self): assert len(Model._fieldnames) == 1 assert 'pk' in Model._fieldnames - def test_objects_attribute_links_to_a_class_specific_manager(self): - assert isinstance(Model.objects, Manager) is True - assert Model.__name__ == Model.objects._model.__name__ - def test_pk_field_is_empty_by_default_on_model_instance(self): assert Model().pk is None @@ -47,7 +43,7 @@ def test_model_is_sloted(self): def test_create_from_manager_returns_an_instance_with_pk_set(self): db = Database(DB_NAME) db.register(Trainer) - giovanni = Trainer.objects.create(name='Giovanni', age=42) + giovanni = Trainer.create(name='Giovanni', age=42) assert giovanni.pk == 1 assert giovanni.name == 'Giovanni' assert giovanni.age == 42 @@ -55,8 +51,8 @@ def test_create_from_manager_returns_an_instance_with_pk_set(self): def test_create_from_manager_returns_an_instance_with_pk_set_to_next_available_id(self): db = Database(DB_NAME) db.register(Trainer) - giovanni = Trainer.objects.create(name='Giovanni', age=42) - james = Trainer.objects.create(name='James', age=21) + giovanni = Trainer.create(name='Giovanni', age=42) + james = Trainer.create(name='James', age=21) assert giovanni.pk == 1 assert james.pk == 2 @@ -64,8 +60,8 @@ def test_create_from_manager_allows_model_instance_as_parameter_for_foreign_key_ db = Database(DB_NAME) db.register(Trainer, Pokemon) - james = Trainer.objects.create(name='James', age=21) - meowth = Pokemon.objects.create(name='Meowth', level=19, trainer=james) + james = Trainer.create(name='James', age=21) + meowth = Pokemon.create(name='Meowth', level=19, trainer=james) assert james.pk == 1 assert meowth.trainer.pk == james.pk @@ -74,10 +70,10 @@ def test_create_from_manager_checks_for_integrity(self): db = Database(DB_NAME) db.register(Trainer, Pokemon) - james = Trainer.objects.create(name='James', age=21) + james = Trainer.create(name='James', age=21) with pytest.raises(sqlite3.IntegrityError): - Pokemon.objects.create(name='Meowth', level=19, trainer=2) + Pokemon.create(name='Meowth', level=19, trainer=2) diff --git a/tests/test_updatequery.py b/tests/test_updatequery.py index 64f2cbd..6da0be6 100644 --- a/tests/test_updatequery.py +++ b/tests/test_updatequery.py @@ -37,8 +37,11 @@ def test_updates_rules_can_be_chained(self): query.set(Trainer.age == 18).execute() def test_can_output_selectquery_as_string(self): - result = str(Trainer.objects.where(Trainer.age > 18)) - expected = "(SELECT * FROM trainer WHERE trainer.age > 18)" + result = str(UpdateQuery(Trainer).set(Trainer.name == 'Newbie').where(Trainer.age < 18)) + assert ( + '[NEED FIX] Provide a Class to handle (Expression, Expression, Expression)' + ) is False + expected = "(UPDATE trainer SET trainer.name = 'Newbie' WHERE trainer.age > 18)" assert result == expected def test_update_one_field_on_all_rows(self): diff --git a/tests/utils.py b/tests/utils.py index e93a97b..210428f 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -61,7 +61,7 @@ def add_trainer(self, names): pass for name in names: - Trainer.objects.create(**self.TRAINERS[name]) + Trainer.create(**self.TRAINERS[name]) def add_pokemon(self, names): try: @@ -70,4 +70,4 @@ def add_pokemon(self, names): pass for name in names: - Pokemon.objects.create(**self.POKEMONS[name]) + Pokemon.create(**self.POKEMONS[name])