Skip to content
This repository
Browse code

Adding items from todo list, including inheritance and options testing

and topo sort
  • Loading branch information...
commit 54fcef2592fa31580ccbcbef53ce5b9ea894f939 1 parent e8a068d
Charles Leifer authored October 03, 2012
4  TODO.rst
Source Rendered
... ...
@@ -1,10 +1,6 @@
1 1
 todo
2 2
 ====
3 3
 
4  
-* Q() with django syntax
5  
-* inheritance test
6  
-* model options test
7  
-* topo sort
8 4
 * backwards compat, esp places where existing api allows strings
9 5
 * stronger input validation?
10 6
 * docs
32  peewee.py
@@ -497,7 +497,7 @@ def __get__(self, instance, instance_type=None):
497 497
 class ForeignKeyField(Field):
498 498
     def __init__(self, rel_model, null=False, related_name=None, cascade=False, extra=None, *args, **kwargs):
499 499
         self.rel_model = rel_model
500  
-        self.related_name = related_name
  500
+        self._related_name = related_name
501 501
         self.cascade = cascade
502 502
         self.extra = extra
503 503
 
@@ -516,7 +516,7 @@ def add_to_class(self, model_class, name):
516 516
         model_class._meta.fields[self.name] = self
517 517
         model_class._meta.columns[self.db_column] = self
518 518
 
519  
-        self.related_name = self.related_name or '%s_set' % (model_class._meta.name)
  519
+        self.related_name = self._related_name or '%s_set' % (model_class._meta.name)
520 520
 
521 521
         if self.rel_model == 'self':
522 522
             self.rel_model = self.model_class
@@ -1976,3 +1976,31 @@ def __eq__(self, other):
1976 1976
 
1977 1977
     def __ne__(self, other):
1978 1978
         return not self == other
  1979
+
  1980
+
  1981
+def create_model_tables(models, **create_table_kwargs):
  1982
+    """Create tables for all given models (in the right order)."""
  1983
+    for m in sort_models_topologically(models):
  1984
+        m.create_table(**create_table_kwargs)
  1985
+
  1986
+def drop_model_tables(models, **drop_table_kwargs):
  1987
+    """Drop tables for all given models (in the right order)."""
  1988
+    for m in reversed(sort_models_topologically(models)):
  1989
+        m.drop_table(**drop_table_kwargs)
  1990
+
  1991
+def sort_models_topologically(models):
  1992
+    """Sort models topologically so that parents will precede children."""
  1993
+    models = set(models)
  1994
+    seen = set()
  1995
+    ordering = []
  1996
+    def dfs(model):
  1997
+        if model in models and model not in seen:
  1998
+            seen.add(model)
  1999
+            for child_model in model._meta.reverse_rel.values():
  2000
+                dfs(child_model)
  2001
+            ordering.append(model)  # parent will follow descendants
  2002
+    # order models by name and table initially to guarantee a total ordering
  2003
+    names = lambda m: (m._meta.name, m._meta.db_table)
  2004
+    for m in sorted(models, key=names, reverse=True):
  2005
+        dfs(m)
  2006
+    return list(reversed(ordering))  # want parents first in output ordering
172  tests.py
@@ -94,7 +94,7 @@ class Meta:
94 94
         db_table = 'users'
95 95
 
96 96
 class Blog(TestModel):
97  
-    user = ForeignKeyField(User, related_name='blogs')
  97
+    user = ForeignKeyField(User)
98 98
     title = CharField(max_length=25)
99 99
     content = TextField(default='')
100 100
     pub_date = DateTimeField(null=True)
@@ -178,9 +178,13 @@ class Meta:
178 178
             (('f2', 'f3'), False),
179 179
         )
180 180
 
  181
+class BlogTwo(Blog):
  182
+    title = TextField()
  183
+    extra_field = CharField()
  184
+
181 185
 
182 186
 MODELS = [User, Blog, Comment, Relationship, NullModel, UniqueModel, OrderedModel, Category, UserCategory,
183  
-          NonIntModel, NonIntRelModel, DBUser, DBBlog, SeqModelA, SeqModelB, MultiIndexModel]
  187
+          NonIntModel, NonIntRelModel, DBUser, DBBlog, SeqModelA, SeqModelB, MultiIndexModel, BlogTwo]
184 188
 INT = test_db.interpolation
185 189
 
186 190
 def drop_tables(only=None):
@@ -762,8 +766,8 @@ def test_related_name(self):
762 766
         b12 = Blog.create(user=u1, title='b12')
763 767
         b2 = Blog.create(user=u2, title='b2')
764 768
 
765  
-        self.assertEqual([b.title for b in u1.blogs], ['b11', 'b12'])
766  
-        self.assertEqual([b.title for b in u2.blogs], ['b2'])
  769
+        self.assertEqual([b.title for b in u1.blog_set], ['b11', 'b12'])
  770
+        self.assertEqual([b.title for b in u2.blog_set], ['b2'])
767 771
 
768 772
     def test_fk_exceptions(self):
769 773
         c1 = Category.create(name='c1')
@@ -1408,9 +1412,116 @@ def reader_thread(q, num):
1408 1412
         self.assertEqual(data_queue.qsize(), 100)
1409 1413
 
1410 1414
 
1411  
-class ModelInheritanceTestCase(BasePeeweeTestCase):
1412  
-    # TODO
1413  
-    pass
  1415
+class ModelOptionInheritanceTestCase(BasePeeweeTestCase):
  1416
+    def test_db_table(self):
  1417
+        self.assertEqual(User._meta.db_table, 'users')
  1418
+
  1419
+        class Foo(TestModel):
  1420
+            pass
  1421
+        self.assertEqual(Foo._meta.db_table, 'foo')
  1422
+
  1423
+        class Foo2(TestModel):
  1424
+            pass
  1425
+        self.assertEqual(Foo2._meta.db_table, 'foo2')
  1426
+
  1427
+        class Foo_3(TestModel):
  1428
+            pass
  1429
+        self.assertEqual(Foo_3._meta.db_table, 'foo_3')
  1430
+
  1431
+    def test_option_inheritance(self):
  1432
+        x_test_db = SqliteDatabase('testing.db')
  1433
+        child2_db = SqliteDatabase('child2.db')
  1434
+
  1435
+        class FakeUser(Model):
  1436
+            pass
  1437
+
  1438
+        class ParentModel(Model):
  1439
+            title = CharField()
  1440
+            user = ForeignKeyField(FakeUser)
  1441
+
  1442
+            class Meta:
  1443
+                database = x_test_db
  1444
+
  1445
+        class ChildModel(ParentModel):
  1446
+            pass
  1447
+
  1448
+        class ChildModel2(ParentModel):
  1449
+            special_field = CharField()
  1450
+
  1451
+            class Meta:
  1452
+                database = child2_db
  1453
+
  1454
+        class GrandChildModel(ChildModel):
  1455
+            pass
  1456
+
  1457
+        class GrandChildModel2(ChildModel2):
  1458
+            special_field = TextField()
  1459
+
  1460
+        self.assertEqual(ParentModel._meta.database.database, 'testing.db')
  1461
+        self.assertEqual(ParentModel._meta.model_class, ParentModel)
  1462
+
  1463
+        self.assertEqual(ChildModel._meta.database.database, 'testing.db')
  1464
+        self.assertEqual(ChildModel._meta.model_class, ChildModel)
  1465
+        self.assertEqual(sorted(ChildModel._meta.fields.keys()), [
  1466
+            'id', 'title', 'user'
  1467
+        ])
  1468
+
  1469
+        self.assertEqual(ChildModel2._meta.database.database, 'child2.db')
  1470
+        self.assertEqual(ChildModel2._meta.model_class, ChildModel2)
  1471
+        self.assertEqual(sorted(ChildModel2._meta.fields.keys()), [
  1472
+            'id', 'special_field', 'title', 'user'
  1473
+        ])
  1474
+
  1475
+        self.assertEqual(GrandChildModel._meta.database.database, 'testing.db')
  1476
+        self.assertEqual(GrandChildModel._meta.model_class, GrandChildModel)
  1477
+        self.assertEqual(sorted(GrandChildModel._meta.fields.keys()), [
  1478
+            'id', 'title', 'user'
  1479
+        ])
  1480
+
  1481
+        self.assertEqual(GrandChildModel2._meta.database.database, 'child2.db')
  1482
+        self.assertEqual(GrandChildModel2._meta.model_class, GrandChildModel2)
  1483
+        self.assertEqual(sorted(GrandChildModel2._meta.fields.keys()), [
  1484
+            'id', 'special_field', 'title', 'user'
  1485
+        ])
  1486
+        self.assertTrue(isinstance(GrandChildModel2._meta.fields['special_field'], TextField))
  1487
+
  1488
+
  1489
+class ModelInheritanceTestCase(ModelTestCase):
  1490
+    requires = [Blog, BlogTwo, User]
  1491
+
  1492
+    def test_model_inheritance_attrs(self):
  1493
+        self.assertEqual(Blog._meta.get_field_names(), ['pk', 'user', 'title', 'content', 'pub_date'])
  1494
+        self.assertEqual(BlogTwo._meta.get_field_names(), ['id', 'user', 'content', 'pub_date', 'title', 'extra_field'])
  1495
+
  1496
+        self.assertEqual(Blog._meta.primary_key.name, 'pk')
  1497
+        self.assertEqual(BlogTwo._meta.primary_key.name, 'id')
  1498
+
  1499
+        self.assertEqual(Blog.user.related_name, 'blog_set')
  1500
+        self.assertEqual(BlogTwo.user.related_name, 'blogtwo_set')
  1501
+
  1502
+        self.assertEqual(User.blog_set.rel_model, Blog)
  1503
+        self.assertEqual(User.blogtwo_set.rel_model, BlogTwo)
  1504
+
  1505
+        self.assertFalse(BlogTwo._meta.db_table == Blog._meta.db_table)
  1506
+
  1507
+    def test_model_inheritance_flow(self):
  1508
+        u = User.create(username='u')
  1509
+
  1510
+        b = Blog.create(title='b', user=u)
  1511
+        b2 = BlogTwo.create(title='b2', extra_field='foo', user=u)
  1512
+
  1513
+        self.assertEqual(list(u.blog_set), [b])
  1514
+        self.assertEqual(list(u.blogtwo_set), [b2])
  1515
+
  1516
+        self.assertEqual(Blog.select().count(), 1)
  1517
+        self.assertEqual(BlogTwo.select().count(), 1)
  1518
+
  1519
+        b_from_db = Blog.get(pk=b.pk)
  1520
+        b2_from_db = BlogTwo.get(id=b2.id)
  1521
+
  1522
+        self.assertEqual(b_from_db.user, u)
  1523
+        self.assertEqual(b2_from_db.user, u)
  1524
+        self.assertEqual(b2_from_db.extra_field, 'foo')
1414 1525
 
1415 1526
 
1416 1527
 class DatabaseTestCase(BasePeeweeTestCase):
@@ -1449,6 +1560,53 @@ def test_connection_state(self):
1449 1560
         self.assertFalse(test_db.is_closed())
1450 1561
 
1451 1562
 
  1563
+class TopologicalSortTestCase(unittest.TestCase):
  1564
+    def test_topological_sort_fundamentals(self):
  1565
+        FKF = ForeignKeyField
  1566
+        # we will be topo-sorting the following models
  1567
+        class A(Model): pass
  1568
+        class B(Model): a = FKF(A)              # must follow A
  1569
+        class C(Model): a, b = FKF(A), FKF(B)   # must follow A and B
  1570
+        class D(Model): c = FKF(C)              # must follow A and B and C
  1571
+        class E(Model): e = FKF('self')
  1572
+        # but excluding this model, which is a child of E
  1573
+        class Excluded(Model): e = FKF(E)
  1574
+
  1575
+        # property 1: output ordering must not depend upon input order
  1576
+        repeatable_ordering = None
  1577
+        for input_ordering in permutations([A, B, C, D, E]):
  1578
+            output_ordering = sort_models_topologically(input_ordering)
  1579
+            repeatable_ordering = repeatable_ordering or output_ordering
  1580
+            self.assertEqual(repeatable_ordering, output_ordering)
  1581
+
  1582
+        # property 2: output ordering must have same models as input
  1583
+        self.assertEqual(len(output_ordering), 5)
  1584
+        self.assertFalse(Excluded in output_ordering)
  1585
+
  1586
+        # property 3: parents must precede children
  1587
+        def assert_precedes(X, Y):
  1588
+            lhs, rhs = map(output_ordering.index, [X, Y])
  1589
+            self.assertTrue(lhs < rhs)
  1590
+        assert_precedes(A, B)
  1591
+        assert_precedes(B, C)  # if true, C follows A by transitivity
  1592
+        assert_precedes(C, D)  # if true, D follows A and B by transitivity
  1593
+
  1594
+        # property 4: independent model hierarchies must be in name order
  1595
+        assert_precedes(A, E)
  1596
+
  1597
+def permutations(xs):
  1598
+    if not xs:
  1599
+        yield []
  1600
+    else:
  1601
+        for y, ys in selections(xs):
  1602
+            for pys in permutations(ys):
  1603
+                yield [y] + pys
  1604
+
  1605
+def selections(xs):
  1606
+    for i in xrange(len(xs)):
  1607
+        yield (xs[i], xs[:i] + xs[i + 1:])
  1608
+
  1609
+
1452 1610
 if test_db.for_update:
1453 1611
     class ForUpdateTestCase(ModelTestCase):
1454 1612
         requires = [User]

0 notes on commit 54fcef2

Please sign in to comment.
Something went wrong with that request. Please try again.