Skip to content
This repository has been archived by the owner on Apr 24, 2020. It is now read-only.

Commit

Permalink
[Refact] Merged Manager into Model.
Browse files Browse the repository at this point in the history
  • Loading branch information
ducdetronquito committed Jan 17, 2017
1 parent 44ffe5e commit 887b44a
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 116 deletions.
103 changes: 37 additions & 66 deletions plume/plume.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class SQLiteAPI:
NE = '!='
NOT = 'NOT'

opposites = {
invert = {
EQ: NE,
IN: ' '.join((NOT, IN)),
EXISTS: ' '.join((NOT, EXISTS)),
Expand All @@ -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)
Expand Down Expand Up @@ -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 = [
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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


Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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')
Expand Down
4 changes: 2 additions & 2 deletions tests/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
32 changes: 0 additions & 32 deletions tests/test_manager.py

This file was deleted.

20 changes: 8 additions & 12 deletions tests/test_model.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from plume.plume import Database, Manager, Model
from plume.plume import Database, Model

from utils import DB_NAME, Pokemon, Trainer

Expand All @@ -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

Expand All @@ -47,25 +43,25 @@ 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

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

def test_create_from_manager_allows_model_instance_as_parameter_for_foreign_key_field(self):
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
Expand All @@ -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)



Expand Down
7 changes: 5 additions & 2 deletions tests/test_updatequery.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
4 changes: 2 additions & 2 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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])

0 comments on commit 887b44a

Please sign in to comment.