diff --git a/.gitignore b/.gitignore index 6c3d924..5bf036c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ __pycache__/* plume.egg-info/* +.cache/* *.pyc *.db diff --git a/tests/clause.py b/tests/clause.py deleted file mode 100644 index 17d7f1a..0000000 --- a/tests/clause.py +++ /dev/null @@ -1,59 +0,0 @@ -from plume import * -from tests.utils import Pokemon - -from unittest import TestCase - - -class ClauseTests(TestCase): - - def test_allows_or_operator_between_two_clauses(self): - clause = (Pokemon.name == 'Charamander') | (Pokemon.name == 'Bulbasaur') - - self.assertEqual( - "( name = 'Charamander' OR name = 'Bulbasaur' )", - str(clause), - ) - - def test_or_operator_between_two_clauses_add_brackets(self): - clause = (Pokemon.name == 'Charamander') | (Pokemon.name == 'Bulbasaur') - clause_str = str(clause) - - self.assertEqual(clause_str[0], '(') - self.assertEqual(clause_str[-1], ')') - - def test_allows_and_operator_between_two_clauses(self): - clause = (Pokemon.name == 'Charamander') & (Pokemon.level == 18) - - self.assertEqual( - "name = 'Charamander' AND level = 18", - str(clause), - ) - - def test_and_operator_between_two_clauses_does_not_add_brackets(self): - clause = (Pokemon.name == 'Charamander') & (Pokemon.level == 18) - clause_str = str(clause) - - self.assertNotEqual(clause_str[0], '(') - self.assertNotEqual(clause_str[-1], ')') - - def test_or_operator_has_lower_precedence_than_and_operator(self): - clause = ( - (Pokemon.name == 'Charamander') | (Pokemon.name == 'Bulbasaur') - & (Pokemon.level > 18) - ) - - self.assertEqual( - "( name = 'Charamander' OR name = 'Bulbasaur' AND level > 18 )", - str(clause), - ) - - def test_bracket_has_higher_precedence_than_and_operator(self): - clause = ( - ((Pokemon.name == 'Charamander') | (Pokemon.name == 'Bulbasaur')) - & (Pokemon.level > 18) - ) - - self.assertEqual( - "level > 18 AND ( name = 'Charamander' OR name = 'Bulbasaur' )", - str(clause), - ) diff --git a/tests/default_model.py b/tests/default_model.py deleted file mode 100644 index db62238..0000000 --- a/tests/default_model.py +++ /dev/null @@ -1,36 +0,0 @@ -from unittest import TestCase - - -class DefaultModelTests(TestCase): - - def test_fieldnames_class_attribute_contains_primary_key_field_name(self): - from plume import Model - self.assertEqual(len(Model._fieldnames), 1) - self.assertIn('pk', Model._fieldnames) - - def test_objects_attribute_links_to_a_class_specific_manager(self): - from plume import Manager, Model - self.assertTrue(isinstance(Model.objects, Manager)) - self.assertEqual(Model.__name__, Model.objects._model.__name__) - - def test_pk_field_is_empty_by_default_on_model_instance(self): - from plume import Model - self.assertIsNone(Model().pk) - - def test_pk_field_can_be_set_as_model_instance_init_param(self): - from plume import Model - m = Model(pk=1) - self.assertEqual(Model.pk, 1) - - def test_two_equivalent_models_are_equals(self): - from plume import Model - m1 = Model(pk=1) - m2 = Model(pk=1) - self.assertTrue(m1 == m2) - - def test_two_different_models_are_not_equals(self): - from plume import Model - m1 = Model(pk=1) - m2 = Model(pk=2) - self.assertFalse(m1 == m2) - diff --git a/tests/manager.py b/tests/manager.py deleted file mode 100644 index f1afacc..0000000 --- a/tests/manager.py +++ /dev/null @@ -1,4 +0,0 @@ -from tests.utils import TestCase - -class ManagerAPITests(TestCase): - pass diff --git a/tests/queryset.py b/tests/queryset.py deleted file mode 100644 index 12cdaf4..0000000 --- a/tests/queryset.py +++ /dev/null @@ -1,184 +0,0 @@ -from tests.utils import Trainer, TestCase - - -class TrainerTestCase(TestCase): - FIXTURES = { - 'models': [Trainer], - 'trainer': { - 'Giovanni': { - 'name': 'Giovanni', - 'age': 42 - }, - 'James': { - 'name': 'James', - 'age': 21 - }, - 'Jessie': { - 'name': 'Jessie', - 'age': 17 - }, - } - } - -class QuerySetAPI(TrainerTestCase): - - def test_output_queryset_as_string(self): - expected = str(Trainer.objects.filter(Trainer.age > 18, Trainer.name != 'Giovanni')) - - self.assertEqual( - "(SELECT * FROM trainer WHERE name != 'Giovanni' AND age > 18)", - expected, - ) - - -class QuerySetSliceTests(TrainerTestCase): - - def test_indexed_access_to_first_element_returns_a_model_instance(self): - self.add_fixture(Trainer, ['Giovanni']) - - trainer = Trainer.objects.filter()[0] - - self.assertEqual(trainer.name, 'Giovanni') - self.assertEqual(trainer.age, 42) - - def test_indexed_access_to_random_element_returns_a_model_instance(self): - self.add_fixture(Trainer, ['Giovanni', 'James', 'Jessie']) - - trainer = Trainer.objects.filter()[2] - - self.assertEqual(trainer.name, 'Jessie') - self.assertEqual(trainer.age, 17) - - def test_slice_access_with_start_and_stop_value_returns_a_model_instance_list(self): - self.add_fixture(Trainer, ['Giovanni', 'James', 'Jessie']) - - trainers = Trainer.objects.filter()[1:3] - - self.assertEqual(len(trainers), 2) - self.assertEqual(trainers[0].name, 'James') - self.assertEqual(trainers[0].age, 21) - self.assertEqual(trainers[1].name, 'Jessie') - self.assertEqual(trainers[1].age, 17) - - def test_slice_access_with_offset_only_returns_a_model_instance_list(self): - self.add_fixture(Trainer, ['Giovanni', 'James', 'Jessie']) - - trainers = Trainer.objects.filter()[1:3] - - self.assertEqual(len(trainers), 2) - self.assertEqual(trainers[0].name, 'James') - self.assertEqual(trainers[0].age, 21) - self.assertEqual(trainers[1].name, 'Jessie') - self.assertEqual(trainers[1].age, 17) - - def test_slice_access_with_offset_only_returns_a_pokemon_list(self): - self.add_fixture(Trainer, ['Giovanni', 'James', 'Jessie']) - - trainers = Trainer.objects.filter()[:2] - - self.assertEqual(len(trainers), 2) - self.assertEqual(trainers[0].name, 'Giovanni') - self.assertEqual(trainers[0].age, 42) - self.assertEqual(trainers[1].name, 'James') - self.assertEqual(trainers[1].age, 21) - - -class QuerySetResultsTests(TrainerTestCase): - - def test_select_from_one_table_with_all_fields(self): - self.add_fixture(Trainer, ['Giovanni', 'James', 'Jessie']) - - result = list(Trainer.objects.filter()) - - self.assertEqual(len(result), 3) - giovanni, james, jessie = result - - self.assertEqual(giovanni.name, 'Giovanni') - self.assertEqual(giovanni.age, 42) - - self.assertEqual(james.name, 'James') - self.assertEqual(james.age, 21) - - self.assertEqual(jessie.name, 'Jessie') - self.assertEqual(jessie.age, 17) - - def test_select_from_one_table_with_one_criterion(self): - self.add_fixture(Trainer, ['Giovanni', 'James', 'Jessie']) - - result = list(Trainer.objects.filter(Trainer.age > 18)) - - self.assertEqual(len(result), 2) - giovanni, james = result - - self.assertEqual(giovanni.name, 'Giovanni') - self.assertEqual(giovanni.age, 42) - - self.assertEqual(james.name, 'James') - self.assertEqual(james.age, 21) - - def test_select_from_one_table_with_ANDs_criteria_operator(self): - self.add_fixture(Trainer, ['Giovanni', 'James', 'Jessie']) - - result = list(Trainer.objects.filter((Trainer.age > 18) & (Trainer.name != 'Giovanni'))) - - self.assertEqual(len(result), 1) - james = result[0] - - self.assertEqual(james.name, 'James') - self.assertEqual(james.age, 21) - - def test_select_from_one_table_with_ANDs_criteria_list(self): - self.add_fixture(Trainer, ['Giovanni', 'James', 'Jessie']) - - result = list(Trainer.objects.filter(Trainer.age > 18, Trainer.name != 'Giovanni')) - - self.assertEqual(len(result), 1) - james = result[0] - - self.assertEqual(james.name, 'James') - self.assertEqual(james.age, 21) - - def test_select_from_one_table_with_chained_filters(self): - self.add_fixture(Trainer, ['Giovanni', 'James', 'Jessie']) - - queryset = Trainer.objects.filter(Trainer.age > 18) - queryset.filter(Trainer.name != 'Giovanni') - result = list(queryset) - - self.assertEqual(len(result), 1) - james = result[0] - - self.assertEqual(james.name, 'James') - self.assertEqual(james.age, 21) - - def test_select_from_one_table_with_in_operator(self): - self.add_fixture(Trainer, ['Giovanni', 'James', 'Jessie']) - - result = list(Trainer.objects.filter(Trainer.age << [17, 21])) - - self.assertEqual(len(result), 2) - james, jessie = result - - self.assertEqual(james.name, 'James') - self.assertEqual(james.age, 21) - - self.assertEqual(jessie.name, 'Jessie') - self.assertEqual(jessie.age, 17) - - def test_select_from_one_table_with_inner_query(self): - """ - self.add_fixture(Trainer, ['Giovanni', 'James', 'Jessie']) - - queryset = Trainer.objects.filter(Trainer.name != 'Giovanni') - queryset.filter(Trainer.age > Trainer.objects.select('age').filter(Trainer.name == 'Jessie')) - result = list(queryset) - - self.assertEqual(len(result), 1) - james = result[0] - - self.assertEqual(james.name, 'James') - self.assertEqual(james.age, 21) - """ - pass - - diff --git a/tests/sqlite_api.py b/tests/sqlite_api.py deleted file mode 100644 index 9323388..0000000 --- a/tests/sqlite_api.py +++ /dev/null @@ -1,43 +0,0 @@ -from plume import SQLiteAPI - -from unittest import TestCase - - -class SQLiteAPITests(TestCase): - - def test_select_from_one_table_with_all_fields(self): - self.assertEqual( - SQLiteAPI.select(tables='pokemon'), - 'SELECT * FROM pokemon', - ) - - def test_select_from_several_tables_with_all_fields(self): - self.assertEqual( - SQLiteAPI.select(tables=('pokemon', 'trainer')), - 'SELECT * FROM pokemon, trainer', - ) - - def test_select_from_one_table_with_several_fields(self): - self.assertEqual( - SQLiteAPI.select(tables='pokemon', fields=('name', 'level')), - 'SELECT name, level FROM pokemon', - ) - - def test_select_from_one_table_with_one_criterion(self): - self.assertEqual( - SQLiteAPI.select(tables='pokemon', where= 'level > 18'), - 'SELECT * FROM pokemon WHERE level > 18', - ) - - - def test_select_from_one_table_with_several_criteria(self): - self.assertEqual( - SQLiteAPI.select(tables='pokemon', where='name = Pikachu AND level > 18'), - 'SELECT * FROM pokemon WHERE name = Pikachu AND level > 18', - ) - - def test_select_from_one_table_with_count_and_offset(self): - self.assertEqual( - SQLiteAPI.select(tables='pokemon', count=10, offset=42), - 'SELECT * FROM pokemon LIMIT 10 OFFSET 42', - ) diff --git a/tests/test_clause.py b/tests/test_clause.py new file mode 100644 index 0000000..04bf16d --- /dev/null +++ b/tests/test_clause.py @@ -0,0 +1,45 @@ +import plume +from tests.utils import Pokemon + +import pytest + + +class TestClause: + + def test_allows_or_operator_between_two_clauses(self): + result = str((Pokemon.name == 'Charamander') | (Pokemon.name == 'Bulbasaur')) + expected = "( name = 'Charamander' OR name = 'Bulbasaur' )" + assert result == expected + + def test_or_operator_between_two_clauses_add_brackets(self): + result = str((Pokemon.name == 'Charamander') | (Pokemon.name == 'Bulbasaur')) + expected_open_bracket = '(' + expected_close_bracket = ')' + assert result[0] == expected_open_bracket + assert result[-1] == expected_close_bracket + + def test_allows_and_operator_between_two_clauses(self): + result = str((Pokemon.name == 'Charamander') & (Pokemon.level == 18)) + expected = "name = 'Charamander' AND level = 18" + assert result == expected + + def test_and_operator_between_two_clauses_does_not_add_brackets(self): + result = str((Pokemon.name == 'Charamander') & (Pokemon.level == 18)) + assert result[0] != '(' + assert result[-1] != ')' + + def test_or_operator_has_lower_precedence_than_and_operator(self): + result = str( + (Pokemon.name == 'Charamander') | (Pokemon.name == 'Bulbasaur') + & (Pokemon.level > 18) + ) + expected = "( name = 'Charamander' OR name = 'Bulbasaur' AND level > 18 )" + assert result == expected + + def test_bracket_has_higher_precedence_than_and_operator(self): + result = str( + ((Pokemon.name == 'Charamander') | (Pokemon.name == 'Bulbasaur')) + & (Pokemon.level > 18) + ) + expected = "level > 18 AND ( name = 'Charamander' OR name = 'Bulbasaur' )" + assert result == expected diff --git a/tests/database.py b/tests/test_database.py similarity index 74% rename from tests/database.py rename to tests/test_database.py index 8b0df88..565fb85 100644 --- a/tests/database.py +++ b/tests/test_database.py @@ -3,12 +3,12 @@ from contextlib import closing import os -from unittest import TestCase +import pytest -class DatabaseAPITests(TestCase): +class TestDatabaseAPI: - def setUp(self): + def setup_method(self): self.db = Database(DB_NAME) def table_exists(self, model): @@ -24,37 +24,37 @@ def count_tables(self): return query[0][0] def test_register_a_custom_model(self): - self.assertFalse(self.table_exists(Trainer)) + assert self.table_exists(Trainer) == False self.db.register(Trainer) - self.assertTrue(self.table_exists(Trainer)) + assert self.table_exists(Trainer) == True def test_register_a_non_custom_model(self): # A error in table creation must raise an exception an must not make any change in the database. - self.assertEqual(self.count_tables(), 0) + assert self.count_tables() == 0 - with self.assertRaises(TypeError): + with pytest.raises(TypeError): self.db.register(object) - self.assertEqual(self.count_tables(), 0) + assert self.count_tables() == 0 def test_register_several_custom_models(self): - self.assertEqual(self.count_tables(), 0) + assert self.count_tables() == 0 self.db.register(Trainer, Pokemon) - self.assertTrue(self.table_exists(Trainer)) - self.assertTrue(self.table_exists(Pokemon)) + assert self.table_exists(Trainer) == True + assert self.table_exists(Pokemon) == True def test_store_a_database_reference_into_model_when_registered(self): self.db.register(Trainer) try: Trainer._db except AttributeError: - self.fail() + pytest.fail() def test_no_database_reference_in_unregistered_model(self): class CustomModel(Model): pass - with self.assertRaises(AttributeError): + with pytest.raises(AttributeError): CustomModel._db def test_register_an_existing_table(self): @@ -62,7 +62,7 @@ def test_register_an_existing_table(self): try: self.db.register(Trainer) except: - self.fail() + pytest.fail() def test_all_model_fields_match_table_fields(self): self.db.register(Trainer) @@ -71,11 +71,12 @@ def test_all_model_fields_match_table_fields(self): self.db._connection.execute('PRAGMA table_info(trainer)').fetchall() ] - self.assertEqual(len(db_fields), 3) + assert len(db_fields) == 3 + for model_field in ['pk', 'name', 'age']: - self.assertIn(model_field, db_fields) + assert model_field in db_fields - def tearDown(self): + def teardown_method(self): try: os.remove(DB_NAME) except: diff --git a/tests/test_manager.py b/tests/test_manager.py new file mode 100644 index 0000000..9136fce --- /dev/null +++ b/tests/test_manager.py @@ -0,0 +1,5 @@ +import pytest + + +class TestManagerAPI: + pass diff --git a/tests/test_model.py b/tests/test_model.py new file mode 100644 index 0000000..0cb7769 --- /dev/null +++ b/tests/test_model.py @@ -0,0 +1,32 @@ +from plume.plume import Manager, Model + +import pytest + + +class TestModel: + + 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 + + def test_pk_field_can_be_set_as_model_instance_init_param(self): + m = Model(pk=1) + assert Model.pk == 1 + + def test_two_equivalent_models_are_equals(self): + m1 = Model(pk=1) + m2 = Model(pk=1) + assert m1 == m2 + + def test_two_different_models_are_not_equals(self): + m1 = Model(pk=1) + m2 = Model(pk=2) + assert m1 != m2 + diff --git a/tests/test_queryset.py b/tests/test_queryset.py new file mode 100644 index 0000000..6f34dc9 --- /dev/null +++ b/tests/test_queryset.py @@ -0,0 +1,170 @@ +from plume import * +from tests.utils import DB_NAME, Trainer + +import pytest + + +class Base: + + TRAINERS = { + 'Giovanni': { + 'name': 'Giovanni', + 'age': 42 + }, + 'James': { + 'name': 'James', + 'age': 21 + }, + 'Jessie': { + 'name': 'Jessie', + 'age': 17 + }, + } + + def setup_method(self): + db = Database(DB_NAME) + db.register(Trainer) + + def add_trainer(self, names): + try: + names = names.split() + except: + pass + + for name in names: + Trainer.objects.create(**self.TRAINERS[name]) + + +class TestQuerySetAPI(Base): + + def test_output_queryset_as_string(self): + result = str(Trainer.objects.filter(Trainer.age > 18, Trainer.name != 'Giovanni')) + expected = "(SELECT * FROM trainer WHERE name != 'Giovanni' AND age > 18)" + assert result == expected + + +class TestQuerySetSlice(Base): + + def test_indexed_access_to_first_element_returns_a_model_instance(self): + self.add_trainer('Giovanni') + trainer = Trainer.objects.filter()[0] + assert trainer.name == 'Giovanni' + assert trainer.age == 42 + + def test_indexed_access_to_random_element_returns_a_model_instance(self): + self.add_trainer(['Giovanni', 'James', 'Jessie']) + trainer = Trainer.objects.filter()[2] + assert trainer.name == 'Jessie' + assert trainer.age == 17 + + def test_slice_access_with_start_and_stop_value_returns_a_model_instance_list(self): + self.add_trainer(['Giovanni', 'James', 'Jessie']) + trainers = Trainer.objects.filter()[1:3] + assert len(trainers) == 2 + assert trainers[0].name == 'James' + assert trainers[0].age == 21 + assert trainers[1].name == 'Jessie' + assert trainers[1].age == 17 + + def test_slice_access_with_offset_only_returns_a_model_instance_list(self): + self.add_trainer(['Giovanni', 'James', 'Jessie']) + trainers = Trainer.objects.filter()[1:3] + assert len(trainers) == 2 + assert trainers[0].name == 'James' + assert trainers[0].age == 21 + assert trainers[1].name == 'Jessie' + assert trainers[1].age == 17 + + def test_slice_access_with_offset_only_returns_a_pokemon_list(self): + self.add_trainer(['Giovanni', 'James', 'Jessie']) + trainers = Trainer.objects.filter()[:2] + assert len(trainers) == 2 + assert trainers[0].name == 'Giovanni' + assert trainers[0].age == 42 + assert trainers[1].name == 'James' + assert trainers[1].age == 21 + + +class TestQuerySetResults(Base): + + def test_select_from_one_table_with_all_fields(self): + self.add_trainer(['Giovanni', 'James', 'Jessie']) + result = list(Trainer.objects.filter()) + + assert len(result) == 3 + giovanni, james, jessie = result + + assert giovanni.name == 'Giovanni' + assert giovanni.age == 42 + + assert james.name == 'James' + assert james.age == 21 + + assert jessie.name == 'Jessie' + assert jessie.age == 17 + + def test_select_from_one_table_with_one_criterion(self): + self.add_trainer(['Giovanni', 'James', 'Jessie']) + result = list(Trainer.objects.filter(Trainer.age > 18)) + assert len(result) == 2 + + giovanni, james = result + assert giovanni.name == 'Giovanni' + assert giovanni.age == 42 + + assert james.name == 'James' + assert james.age == 21 + + def test_select_from_one_table_with_ANDs_criteria_operator(self): + self.add_trainer(['Giovanni', 'James', 'Jessie']) + result = list(Trainer.objects.filter((Trainer.age > 18) & (Trainer.name != 'Giovanni'))) + assert len(result) == 1 + + james = result[0] + assert james.name == 'James' + assert james.age == 21 + + def test_select_from_one_table_with_ANDs_criteria_list(self): + self.add_trainer(['Giovanni', 'James', 'Jessie']) + result = list(Trainer.objects.filter(Trainer.age > 18, Trainer.name != 'Giovanni')) + assert len(result) == 1 + + james = result[0] + assert james.name == 'James' + assert james.age == 21 + + def test_select_from_one_table_with_chained_filters(self): + self.add_trainer(['Giovanni', 'James', 'Jessie']) + queryset = Trainer.objects.filter(Trainer.age > 18) + queryset.filter(Trainer.name != 'Giovanni') + result = list(queryset) + assert len(result) == 1 + + james = result[0] + assert james.name == 'James' + assert james.age == 21 + + def test_select_from_one_table_with_in_operator(self): + self.add_trainer(['Giovanni', 'James', 'Jessie']) + result = list(Trainer.objects.filter(Trainer.age << [17, 21])) + assert len(result) == 2 + + james, jessie = result + assert james.name == 'James' + assert james.age == 21 + + assert jessie.name == 'Jessie' + assert jessie.age == 17 + + """ + def test_select_from_one_table_with_inner_query(self): + self.add_trainer(['Giovanni', 'James', 'Jessie']) + queryset = Trainer.objects.filter(Trainer.name != 'Giovanni') + queryset.filter(Trainer.age > Trainer.objects.select('age').filter(Trainer.name == 'Jessie')) + result = list(queryset) + assert len(result) == 1 + + james = result[0] + assert james.name == 'James' + assert james.age == 21 + """ diff --git a/tests/test_sqliteapi.py b/tests/test_sqliteapi.py new file mode 100644 index 0000000..6960129 --- /dev/null +++ b/tests/test_sqliteapi.py @@ -0,0 +1,37 @@ +from plume.plume import SQLiteAPI + +import pytest + + +class TestSQLiteAPI: + + def test_select_from_one_table_with_all_fields(self): + result = SQLiteAPI.select(tables='pokemon') + expected = 'SELECT * FROM pokemon' + assert result == expected + + def test_select_from_several_tables_with_all_fields(self): + result = SQLiteAPI.select(tables=('pokemon', 'trainer')) + expected = 'SELECT * FROM pokemon, trainer' + assert result == expected + + def test_select_from_one_table_with_several_fields(self): + result = SQLiteAPI.select(tables='pokemon', fields=('name', 'level')) + expected = 'SELECT name, level FROM pokemon' + assert result == expected + + def test_select_from_one_table_with_one_criterion(self): + result = SQLiteAPI.select(tables='pokemon', where= 'level > 18') + expected = 'SELECT * FROM pokemon WHERE level > 18' + assert result == expected + + + def test_select_from_one_table_with_several_criteria(self): + result = SQLiteAPI.select(tables='pokemon', where="name = 'Pikachu' AND level > 18") + expected = "SELECT * FROM pokemon WHERE name = 'Pikachu' AND level > 18" + assert result == expected + + def test_select_from_one_table_with_count_and_offset(self): + result = SQLiteAPI.select(tables='pokemon', count=10, offset=42) + expected = 'SELECT * FROM pokemon LIMIT 10 OFFSET 42' + assert result == expected diff --git a/tests/utils.py b/tests/utils.py index 80236a0..679b8e2 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -15,20 +15,3 @@ class Trainer(Model): name = TextField() age = IntegerField() - -class TestCase(unittest.TestCase): - - def setUp(self): - db = Database(DB_NAME) - - for model in self.FIXTURES.get('models', ()): - db.register(model) - - - def add_fixture(self, model_class, values): - - model_name = model_class.__name__.lower() - - for value in values: - model_class.objects.create(**self.FIXTURES[model_name][value]) -