├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.rst ├── README_template ├── manage.py ├── quick_orm ├── __init__.py ├── column_types.py ├── core.py ├── examples │ ├── __init__.py │ ├── hello_world.py │ ├── many_to_many.py │ ├── many_to_many_options.py │ ├── many_to_many_to_self.py │ ├── many_to_one.py │ ├── many_to_one_options.py │ ├── many_to_one_to_self.py │ ├── meta_builder.py │ ├── model_for_stackoverflow.py │ ├── multiple_databases.py │ ├── multiple_many_to_one.py │ ├── one_to_one.py │ ├── raw_sql.py │ └── table_inheritance.py ├── extensions.py └── testsuite │ ├── __init__.py │ ├── core.py │ ├── fixtures.py │ └── models.py ├── requires.txt └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.swp 3 | *.db 4 | build/ 5 | dist/ 6 | MANIFEST 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 by Tyler Long. 2 | 3 | Some rights reserved. 4 | 5 | Redistribution and use in source and binary forms of the software as well 6 | as documentation, with or without modification, are permitted provided 7 | that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above 13 | copyright notice, this list of conditions and the following 14 | disclaimer in the documentation and/or other materials provided 15 | with the distribution. 16 | 17 | * The names of the contributors may not be used to endorse or 18 | promote products derived from this software without specific 19 | prior written permission. 20 | 21 | THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND 22 | CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT 23 | NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 25 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 26 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 32 | DAMAGE. 33 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE manage.py README_template README.rst requires.txt todo.txt -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | quick_orm 2 | ========= 3 | - **News**: quick_orm is fully compatible with the newest SQLAlchemy 0.7.9. 4 | - **Notice**: quick_orm is NOT YET compatible with SQLAlchemy 0.8.x. 5 | 6 | 7 | | 8 | 9 | Introduction 10 | ------------ 11 | Python ORM framework which enables you to get started in less than a minute! 12 | 13 | Super easy to setup and super easy to use, yet super powerful! 14 | 15 | You would regret that you didn't discorver it earlier! 16 | 17 | 18 | | 19 | 20 | Features 21 | -------- 22 | - quick: you could get and play with it in less than a minute. It couldn't be more straightforward. 23 | - easy: you don't have to write any SQL statements, including those "create table xxx ..." ones. 24 | - simple: the core code counts only 217 lines including comments and pydocs, there is no room for bugs. 25 | - free: released under BSD license, you are free to use it and distribute it. 26 | - powerful: built upon SQLAlchemy and doesn't compromise its power. 27 | - support relationships by means of python decorators. 28 | - support table inheritance in a most natural way. 29 | - support multiple databases: you can map your models to many databases without difficulty. 30 | - write less, do more: taking advantage of python metaclass reduces data modeling code dramatically. 31 | - long-term maintained: Continous efforts are taken to improve and maintain it. 32 | 33 | 34 | | 35 | 36 | Quick Start 37 | ----------- 38 | 39 | :: 40 | 41 | pip install quick_orm 42 | 43 | Refer to the following examples to write your own database manipulation code. 44 | 45 | 46 | | 47 | 48 | Hello World example 49 | ------------------- 50 | 51 | :: 52 | 53 | from quick_orm.core import Database 54 | from sqlalchemy import Column, String 55 | 56 | __metaclass__ = Database.DefaultMeta 57 | 58 | class User: 59 | name = Column(String(30)) 60 | 61 | Database.register() 62 | 63 | if __name__ == '__main__': 64 | db = Database('sqlite://') # database urls: http://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls 65 | db.create_tables() # create tables, you don't have to write any SQL. 66 | 67 | user = User(name = 'Hello World') 68 | db.session.add_then_commit(user) # commit user to database. 69 | 70 | user = db.session.query(User).get(1) 71 | print 'My name is', user.name 72 | print 'created_at', user.created_at # created_at and updated_at timestamps are added automatically. 73 | print 'updated_at', user.updated_at 74 | 75 | user.name = 'Tyler Long' 76 | db.session.commit() # commit changes to database. 77 | print 'My name is', user.name 78 | print 'created_at', user.created_at 79 | print 'updated_at', user.updated_at 80 | 81 | 82 | | 83 | 84 | Many-to-one relationship example 85 | -------------------------------- 86 | 87 | :: 88 | 89 | from quick_orm.core import Database 90 | from sqlalchemy import Column, String, Text 91 | 92 | __metaclass__ = Database.DefaultMeta 93 | 94 | class Question: 95 | title = Column(String(70)) 96 | content = Column(Text) 97 | 98 | @Database.many_to_one(Question) 99 | class Answer: 100 | content = Column(Text) 101 | 102 | Database.register() 103 | 104 | if __name__ == '__main__': 105 | db = Database('sqlite://') 106 | db.create_tables() 107 | 108 | question = Question(title = 'What is quick_orm?', content = 'What is quick_orm?') 109 | answer = Answer(question = question, content = 'quick_orm is a Python ORM framework which enables you to get started in less than a minute!') 110 | db.session.add_then_commit(answer) 111 | 112 | question = db.session.query(Question).get(1) 113 | print 'The question is:', question.title 114 | print 'The answer is:', question.answers.first().content 115 | 116 | 117 | | 118 | 119 | Many-to-one relationship options example 120 | ---------------------------------------- 121 | 122 | :: 123 | 124 | from quick_orm.core import Database 125 | from sqlalchemy import Column, String, Text 126 | 127 | __metaclass__ = Database.DefaultMeta 128 | 129 | class Question: 130 | title = Column(String(70)) 131 | content = Column(Text) 132 | 133 | @Database.many_to_one(Question, ref_name = 'question', backref_name = 'answers') 134 | class Answer: 135 | content = Column(Text) 136 | 137 | Database.register() 138 | 139 | if __name__ == '__main__': 140 | db = Database('sqlite://') 141 | db.create_tables() 142 | 143 | question = Question(title = 'What is quick_orm?', content = 'What is quick_orm?') 144 | answer = Answer(question = question, content = 'quick_orm is a Python ORM framework which enables you to get started in less than a minute!') 145 | db.session.add_then_commit(answer) 146 | 147 | question = db.session.query(Question).get(1) 148 | print 'The question is:', question.title 149 | print 'The answer is:', question.answers.first().content 150 | 151 | 152 | | 153 | 154 | Many-to-one relationship with oneself example 155 | --------------------------------------------- 156 | 157 | :: 158 | 159 | from quick_orm.core import Database 160 | from sqlalchemy import Column, String 161 | 162 | __metaclass__ = Database.DefaultMeta 163 | 164 | @Database.many_to_one('Node', ref_name = 'parent_node', backref_name = 'children_nodes') 165 | class Node: 166 | name = Column(String(70)) 167 | 168 | Database.register() 169 | 170 | if __name__ == '__main__': 171 | db = Database('sqlite://') 172 | db.create_tables() 173 | 174 | root_node = Node(name = 'root') 175 | node1 = Node(name = 'node1', parent_node = root_node) 176 | node2 = Node(name = 'node2', parent_node = root_node) 177 | db.session.add_then_commit(root_node) 178 | 179 | root_node = db.session.query(Node).filter_by(name = 'root').one() 180 | print 'Root node has {0} children nodes, they are {1}'\ 181 | .format(root_node.children_nodes.count(), ', '.join(node.name for node in root_node.children_nodes)) 182 | 183 | 184 | | 185 | 186 | Many-to-many relationship example 187 | --------------------------------- 188 | 189 | :: 190 | 191 | from quick_orm.core import Database 192 | from sqlalchemy import Column, String 193 | 194 | __metaclass__ = Database.DefaultMeta 195 | 196 | class User: 197 | name = Column(String(30)) 198 | 199 | @Database.many_to_many(User) 200 | class Role: 201 | name = Column(String(30)) 202 | 203 | Database.register() 204 | 205 | if __name__ == '__main__': 206 | db = Database('sqlite://') 207 | db.create_tables() 208 | 209 | user1 = User(name = 'Tyler Long') 210 | user2 = User(name = 'Peter Lau') 211 | role = Role(name = 'Administrator', users = [user1, user2]) 212 | db.session.add_then_commit(role) 213 | 214 | admin_role = db.session.query(Role).filter_by(name = 'Administrator').one() 215 | print ', '.join([user.name for user in admin_role.users]), 'are administrators' 216 | 217 | 218 | | 219 | 220 | Many-to-many relationship options example 221 | ----------------------------------------- 222 | 223 | :: 224 | 225 | from quick_orm.core import Database 226 | from sqlalchemy import Column, String 227 | 228 | __metaclass__ = Database.DefaultMeta 229 | 230 | class User: 231 | name = Column(String(30)) 232 | 233 | @Database.many_to_many(User, ref_name = 'users', backref_name = 'roles', middle_table_name = 'user_role') 234 | class Role: 235 | name = Column(String(30)) 236 | 237 | Database.register() 238 | 239 | if __name__ == '__main__': 240 | db = Database('sqlite://') 241 | db.create_tables() 242 | 243 | user1 = User(name = 'Tyler Long') 244 | user2 = User(name = 'Peter Lau') 245 | role = Role(name = 'Administrator', users = [user1, user2]) 246 | db.session.add_then_commit(role) 247 | 248 | admin_role = db.session.query(Role).filter_by(name = 'Administrator').one() 249 | print ', '.join([user.name for user in admin_role.users]), 'are administrators' 250 | 251 | 252 | | 253 | 254 | Many-to-many relationship with oneself example 255 | ---------------------------------------------- 256 | 257 | :: 258 | 259 | from quick_orm.core import Database 260 | from sqlalchemy import Column, String 261 | 262 | __metaclass__ = Database.DefaultMeta 263 | 264 | @Database.many_to_many('User', ref_name = 'users_i_follow', backref_name = 'users_follow_me') 265 | class User: 266 | name = Column(String(30)) 267 | 268 | Database.register() 269 | 270 | if __name__ == '__main__': 271 | db = Database('sqlite://') 272 | db.create_tables() 273 | 274 | peter = User(name = 'Peter Lau') 275 | mark = User(name = 'Mark Wong', users_i_follow = [peter, ]) 276 | tyler = User(name = 'Tyler Long', users_i_follow = [peter, ], users_follow_me = [mark, ]) 277 | db.session.add_then_commit(tyler) 278 | 279 | tyler = db.session.query(User).filter_by(name = 'Tyler Long').one() 280 | print 'Tyler Long is following:', ', '.join(user.name for user in tyler.users_i_follow) 281 | print 'People who are following Tyler Long:', ', '.join(user.name for user in tyler.users_follow_me) 282 | mark = db.session.query(User).filter_by(name = 'Mark Wong').one() 283 | print 'Mark Wong is following:', ', '.join(user.name for user in mark.users_i_follow) 284 | 285 | 286 | | 287 | 288 | One-to-one relationship example 289 | ------------------------------- 290 | 291 | :: 292 | 293 | from quick_orm.core import Database 294 | from sqlalchemy import Column, String 295 | 296 | __metaclass__ = Database.DefaultMeta 297 | 298 | class User: 299 | name = Column(String(30)) 300 | 301 | @Database.one_to_one(User) 302 | class Contact: 303 | email = Column(String(70)) 304 | address = Column(String(70)) 305 | 306 | Database.register() 307 | 308 | if __name__ == '__main__': 309 | db = Database('sqlite://') 310 | db.create_tables() 311 | 312 | contact = Contact(email = 'quick.orm.feedback@gmail.com', address = 'Shenzhen, China') 313 | user = User(name = 'Tyler Long', contact = contact) 314 | db.session.add_then_commit(user) 315 | 316 | user = db.session.query(User).get(1) 317 | print 'User:', user.name 318 | print 'Email:', user.contact.email 319 | print 'Address:', user.contact.address 320 | 321 | 322 | | 323 | 324 | Multiple many-to-one relationships example 325 | ------------------------------------------ 326 | 327 | :: 328 | 329 | from quick_orm.core import Database 330 | from sqlalchemy import Column, String, Text 331 | 332 | __metaclass__ = Database.DefaultMeta 333 | 334 | class User: 335 | name = Column(String(30)) 336 | 337 | @Database.many_to_one(User, ref_name = 'author', backref_name = 'articles_authored') 338 | @Database.many_to_one(User, ref_name = 'editor', backref_name = 'articles_edited') 339 | class Article: 340 | title = Column(String(80)) 341 | content = Column(Text) 342 | 343 | Database.register() 344 | 345 | if __name__ == '__main__': 346 | db = Database('sqlite://') 347 | db.create_tables() 348 | 349 | author = User(name = 'Tyler Long') 350 | editor = User(name = 'Peter Lau') 351 | article = Article(author = author, editor = editor, title = 'quick_orm is super quick and easy', 352 | content = 'quick_orm is super quick and easy. Believe it or not.') 353 | db.session.add_then_commit(article) 354 | 355 | article = db.session.query(Article).get(1) 356 | print 'Article:', article.title 357 | print 'Author:', article.author.name 358 | print 'Editor:', article.editor.name 359 | 360 | 361 | | 362 | 363 | Performing raw sql query example 364 | -------------------------------- 365 | 366 | :: 367 | 368 | from quick_orm.core import Database 369 | from sqlalchemy import Column, String 370 | 371 | __metaclass__ = Database.DefaultMeta 372 | 373 | class User: 374 | name = Column(String(70)) 375 | 376 | Database.register() 377 | 378 | if __name__ == '__main__': 379 | db = Database('sqlite://') 380 | db.create_tables() 381 | 382 | count = db.engine.execute('select count(name) from user').scalar() 383 | print 'There are {0} users in total'.format(count) 384 | 385 | 386 | | 387 | 388 | Multiple databases example 389 | -------------------------- 390 | 391 | :: 392 | 393 | from quick_orm.core import Database 394 | from sqlalchemy import Column, String 395 | 396 | __metaclass__ = Database.DefaultMeta 397 | 398 | class User: 399 | name = Column(String(30)) 400 | 401 | Database.register() 402 | 403 | if __name__ == '__main__': 404 | db1 = Database('sqlite://') 405 | db1.create_tables() 406 | 407 | db2 = Database('sqlite://') 408 | db2.create_tables() 409 | 410 | user1 = User(name = 'user in db1') 411 | user2 = User(name = 'user in db2') 412 | db1.session.add_then_commit(user1) 413 | db2.session.add_then_commit(user2) 414 | 415 | print 'I am', db1.session.query(User).get(1).name 416 | print 'I am', db2.session.query(User).get(1).name 417 | 418 | 419 | | 420 | 421 | Table inheritance example 422 | ------------------------- 423 | 424 | :: 425 | 426 | from quick_orm.core import Database 427 | from sqlalchemy import Column, String, Text 428 | 429 | __metaclass__ = Database.DefaultMeta 430 | 431 | class User: 432 | name = Column(String(70)) 433 | 434 | @Database.many_to_one(User) 435 | class Post: 436 | content = Column(Text) 437 | 438 | class Question(Post): 439 | title = Column(String(70)) 440 | 441 | @Database.many_to_one(Question) 442 | class Answer(Post): 443 | pass 444 | 445 | @Database.many_to_one(Post) 446 | class Comment(Post): 447 | pass 448 | 449 | @Database.many_to_many(Post) 450 | class Tag: 451 | name = Column(String(70)) 452 | 453 | Database.register() 454 | 455 | if __name__ == '__main__': 456 | db = Database('sqlite://') 457 | db.create_tables() 458 | 459 | user1 = User(name = 'Tyler Long') 460 | user2 = User(name = 'Peter Lau') 461 | 462 | tag1 = Tag(name = 'quick_orm') 463 | tag2 = Tag(name = 'nice') 464 | 465 | question = Question(user = user1, title = 'What is quick_orm?', content = 'What is quick_orm?', tags = [tag1, ]) 466 | question2 = Question(user = user1, title = 'Have you tried quick_orm?', content = 'Have you tried quick_orm?', tags = [tag1, ]) 467 | 468 | answer = Answer(user = user1, question = question, tags = [tag1, ], 469 | content = 'quick_orm is a Python ORM framework which enables you to get started in less than a minute!') 470 | 471 | comment1 = Comment(user = user2, content = 'good question', post = question) 472 | comment2 = Comment(user = user2, content = 'nice answer', post = answer, tags = [tag2, ]) 473 | 474 | db.session.add_all_then_commit([question, question2, answer, comment1, comment2, tag1, tag2, ]) 475 | 476 | question = db.session.query(Question).get(1) 477 | print 'tags for question "{0}": "{1}"'.format(question.title, ', '.join(tag.name for tag in question.tags)) 478 | print 'new comment for question:', question.comments.first().content 479 | print 'new comment for answer:', question.answers.first().comments.first().content 480 | 481 | user = db.session.query(User).filter_by(name = 'Peter Lau').one() 482 | print 'Peter Lau has posted {0} comments'.format(user.comments.count()) 483 | 484 | tag = db.session.query(Tag).filter_by(name = 'quick_orm').first() 485 | print '{0} questions are tagged "quick_orm"'.format(tag.questions.count()) 486 | 487 | 488 | | 489 | 490 | MetaBuilder to avoid duplicate code example 491 | ------------------------------------------- 492 | 493 | :: 494 | 495 | from quick_orm.core import Database 496 | from sqlalchemy import Column, String 497 | 498 | class DefaultModel: 499 | name = Column(String(70)) 500 | 501 | __metaclass__ = Database.MetaBuilder(DefaultModel) 502 | 503 | class User: 504 | pass 505 | 506 | class Group: 507 | pass 508 | 509 | Database.register() 510 | 511 | if __name__ == '__main__': 512 | db = Database('sqlite://') 513 | db.create_tables() 514 | user = User(name = 'tylerlong') 515 | db.session.add(user) 516 | group = Group(name = 'python') 517 | db.session.add_then_commit(group) 518 | 519 | print user.name 520 | print group.name 521 | 522 | 523 | | 524 | 525 | Model for stackoverflow.com example 526 | ----------------------------------- 527 | 528 | :: 529 | 530 | from quick_orm.core import Database 531 | from sqlalchemy import Column, String, Text 532 | 533 | __metaclass__ = Database.DefaultMeta 534 | 535 | @Database.many_to_many('User', ref_name = 'followed_users', backref_name = 'followers') 536 | class User: 537 | email = Column(String(200)) 538 | name = Column(String(100)) 539 | 540 | @Database.many_to_one(User) 541 | class Post: 542 | content = Column(Text) 543 | 544 | @Database.many_to_one(Post) 545 | class Comment(Post): 546 | pass 547 | 548 | class Question(Post): 549 | title = Column(String(200)) 550 | 551 | @Database.many_to_one(Question) 552 | class Answer(Post): 553 | pass 554 | 555 | @Database.many_to_many(Post) 556 | class Tag: 557 | name = Column(String(50)) 558 | 559 | @Database.many_to_one(User, ref_name = 'sender', backref_name = 'messages_sent') 560 | @Database.many_to_one(User, ref_name = 'receiver', backref_name = 'messages_received') 561 | class Message: 562 | content = Column(Text) 563 | 564 | @Database.many_to_one(User) 565 | @Database.many_to_one(Post) 566 | class Vote: 567 | type = Column(String(20)) #"vote_up" or "vote_down" 568 | 569 | Database.register() 570 | 571 | if __name__ == '__main__': 572 | db = Database('sqlite://') 573 | db.create_tables() 574 | 575 | user1 = User(email = 'tylerlong@example.com', name = 'Tyler Long') 576 | user2 = User(email = 'peterlau@example.com', name = 'Peter Lau') 577 | 578 | tag1 = Tag(name = 'Python') 579 | tag2 = Tag(name = 'quick_orm') 580 | 581 | question1 = Question(user = user1, title = 'Can you program in Python?', content = 'RT') 582 | question2 = Question(user = user1, title = 'Do you know quick_orm?', content = 'RT') 583 | 584 | answer1 = Answer(user = user2, question = question1, content = 'Yes I can') 585 | answer2 = Answer(user = user2, question = question2, content = 'No I don\'t') 586 | 587 | comment1 = Comment(user = user1, content = 'You rock') 588 | comment2 = Comment(user = user1, content = 'You suck') 589 | 590 | answer1.comments = [comment1,] 591 | answer2.comments = [comment2,] 592 | 593 | user1.followers = [user2,] 594 | question1.tags = [tag1,] 595 | answer2.tags = [tag2,] 596 | 597 | vote1 = Vote(user = user1, type = 'vote_up', post = question1) 598 | vote2 = Vote(user = user2, type = 'vote_up', post = question1) 599 | vote2 = Vote(user = user2, type = 'vote_down', post = question2) 600 | 601 | db.session.add_all_then_commit([user1, user2,]) 602 | 603 | print user2.name, 'is following', ', '.join(user.name for user in user2.followed_users) 604 | print user1.name, 'questions:', ', '.join(question.title for question in user1.questions) 605 | print 'question1 tags:', ', '.join(tag.name for tag in question1.tags) 606 | print 'answer2 comments:', ', '.join(comment.content for comment in answer2.comments) 607 | print 'answer "', answer1.content, '" is for question: "', answer1.question.title, '"' 608 | print 'there are {0} vote_ups for question "{1}"'.format(question1.votes.filter_by(type = 'vote_up').count(), question1.title) 609 | 610 | 611 | | 612 | 613 | Examples from real life 614 | ----------------------- 615 | - Everblog_ is a personal blogging platform taking advantage of evernote, it chooses quick_orm as its ORM framework. Refer to `everblog's database model file`_ for more detail. 616 | 617 | .. _Everblog: https://github.com/tylerlong/everblog 618 | .. _`everblog's database model file`: https://github.com/tylerlong/everblog/blob/master/everblog/models.py 619 | 620 | If you know any other successful stories about quick_orm, do tell me and I will list them above. 621 | 622 | 623 | | 624 | 625 | Where to learn more about quick_orm? 626 | ------------------------------------ 627 | As said above, quick_orm is built upon SQLAlchemy. quick_orm never tries to hide SQLAlchemy's flexibility and power. Everything availiable in SQLAlchemy is still available in quick_orm. 628 | 629 | So please read the documents of SQLAlchemy, you would learn much more there than you could here. 630 | 631 | Read quick_orm's source code, try to improve it. 632 | 633 | 634 | | 635 | 636 | You wanna involve? 637 | ------------------ 638 | quick_orm is released under BSD lisence. 639 | 640 | The source code is hosted on github: https://github.com/tylerlong/quick_orm 641 | 642 | 643 | | 644 | 645 | Acknowledgements 646 | ---------------- 647 | quick_orm is built upon SQLAlchemy - the famous Python SQL Toolkit and Object Relational Mapper. All of the glory belongs to the SQLAlchemy development team and the SQLAlchemy community! My contribution to quick_orm becomes trivial compared with theirs( to SQLAlchemy). 648 | 649 | 650 | | 651 | 652 | Feedback 653 | -------- 654 | Comments, suggestions, questions, free beer, t-shirts, kindles, ipads ... are all welcome! 655 | 656 | Email: quick.orm.feedback@gmail.com 657 | 658 | 659 | | 660 | 661 | todo list 662 | --------- 663 | #. full text search. (class decorator for model?) 664 | #. orm for nosql? such as this one: http://qslack.com/projects/rhino-a-ruby-hbase-orm/ 665 | #. ref_grandchildren can't access some attributes of grandchildren. for example: everblog project: tag.blog_entrys.lang report an error. 666 | #. generate visual charts according to model. It is good for analyzing and demonstrating. 667 | #. multiple many_to_many between two models 668 | #. make table name customizable 669 | -------------------------------------------------------------------------------- /README_template: -------------------------------------------------------------------------------- 1 | quick_orm 2 | ========= 3 | - **News**: quick_orm is fully compatible with the newest SQLAlchemy 0.7.9. 4 | - **Notice**: quick_orm is NOT YET compatible with SQLAlchemy 0.8.x. 5 | 6 | 7 | | 8 | 9 | Introduction 10 | ------------ 11 | Python ORM framework which enables you to get started in less than a minute! 12 | 13 | Super easy to setup and super easy to use, yet super powerful! 14 | 15 | You would regret that you didn't discorver it earlier! 16 | 17 | 18 | | 19 | 20 | Features 21 | -------- 22 | - quick: you could get and play with it in less than a minute. It couldn't be more straightforward. 23 | - easy: you don't have to write any SQL statements, including those "create table xxx ..." ones. 24 | - simple: the core code counts only {{ lines_count }} lines including comments and pydocs, there is no room for bugs. 25 | - free: released under BSD license, you are free to use it and distribute it. 26 | - powerful: built upon SQLAlchemy and doesn't compromise its power. 27 | - support relationships by means of python decorators. 28 | - support table inheritance in a most natural way. 29 | - support multiple databases: you can map your models to many databases without difficulty. 30 | - write less, do more: taking advantage of python metaclass reduces data modeling code dramatically. 31 | - long-term maintained: Continous efforts are taken to improve and maintain it. 32 | 33 | 34 | | 35 | 36 | Quick Start 37 | ----------- 38 | 39 | :: 40 | 41 | pip install quick_orm 42 | 43 | Refer to the following examples to write your own database manipulation code. 44 | 45 | {{ examples }} 46 | 47 | 48 | | 49 | 50 | Examples from real life 51 | ----------------------- 52 | - Everblog_ is a personal blogging platform taking advantage of evernote, it chooses quick_orm as its ORM framework. Refer to `everblog's database model file`_ for more detail. 53 | 54 | .. _Everblog: https://github.com/tylerlong/everblog 55 | .. _`everblog's database model file`: https://github.com/tylerlong/everblog/blob/master/everblog/models.py 56 | 57 | If you know any other successful stories about quick_orm, do tell me and I will list them above. 58 | 59 | 60 | | 61 | 62 | Where to learn more about quick_orm? 63 | ------------------------------------ 64 | As said above, quick_orm is built upon SQLAlchemy. quick_orm never tries to hide SQLAlchemy's flexibility and power. Everything availiable in SQLAlchemy is still available in quick_orm. 65 | 66 | So please read the documents of SQLAlchemy, you would learn much more there than you could here. 67 | 68 | Read quick_orm's source code, try to improve it. 69 | 70 | 71 | | 72 | 73 | You wanna involve? 74 | ------------------ 75 | quick_orm is released under BSD lisence. 76 | 77 | The source code is hosted on github: https://github.com/tylerlong/quick_orm 78 | 79 | 80 | | 81 | 82 | Acknowledgements 83 | ---------------- 84 | quick_orm is built upon SQLAlchemy - the famous Python SQL Toolkit and Object Relational Mapper. All of the glory belongs to the SQLAlchemy development team and the SQLAlchemy community! My contribution to quick_orm becomes trivial compared with theirs( to SQLAlchemy). 85 | 86 | 87 | | 88 | 89 | Feedback 90 | -------- 91 | Comments, suggestions, questions, free beer, t-shirts, kindles, ipads ... are all welcome! 92 | 93 | Email: quick.orm.feedback@gmail.com 94 | 95 | 96 | | 97 | 98 | todo list 99 | --------- 100 | #. full text search. (class decorator for model?) 101 | #. orm for nosql? such as this one: http://qslack.com/projects/rhino-a-ruby-hbase-orm/ 102 | #. ref_grandchildren can't access some attributes of grandchildren. for example: everblog project: tag.blog_entrys.lang report an error. 103 | #. generate visual charts according to model. It is good for analyzing and demonstrating. 104 | #. multiple many_to_many between two models 105 | #. make table name customizable 106 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | manage 4 | ~~~~~~ 5 | Provides useful commands to manage the project 6 | """ 7 | import sys, subprocess 8 | from toolkit_library.inspector import ModuleInspector, PackageInspector 9 | from toolkit_library.input_util import InputUtil 10 | from quick_orm import examples 11 | 12 | def run_examples(): 13 | """Run all of the examples""" 14 | # automatically install the newest code before testing 15 | subprocess.call([sys.executable, 'setup.py', 'install']) 16 | 17 | # call each example file and print the result 18 | for module in PackageInspector(examples).get_all_modules(): 19 | print '****** {0} ******'.format(module) 20 | subprocess.call([sys.executable, 'quick_orm/examples/{0}.py'.format(module)]) 21 | print 22 | 23 | 24 | def run_tests(): 25 | """Run unit tests""" 26 | from quick_orm.testsuite import run_testsuite 27 | db_name = InputUtil.get_input('database', default = 'sqlite', pattern = '(?:mysql|sqlite|postgresql)') 28 | run_testsuite(db_name) 29 | 30 | 31 | if __name__ == '__main__': 32 | inspector = ModuleInspector(sys.modules[__name__]) 33 | inspector.invoke(*sys.argv[1:]) 34 | -------------------------------------------------------------------------------- /quick_orm/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'Tyler Long' 2 | __version__ = '0.4.4' 3 | -------------------------------------------------------------------------------- /quick_orm/column_types.py: -------------------------------------------------------------------------------- 1 | # encoding=utf-8 2 | """ 3 | quick_orm.column_types 4 | ~~~~~~~~~~~~~~~~~~~~~~ 5 | More database column types 6 | """ 7 | import json 8 | from sqlalchemy.types import TypeDecorator, String 9 | 10 | class JsonType(TypeDecorator): 11 | '''Dumps simple python data structures to json format and stores them as string 12 | Convert the data back to original python data structures when read. 13 | Differences from sqlalchemy PickleType: PickleType only supports python, JsonType supports a lot of languages 14 | Think that you might want to read the data out of database using Java or PHP(or C#...etc). 15 | ''' 16 | 17 | impl = String 18 | 19 | def process_bind_param(self, value, dialect): 20 | if value is not None: 21 | value = json.dumps(value) 22 | return value 23 | 24 | def process_result_value(self, value, dialect): 25 | if value is not None: 26 | value = json.loads(value) 27 | return value 28 | -------------------------------------------------------------------------------- /quick_orm/core.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | quick_orm.core 4 | ~~~~~~~~~~~~~~ 5 | Core of quick_orm 6 | """ 7 | from toolkit_library.string_util import StringUtil 8 | from sqlalchemy import create_engine, Column, Integer, ForeignKey, String, DateTime, event 9 | from sqlalchemy.orm import scoped_session, sessionmaker, relationship, backref 10 | from sqlalchemy.schema import Table 11 | from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta, _as_declarative 12 | from extensions import DatabaseExtension, SessionExtension, ModelExtension 13 | 14 | models = list() 15 | 16 | class MyDeclarativeMeta(DeclarativeMeta): 17 | #override __init__ of DeclarativeMeta 18 | def __init__(cls, classname, bases, attrs): 19 | models.append(cls) 20 | return type.__init__(cls, classname, bases, attrs) 21 | 22 | 23 | @DatabaseExtension.extend # extend Database to add some useful methods 24 | class Database(object): 25 | 26 | Base = declarative_base() 27 | 28 | @staticmethod 29 | def register(): 30 | for i in range(len(models)): 31 | model = models[i] 32 | if '_decl_class_registry' in model.__dict__: 33 | continue 34 | _as_declarative(model, model.__name__, model.__dict__) 35 | 36 | #for auto-update timestamps 37 | event.listen(model, 'before_insert', ModelExtension.before_insert_listener) 38 | event.listen(model, 'before_update', ModelExtension.before_update_listener) 39 | 40 | # for ref grandchildren 41 | for j in range(i): 42 | if not models[j] in model.__bases__: 43 | continue 44 | parent = models[j] 45 | for grandparent in parent._many_to_models: 46 | setattr(grandparent, model._readable_names, 47 | (lambda parent, model: property(lambda self: getattr(self, parent._readable_names) 48 | .filter_by(real_type = model._readable_name)))(parent, model)) 49 | 50 | for grandparent in parent._one_to_models: 51 | setattr(grandparent, model._readable_name, 52 | (lambda parent, model: property(lambda self: getattr(self, parent._readable_name) 53 | if getattr(self, parent._readable_name).real_type == model._readable_name else None))(parent, model)) 54 | models[:] = [] 55 | 56 | def __init__(self, connection_string, **kwargs): 57 | """Initiate a database engine which is very low level, and a database session which deals with orm.""" 58 | 59 | # Solve an issue with mysql character encoding(maybe it's a bug of MySQLdb) 60 | # Refer to http://plone.293351.n2.nabble.com/Troubles-with-encoding-SQLAlchemy-MySQLdb-or-mysql-configuration-pb-td4827540.html 61 | if 'mysql:' in connection_string and 'charset=' not in connection_string: 62 | raise ValueError("""No charset was specified for a mysql connection string. 63 | Please specify something like '?charset=utf8' explicitly.""") 64 | 65 | self.engine = create_engine(connection_string, convert_unicode = True, encoding = 'utf-8', **kwargs) 66 | self.session = SessionExtension.extend(scoped_session(sessionmaker(autocommit = False, autoflush = False, bind = self.engine))) 67 | 68 | @staticmethod 69 | def foreign_key(ref_model, ref_name = None, backref_name = None, one_to_one = False): 70 | """"Class decorator, add a foreign key to a SQLAlchemy model. 71 | Parameters: 72 | ref_model is the destination model, in a one-to-many relationship, it is the "one" side. 73 | ref_name is the user-friendly name of destination model(if omitted, destintion table name will be used instead). 74 | backref_name is the name used to back ref the "many" side. 75 | one_to_one is this foreign_key for a one-to-one relationship? 76 | """ 77 | if isinstance(ref_model, str): 78 | ref_model_name = ref_model 79 | else: 80 | ref_model_name = ref_model.__name__ 81 | ref_table_name = StringUtil.camelcase_to_underscore(ref_model_name) 82 | ref_name = ref_name or ref_table_name 83 | foreign_key = '{0}_id'.format(ref_name) 84 | def ref_table(cls): 85 | if one_to_one: 86 | if backref_name: 87 | cls._readable_name = backref_name 88 | if not isinstance(ref_model, str): 89 | if ref_name: 90 | ref_model._readable_name = ref_name 91 | cls._one_to_models.append(ref_model) 92 | ref_model._one_to_models.append(cls) 93 | else: 94 | if backref_name: 95 | cls._readable_names = backref_name 96 | if not isinstance(ref_model, str): 97 | if ref_name: 98 | ref_model._readable_name = ref_name 99 | cls._many_to_models.append(ref_model) 100 | ref_model._one_to_models.append(cls) 101 | model_name = cls.__name__ 102 | table_name = cls._readable_name 103 | setattr(cls, foreign_key, Column(Integer, ForeignKey('{0}.id'.format(ref_table_name), ondelete = "CASCADE"))) 104 | my_backref_name = backref_name or (table_name if one_to_one else '{0}s'.format(table_name)) 105 | backref_options = dict(uselist = False) if one_to_one else dict(lazy = 'dynamic') 106 | backref_options['cascade'] = 'all' 107 | setattr(cls, ref_name, relationship(ref_model_name, 108 | primaryjoin = '{0}.{1} == {2}.id'.format(model_name, foreign_key, ref_model_name), 109 | backref = backref(my_backref_name, **backref_options), remote_side = '{0}.id'.format(ref_model_name))) 110 | return cls 111 | return ref_table 112 | 113 | @staticmethod 114 | def many_to_one(ref_model, ref_name = None, backref_name = None): 115 | return Database.foreign_key(ref_model, ref_name, backref_name, False) 116 | 117 | @staticmethod 118 | def one_to_one(ref_model, ref_name = None, backref_name = None): 119 | return Database.foreign_key(ref_model, ref_name, backref_name, True) 120 | 121 | @staticmethod 122 | def many_to_many(ref_model, ref_name = None, backref_name = None, middle_table_name = None): 123 | """Class Decorator, add a many-to-many relationship between two SQLAlchemy models. 124 | Parameters: 125 | ref_table_name is the name of the destination table, it is NOT the one decorated by this method. 126 | ref_name is how this model reference the destination models. 127 | backref_name is how the destination model reference this model. 128 | middle_table_name is the middle table name of this many-to-many relationship. 129 | """ 130 | if isinstance(ref_model, str): 131 | ref_model_name = ref_model 132 | else: 133 | ref_model_name = ref_model.__name__ 134 | ref_table_name = StringUtil.camelcase_to_underscore(ref_model_name) 135 | ref_name = ref_name or '{0}s'.format(ref_table_name) 136 | def ref_table(cls): 137 | if backref_name: 138 | cls._readable_names = backref_name 139 | if not isinstance(ref_model, str): 140 | ref_model._readable_names = ref_name 141 | cls._many_to_models.append(ref_model) 142 | ref_model._many_to_models.append(cls) 143 | table_name = cls._readable_name 144 | 145 | my_middle_table_name = middle_table_name or '{0}_{1}'.format(table_name, ref_table_name) 146 | if table_name == ref_table_name: 147 | left_column_name = 'left_id' 148 | right_column_name = 'right_id' 149 | else: 150 | left_column_name = '{0}_id'.format(table_name) 151 | right_column_name = '{0}_id'.format(ref_table_name) 152 | middle_table = Table(my_middle_table_name, Database.Base.metadata, 153 | Column(left_column_name, Integer, ForeignKey('{0}.id'.format(table_name), ondelete = "CASCADE"), primary_key = True), 154 | Column(right_column_name, Integer, ForeignKey('{0}.id'.format(ref_table_name), ondelete = "CASCADE"), primary_key = True)) 155 | 156 | my_backref_name = backref_name or '{0}s'.format(table_name) 157 | parameters = dict(secondary = middle_table, lazy = 'dynamic', backref = backref(my_backref_name, lazy = 'dynamic')) 158 | if table_name == ref_table_name: 159 | parameters['primaryjoin'] = cls.id == middle_table.c.left_id 160 | parameters['secondaryjoin'] = cls.id == middle_table.c.right_id 161 | 162 | setattr(cls, ref_name, relationship(ref_model_name, **parameters)) 163 | 164 | return cls 165 | return ref_table 166 | 167 | 168 | class DefaultMeta(MyDeclarativeMeta): 169 | """metaclass for all model classes, let model class inherit Database.Base and handle table inheritance. 170 | All other model metaclasses are either directly or indirectly derived from this class. 171 | """ 172 | def __new__(cls, name, bases, attrs): 173 | # add Database.Base as parent class 174 | bases = list(bases) 175 | if object in bases: 176 | bases.remove(object) 177 | bases.append(Database.Base) 178 | seen = set() 179 | bases = tuple(base for base in bases if not base in seen and not seen.add(base)) 180 | 181 | attrs['__tablename__'] = StringUtil.camelcase_to_underscore(name) 182 | attrs['id'] = Column(Integer, primary_key = True) 183 | attrs['created_at'] = Column(DateTime) 184 | attrs['updated_at'] = Column(DateTime) 185 | attrs['_readable_name'] = attrs['__tablename__'] 186 | attrs['_readable_names'] = attrs['_readable_name'] + 's' 187 | attrs['_one_to_models'] = attrs['_many_to_models'] = [] 188 | if not '__mapper_args__' in attrs: 189 | attrs['__mapper_args__'] = {} 190 | 191 | # the for loop bellow handles table inheritance 192 | for base in [base for base in bases if base in models]: 193 | if not hasattr(base, 'real_type'): 194 | base.real_type = Column('real_type', String(24), nullable = False, index = True) 195 | base.__mapper_args__['polymorphic_on'] = base.real_type 196 | base.__mapper_args__['polymorphic_identity'] = base._readable_name 197 | attrs['id'] = Column(Integer, ForeignKey('{0}.id'.format(base._readable_name), ondelete = "CASCADE"), primary_key = True) 198 | attrs['__mapper_args__']['polymorphic_identity'] = attrs['_readable_name'] 199 | attrs['__mapper_args__']['inherit_condition'] = attrs['id'] == base.id 200 | 201 | return MyDeclarativeMeta.__new__(cls, name, bases, attrs) 202 | 203 | 204 | @staticmethod 205 | def MetaBuilder(*models): 206 | """Build a new model metaclass. The new metaclass is derived from Database.DefaultMeta, 207 | and it will add *models as base classes to a model class. 208 | """ 209 | class InnerMeta(Database.DefaultMeta): 210 | """metaclass for model class, it will add *models as bases of the model class.""" 211 | def __new__(cls, name, bases, attrs): 212 | bases = list(bases) 213 | bases.extend(models) 214 | seen = set() 215 | bases = tuple(base for base in bases if not base in seen and not seen.add(base)) 216 | return Database.DefaultMeta.__new__(cls, name, bases, attrs) 217 | return InnerMeta 218 | -------------------------------------------------------------------------------- /quick_orm/examples/__init__.py: -------------------------------------------------------------------------------- 1 | examples_list = [ 2 | 'hello_world', 3 | 'many_to_one', 4 | 'many_to_one_options', 5 | 'many_to_one_to_self', 6 | 'many_to_many', 7 | 'many_to_many_options', 8 | 'many_to_many_to_self', 9 | 'one_to_one', 10 | 'multiple_many_to_one', 11 | 'raw_sql', 12 | 'multiple_databases', 13 | 'table_inheritance', 14 | 'meta_builder', 15 | 'model_for_stackoverflow', 16 | ] 17 | 18 | examples_dict = { 19 | 'hello_world': 'Hello World', 20 | 'many_to_one': 'Many-to-one relationship', 21 | 'many_to_one_options': 'Many-to-one relationship options', 22 | 'many_to_one_to_self': 'Many-to-one relationship with oneself', 23 | 'many_to_many': 'Many-to-many relationship', 24 | 'many_to_many_options': 'Many-to-many relationship options', 25 | 'many_to_many_to_self': 'Many-to-many relationship with oneself', 26 | 'one_to_one': 'One-to-one relationship', 27 | 'multiple_many_to_one': 'Multiple many-to-one relationships', 28 | 'raw_sql': 'Performing raw sql query', 29 | 'multiple_databases': 'Multiple databases', 30 | 'table_inheritance': 'Table inheritance', 31 | 'meta_builder': 'MetaBuilder to avoid duplicate code', 32 | 'model_for_stackoverflow': 'Model for stackoverflow.com', 33 | } 34 | -------------------------------------------------------------------------------- /quick_orm/examples/hello_world.py: -------------------------------------------------------------------------------- 1 | from quick_orm.core import Database 2 | from sqlalchemy import Column, String 3 | 4 | __metaclass__ = Database.DefaultMeta 5 | 6 | class User: 7 | name = Column(String(30)) 8 | 9 | Database.register() 10 | 11 | if __name__ == '__main__': 12 | db = Database('sqlite://') # database urls: http://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls 13 | db.create_tables() # create tables, you don't have to write any SQL. 14 | 15 | user = User(name = 'Hello World') 16 | db.session.add_then_commit(user) # commit user to database. 17 | 18 | user = db.session.query(User).get(1) 19 | print 'My name is', user.name 20 | print 'created_at', user.created_at # created_at and updated_at timestamps are added automatically. 21 | print 'updated_at', user.updated_at 22 | 23 | user.name = 'Tyler Long' 24 | db.session.commit() # commit changes to database. 25 | print 'My name is', user.name 26 | print 'created_at', user.created_at 27 | print 'updated_at', user.updated_at -------------------------------------------------------------------------------- /quick_orm/examples/many_to_many.py: -------------------------------------------------------------------------------- 1 | from quick_orm.core import Database 2 | from sqlalchemy import Column, String 3 | 4 | __metaclass__ = Database.DefaultMeta 5 | 6 | class User: 7 | name = Column(String(30)) 8 | 9 | @Database.many_to_many(User) 10 | class Role: 11 | name = Column(String(30)) 12 | 13 | Database.register() 14 | 15 | if __name__ == '__main__': 16 | db = Database('sqlite://') 17 | db.create_tables() 18 | 19 | user1 = User(name = 'Tyler Long') 20 | user2 = User(name = 'Peter Lau') 21 | role = Role(name = 'Administrator', users = [user1, user2]) 22 | db.session.add_then_commit(role) 23 | 24 | admin_role = db.session.query(Role).filter_by(name = 'Administrator').one() 25 | print ', '.join([user.name for user in admin_role.users]), 'are administrators' -------------------------------------------------------------------------------- /quick_orm/examples/many_to_many_options.py: -------------------------------------------------------------------------------- 1 | from quick_orm.core import Database 2 | from sqlalchemy import Column, String 3 | 4 | __metaclass__ = Database.DefaultMeta 5 | 6 | class User: 7 | name = Column(String(30)) 8 | 9 | @Database.many_to_many(User, ref_name = 'users', backref_name = 'roles', middle_table_name = 'user_role') 10 | class Role: 11 | name = Column(String(30)) 12 | 13 | Database.register() 14 | 15 | if __name__ == '__main__': 16 | db = Database('sqlite://') 17 | db.create_tables() 18 | 19 | user1 = User(name = 'Tyler Long') 20 | user2 = User(name = 'Peter Lau') 21 | role = Role(name = 'Administrator', users = [user1, user2]) 22 | db.session.add_then_commit(role) 23 | 24 | admin_role = db.session.query(Role).filter_by(name = 'Administrator').one() 25 | print ', '.join([user.name for user in admin_role.users]), 'are administrators' -------------------------------------------------------------------------------- /quick_orm/examples/many_to_many_to_self.py: -------------------------------------------------------------------------------- 1 | from quick_orm.core import Database 2 | from sqlalchemy import Column, String 3 | 4 | __metaclass__ = Database.DefaultMeta 5 | 6 | @Database.many_to_many('User', ref_name = 'users_i_follow', backref_name = 'users_follow_me') 7 | class User: 8 | name = Column(String(30)) 9 | 10 | Database.register() 11 | 12 | if __name__ == '__main__': 13 | db = Database('sqlite://') 14 | db.create_tables() 15 | 16 | peter = User(name = 'Peter Lau') 17 | mark = User(name = 'Mark Wong', users_i_follow = [peter, ]) 18 | tyler = User(name = 'Tyler Long', users_i_follow = [peter, ], users_follow_me = [mark, ]) 19 | db.session.add_then_commit(tyler) 20 | 21 | tyler = db.session.query(User).filter_by(name = 'Tyler Long').one() 22 | print 'Tyler Long is following:', ', '.join(user.name for user in tyler.users_i_follow) 23 | print 'People who are following Tyler Long:', ', '.join(user.name for user in tyler.users_follow_me) 24 | mark = db.session.query(User).filter_by(name = 'Mark Wong').one() 25 | print 'Mark Wong is following:', ', '.join(user.name for user in mark.users_i_follow) -------------------------------------------------------------------------------- /quick_orm/examples/many_to_one.py: -------------------------------------------------------------------------------- 1 | from quick_orm.core import Database 2 | from sqlalchemy import Column, String, Text 3 | 4 | __metaclass__ = Database.DefaultMeta 5 | 6 | class Question: 7 | title = Column(String(70)) 8 | content = Column(Text) 9 | 10 | @Database.many_to_one(Question) 11 | class Answer: 12 | content = Column(Text) 13 | 14 | Database.register() 15 | 16 | if __name__ == '__main__': 17 | db = Database('sqlite://') 18 | db.create_tables() 19 | 20 | question = Question(title = 'What is quick_orm?', content = 'What is quick_orm?') 21 | answer = Answer(question = question, content = 'quick_orm is a Python ORM framework which enables you to get started in less than a minute!') 22 | db.session.add_then_commit(answer) 23 | 24 | question = db.session.query(Question).get(1) 25 | print 'The question is:', question.title 26 | print 'The answer is:', question.answers.first().content -------------------------------------------------------------------------------- /quick_orm/examples/many_to_one_options.py: -------------------------------------------------------------------------------- 1 | from quick_orm.core import Database 2 | from sqlalchemy import Column, String, Text 3 | 4 | __metaclass__ = Database.DefaultMeta 5 | 6 | class Question: 7 | title = Column(String(70)) 8 | content = Column(Text) 9 | 10 | @Database.many_to_one(Question, ref_name = 'question', backref_name = 'answers') 11 | class Answer: 12 | content = Column(Text) 13 | 14 | Database.register() 15 | 16 | if __name__ == '__main__': 17 | db = Database('sqlite://') 18 | db.create_tables() 19 | 20 | question = Question(title = 'What is quick_orm?', content = 'What is quick_orm?') 21 | answer = Answer(question = question, content = 'quick_orm is a Python ORM framework which enables you to get started in less than a minute!') 22 | db.session.add_then_commit(answer) 23 | 24 | question = db.session.query(Question).get(1) 25 | print 'The question is:', question.title 26 | print 'The answer is:', question.answers.first().content -------------------------------------------------------------------------------- /quick_orm/examples/many_to_one_to_self.py: -------------------------------------------------------------------------------- 1 | from quick_orm.core import Database 2 | from sqlalchemy import Column, String 3 | 4 | __metaclass__ = Database.DefaultMeta 5 | 6 | @Database.many_to_one('Node', ref_name = 'parent_node', backref_name = 'children_nodes') 7 | class Node: 8 | name = Column(String(70)) 9 | 10 | Database.register() 11 | 12 | if __name__ == '__main__': 13 | db = Database('sqlite://') 14 | db.create_tables() 15 | 16 | root_node = Node(name = 'root') 17 | node1 = Node(name = 'node1', parent_node = root_node) 18 | node2 = Node(name = 'node2', parent_node = root_node) 19 | db.session.add_then_commit(root_node) 20 | 21 | root_node = db.session.query(Node).filter_by(name = 'root').one() 22 | print 'Root node has {0} children nodes, they are {1}'\ 23 | .format(root_node.children_nodes.count(), ', '.join(node.name for node in root_node.children_nodes)) -------------------------------------------------------------------------------- /quick_orm/examples/meta_builder.py: -------------------------------------------------------------------------------- 1 | from quick_orm.core import Database 2 | from sqlalchemy import Column, String 3 | 4 | class DefaultModel: 5 | name = Column(String(70)) 6 | 7 | __metaclass__ = Database.MetaBuilder(DefaultModel) 8 | 9 | class User: 10 | pass 11 | 12 | class Group: 13 | pass 14 | 15 | Database.register() 16 | 17 | if __name__ == '__main__': 18 | db = Database('sqlite://') 19 | db.create_tables() 20 | user = User(name = 'tylerlong') 21 | db.session.add(user) 22 | group = Group(name = 'python') 23 | db.session.add_then_commit(group) 24 | 25 | print user.name 26 | print group.name -------------------------------------------------------------------------------- /quick_orm/examples/model_for_stackoverflow.py: -------------------------------------------------------------------------------- 1 | from quick_orm.core import Database 2 | from sqlalchemy import Column, String, Text 3 | 4 | __metaclass__ = Database.DefaultMeta 5 | 6 | @Database.many_to_many('User', ref_name = 'followed_users', backref_name = 'followers') 7 | class User: 8 | email = Column(String(200)) 9 | name = Column(String(100)) 10 | 11 | @Database.many_to_one(User) 12 | class Post: 13 | content = Column(Text) 14 | 15 | @Database.many_to_one(Post) 16 | class Comment(Post): 17 | pass 18 | 19 | class Question(Post): 20 | title = Column(String(200)) 21 | 22 | @Database.many_to_one(Question) 23 | class Answer(Post): 24 | pass 25 | 26 | @Database.many_to_many(Post) 27 | class Tag: 28 | name = Column(String(50)) 29 | 30 | @Database.many_to_one(User, ref_name = 'sender', backref_name = 'messages_sent') 31 | @Database.many_to_one(User, ref_name = 'receiver', backref_name = 'messages_received') 32 | class Message: 33 | content = Column(Text) 34 | 35 | @Database.many_to_one(User) 36 | @Database.many_to_one(Post) 37 | class Vote: 38 | type = Column(String(20)) #"vote_up" or "vote_down" 39 | 40 | Database.register() 41 | 42 | if __name__ == '__main__': 43 | db = Database('sqlite://') 44 | db.create_tables() 45 | 46 | user1 = User(email = 'tylerlong@example.com', name = 'Tyler Long') 47 | user2 = User(email = 'peterlau@example.com', name = 'Peter Lau') 48 | 49 | tag1 = Tag(name = 'Python') 50 | tag2 = Tag(name = 'quick_orm') 51 | 52 | question1 = Question(user = user1, title = 'Can you program in Python?', content = 'RT') 53 | question2 = Question(user = user1, title = 'Do you know quick_orm?', content = 'RT') 54 | 55 | answer1 = Answer(user = user2, question = question1, content = 'Yes I can') 56 | answer2 = Answer(user = user2, question = question2, content = 'No I don\'t') 57 | 58 | comment1 = Comment(user = user1, content = 'You rock') 59 | comment2 = Comment(user = user1, content = 'You suck') 60 | 61 | answer1.comments = [comment1,] 62 | answer2.comments = [comment2,] 63 | 64 | user1.followers = [user2,] 65 | question1.tags = [tag1,] 66 | answer2.tags = [tag2,] 67 | 68 | vote1 = Vote(user = user1, type = 'vote_up', post = question1) 69 | vote2 = Vote(user = user2, type = 'vote_up', post = question1) 70 | vote2 = Vote(user = user2, type = 'vote_down', post = question2) 71 | 72 | db.session.add_all_then_commit([user1, user2,]) 73 | 74 | print user2.name, 'is following', ', '.join(user.name for user in user2.followed_users) 75 | print user1.name, 'questions:', ', '.join(question.title for question in user1.questions) 76 | print 'question1 tags:', ', '.join(tag.name for tag in question1.tags) 77 | print 'answer2 comments:', ', '.join(comment.content for comment in answer2.comments) 78 | print 'answer "', answer1.content, '" is for question: "', answer1.question.title, '"' 79 | print 'there are {0} vote_ups for question "{1}"'.format(question1.votes.filter_by(type = 'vote_up').count(), question1.title) 80 | -------------------------------------------------------------------------------- /quick_orm/examples/multiple_databases.py: -------------------------------------------------------------------------------- 1 | from quick_orm.core import Database 2 | from sqlalchemy import Column, String 3 | 4 | __metaclass__ = Database.DefaultMeta 5 | 6 | class User: 7 | name = Column(String(30)) 8 | 9 | Database.register() 10 | 11 | if __name__ == '__main__': 12 | db1 = Database('sqlite://') 13 | db1.create_tables() 14 | 15 | db2 = Database('sqlite://') 16 | db2.create_tables() 17 | 18 | user1 = User(name = 'user in db1') 19 | user2 = User(name = 'user in db2') 20 | db1.session.add_then_commit(user1) 21 | db2.session.add_then_commit(user2) 22 | 23 | print 'I am', db1.session.query(User).get(1).name 24 | print 'I am', db2.session.query(User).get(1).name -------------------------------------------------------------------------------- /quick_orm/examples/multiple_many_to_one.py: -------------------------------------------------------------------------------- 1 | from quick_orm.core import Database 2 | from sqlalchemy import Column, String, Text 3 | 4 | __metaclass__ = Database.DefaultMeta 5 | 6 | class User: 7 | name = Column(String(30)) 8 | 9 | @Database.many_to_one(User, ref_name = 'author', backref_name = 'articles_authored') 10 | @Database.many_to_one(User, ref_name = 'editor', backref_name = 'articles_edited') 11 | class Article: 12 | title = Column(String(80)) 13 | content = Column(Text) 14 | 15 | Database.register() 16 | 17 | if __name__ == '__main__': 18 | db = Database('sqlite://') 19 | db.create_tables() 20 | 21 | author = User(name = 'Tyler Long') 22 | editor = User(name = 'Peter Lau') 23 | article = Article(author = author, editor = editor, title = 'quick_orm is super quick and easy', 24 | content = 'quick_orm is super quick and easy. Believe it or not.') 25 | db.session.add_then_commit(article) 26 | 27 | article = db.session.query(Article).get(1) 28 | print 'Article:', article.title 29 | print 'Author:', article.author.name 30 | print 'Editor:', article.editor.name -------------------------------------------------------------------------------- /quick_orm/examples/one_to_one.py: -------------------------------------------------------------------------------- 1 | from quick_orm.core import Database 2 | from sqlalchemy import Column, String 3 | 4 | __metaclass__ = Database.DefaultMeta 5 | 6 | class User: 7 | name = Column(String(30)) 8 | 9 | @Database.one_to_one(User) 10 | class Contact: 11 | email = Column(String(70)) 12 | address = Column(String(70)) 13 | 14 | Database.register() 15 | 16 | if __name__ == '__main__': 17 | db = Database('sqlite://') 18 | db.create_tables() 19 | 20 | contact = Contact(email = 'quick.orm.feedback@gmail.com', address = 'Shenzhen, China') 21 | user = User(name = 'Tyler Long', contact = contact) 22 | db.session.add_then_commit(user) 23 | 24 | user = db.session.query(User).get(1) 25 | print 'User:', user.name 26 | print 'Email:', user.contact.email 27 | print 'Address:', user.contact.address -------------------------------------------------------------------------------- /quick_orm/examples/raw_sql.py: -------------------------------------------------------------------------------- 1 | from quick_orm.core import Database 2 | from sqlalchemy import Column, String 3 | 4 | __metaclass__ = Database.DefaultMeta 5 | 6 | class User: 7 | name = Column(String(70)) 8 | 9 | Database.register() 10 | 11 | if __name__ == '__main__': 12 | db = Database('sqlite://') 13 | db.create_tables() 14 | 15 | count = db.engine.execute('select count(name) from user').scalar() 16 | print 'There are {0} users in total'.format(count) -------------------------------------------------------------------------------- /quick_orm/examples/table_inheritance.py: -------------------------------------------------------------------------------- 1 | from quick_orm.core import Database 2 | from sqlalchemy import Column, String, Text 3 | 4 | __metaclass__ = Database.DefaultMeta 5 | 6 | class User: 7 | name = Column(String(70)) 8 | 9 | @Database.many_to_one(User) 10 | class Post: 11 | content = Column(Text) 12 | 13 | class Question(Post): 14 | title = Column(String(70)) 15 | 16 | @Database.many_to_one(Question) 17 | class Answer(Post): 18 | pass 19 | 20 | @Database.many_to_one(Post) 21 | class Comment(Post): 22 | pass 23 | 24 | @Database.many_to_many(Post) 25 | class Tag: 26 | name = Column(String(70)) 27 | 28 | Database.register() 29 | 30 | if __name__ == '__main__': 31 | db = Database('sqlite://') 32 | db.create_tables() 33 | 34 | user1 = User(name = 'Tyler Long') 35 | user2 = User(name = 'Peter Lau') 36 | 37 | tag1 = Tag(name = 'quick_orm') 38 | tag2 = Tag(name = 'nice') 39 | 40 | question = Question(user = user1, title = 'What is quick_orm?', content = 'What is quick_orm?', tags = [tag1, ]) 41 | question2 = Question(user = user1, title = 'Have you tried quick_orm?', content = 'Have you tried quick_orm?', tags = [tag1, ]) 42 | 43 | answer = Answer(user = user1, question = question, tags = [tag1, ], 44 | content = 'quick_orm is a Python ORM framework which enables you to get started in less than a minute!') 45 | 46 | comment1 = Comment(user = user2, content = 'good question', post = question) 47 | comment2 = Comment(user = user2, content = 'nice answer', post = answer, tags = [tag2, ]) 48 | 49 | db.session.add_all_then_commit([question, question2, answer, comment1, comment2, tag1, tag2, ]) 50 | 51 | question = db.session.query(Question).get(1) 52 | print 'tags for question "{0}": "{1}"'.format(question.title, ', '.join(tag.name for tag in question.tags)) 53 | print 'new comment for question:', question.comments.first().content 54 | print 'new comment for answer:', question.answers.first().comments.first().content 55 | 56 | user = db.session.query(User).filter_by(name = 'Peter Lau').one() 57 | print 'Peter Lau has posted {0} comments'.format(user.comments.count()) 58 | 59 | tag = db.session.query(Tag).filter_by(name = 'quick_orm').first() 60 | print '{0} questions are tagged "quick_orm"'.format(tag.questions.count()) 61 | -------------------------------------------------------------------------------- /quick_orm/extensions.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | quick_orm.extentions 4 | ~~~~~~~~~~~~~~~~~~~~ 5 | extends the core of quick_orm 6 | """ 7 | import inspect 8 | from types import MethodType 9 | from datetime import datetime 10 | 11 | 12 | class DatabaseExtension(object): 13 | """Extend the database""" 14 | @staticmethod 15 | def extend(Database): 16 | """Extend database""" 17 | def create_tables(self): 18 | """Create all tables""" 19 | Database.Base.metadata.create_all(bind = self.engine) 20 | Database.create_tables = create_tables 21 | 22 | def drop_tables(self): 23 | """Drop all tables""" 24 | Database.Base.metadata.drop_all(bind = self.engine) 25 | Database.drop_tables = drop_tables 26 | 27 | def load_data(self, data): 28 | """Load data into database from a module or an iterable of items 29 | All items must be derived from Database.Base, otherwise will be ignored. 30 | If the data paramter is a module, will try to load data from variables ending with 's' in that module. 31 | """ 32 | if not data: 33 | raise ValueError('data parameter should not be None or empty') 34 | #Iterable of items 35 | if hasattr(data, '__iter__') and all(isinstance(item, Database.Base) for item in data): 36 | self.session.add_all(data) 37 | self.session.commit() 38 | #Module 39 | elif inspect.ismodule(data): 40 | for items in (getattr(data, attr) for attr in dir(data) if attr.endswith('s')): 41 | self.load_data(items) 42 | Database.load_data = load_data 43 | 44 | return Database 45 | 46 | 47 | class SessionExtension(object): 48 | """Extend the database session""" 49 | @staticmethod 50 | def extend(session): 51 | """Extend session""" 52 | def add_then_commit(session, obj): 53 | session.add(obj) 54 | session.commit() 55 | session.add_then_commit = MethodType(add_then_commit, session) 56 | 57 | def add_all_then_commit(session, items): 58 | session.add_all(items) 59 | session.commit() 60 | session.add_all_then_commit = MethodType(add_all_then_commit, session) 61 | 62 | def delete_then_commit(session, obj): 63 | session.delete(obj) 64 | session.commit() 65 | session.delete_then_commit = MethodType(delete_then_commit, session) 66 | 67 | return session 68 | 69 | 70 | class ModelExtension(object): 71 | """Extend each model""" 72 | 73 | @staticmethod 74 | def before_insert_listener(mapper, connection, target): 75 | target.created_at = datetime.now() 76 | 77 | @staticmethod 78 | def before_update_listener(mapper, connection, target): 79 | target.updated_at = datetime.now() -------------------------------------------------------------------------------- /quick_orm/testsuite/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | quick_orm.testsuite 4 | ~~~~~~~~~~~~~~~~~~~ 5 | unit tests for quick_orm project 6 | """ 7 | import unittest 8 | from quick_orm.core import Database 9 | 10 | db = None 11 | db_strs = { 12 | 'postgresql': 'postgresql://postgres:123456@localhost/quick_orm', 13 | 'sqlite': 'sqlite:///quick_orm/testsuite/quick_orm.db', 14 | 'mysql': 'mysql://root:123456@localhost/quick_orm?charset=utf8', 15 | } 16 | 17 | def run_testsuite(db_name): 18 | import quick_orm.testsuite.fixtures as fixtures 19 | 20 | global db 21 | db_str = db_strs[db_name] 22 | db = Database(db_str) 23 | db.drop_tables() 24 | db.create_tables() 25 | db.load_data(fixtures) 26 | 27 | from quick_orm.testsuite.core import CoreTestCase 28 | test_suite = unittest.TestSuite() 29 | test_suite.addTest(unittest.makeSuite(CoreTestCase)) 30 | 31 | unittest.TextTestRunner(verbosity=1).run(test_suite) 32 | -------------------------------------------------------------------------------- /quick_orm/testsuite/core.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | quick_orm.testsuite.core 4 | ~~~~~~~~~~~~~~~~~~~~~~~~ 5 | test the core of quick_orm 6 | """ 7 | import unittest 8 | from toolkit_library.inspector import ModuleInspector 9 | import quick_orm.testsuite.models as models 10 | from quick_orm.testsuite import db 11 | 12 | exec(ModuleInspector(models).import_all_classes_statement()) 13 | 14 | class CoreTestCase(unittest.TestCase): 15 | """The default test case""" 16 | 17 | def tearDown(self): 18 | db.session.remove() 19 | 20 | def test_many_to_one(self): 21 | """Test many_to_one relationship""" 22 | user = db.session.query(User).filter_by(name = 'simon').first() 23 | assert user 24 | assert user.blog_entries.count() > 0 25 | assert user.blog_entries.first().user.name == 'simon' 26 | 27 | def test_many_to_many(self): 28 | """Test many_to_many relationship""" 29 | user = db.session.query(User).filter_by(name = 'simon').first() 30 | assert user 31 | assert user.groups.count() > 0 32 | assert user.groups.first().users.count() > 0 33 | assert any(u.name == 'simon' for u in user.groups.first().users) 34 | 35 | def test_many_to_one_cascade(self): 36 | """Test cascade in a many_to_one relationship. 37 | When record from the one side gets deleted, 38 | records from the many side are deleted automatically. 39 | Make sure no ignorant records are deleted. 40 | """ 41 | user_query = db.session.query(User) 42 | user_count = user_query.count() 43 | blog_query = db.session.query(BlogEntry) 44 | blog_count = blog_query.count() 45 | user = db.session.query(User).filter_by(name = 'peter').first() 46 | assert user 47 | user_blog_count = user.blog_entries.count() 48 | blog_entry = user.blog_entries.first() 49 | assert blog_entry 50 | db.session.delete_then_commit(user) 51 | blog_entry = db.session.query(BlogEntry).get(blog_entry.id) 52 | assert not blog_entry 53 | assert user_count - 1 == user_query.count() 54 | assert blog_count - user_blog_count == blog_query.count() 55 | 56 | def test_many_to_many_cascade(self): 57 | """Test cascade in a many_to_many relationship. 58 | when record from either side gets deleted, 59 | records from the middle table are deleted automatically, 60 | records from the other side retain. 61 | make sure no ignorant records are deleted. 62 | """ 63 | user_query = db.session.query(User) 64 | user_count = user_query.count() 65 | group_query = db.session.query(Group) 66 | group_count = group_query.count() 67 | middle_query_str = 'select count(*) from user_group' 68 | middle_count = db.engine.execute(middle_query_str).scalar() 69 | 70 | user = db.session.query(User).filter_by(name = 'tyler').first() 71 | assert user 72 | user_middle_count = db.engine.execute('select count(*) from user_group where user_id={0}'.format(user.id)).scalar() 73 | group =db.session.query(Group).filter_by(name = 'editor').first() 74 | assert group 75 | query_str = 'select count(*) from user_group where user_id={0} and group_id={1}'.format(user.id, group.id) 76 | assert db.engine.execute(query_str).scalar() == 1 77 | db.session.delete_then_commit(user) 78 | assert db.engine.execute(query_str).scalar() == 0 79 | group = db.session.query(Group).filter_by(name = 'editor').first() 80 | assert group 81 | 82 | assert user_count - 1 == user_query.count() 83 | assert group_count == group_query.count() 84 | assert middle_count - user_middle_count == db.engine.execute(middle_query_str).scalar() 85 | 86 | def test_table_inheritance(self): 87 | """Test table inheritance""" 88 | question_query = db.session.query(Question) 89 | question_count = question_query.count() 90 | assert question_count > 0 91 | question = db.session.query(Question).filter_by(title = 'question_title_1').first() 92 | assert question 93 | 94 | assert question.user 95 | assert any(question.title == q.title for q in question.user.questions) 96 | -------------------------------------------------------------------------------- /quick_orm/testsuite/fixtures.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | quick_orm.testsuite.fixtures 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | sample data for testing purpose 6 | """ 7 | from toolkit_library.inspector import ModuleInspector 8 | import quick_orm.testsuite.models 9 | 10 | exec(ModuleInspector(quick_orm.testsuite.models).import_all_classes_statement()) 11 | 12 | users = [] 13 | users.append(User(name = 'peter')) 14 | users.append(User(name = 'tyler')) 15 | users.append(User(name = 'simon')) 16 | users.append(User(name = 'jason')) 17 | users.append(User(name = 'justin')) 18 | 19 | groups = [] 20 | groups.append(Group(name = 'admin', users = [users[0],])) 21 | groups.append(Group(name = 'editor', users = [users[1], users[3], users[4], ])) 22 | groups.append(Group(name = 'user', users = [users[2],])) 23 | 24 | blog_entries = [] 25 | blog_entries.append(BlogEntry(title = 'blog_entry_title_1', content = 'blog_entry_content_1', user = users[0])) 26 | blog_entries.append(BlogEntry(title = 'blog_entry_title_2', content = 'blog_entry_content_2', user = users[1])) 27 | blog_entries.append(BlogEntry(title = 'blog_entry_title_3', content = 'blog_entry_content_3', user = users[2])) 28 | blog_entries.append(BlogEntry(title = 'blog_entry_title_4', content = 'blog_entry_content_4', user = users[2])) 29 | blog_entries.append(BlogEntry(title = 'blog_entry_title_5', content = 'blog_entry_content_5', user = users[3])) 30 | blog_entries.append(BlogEntry(title = 'blog_entry_title_6', content = 'blog_entry_content_6', user = users[3])) 31 | blog_entries.append(BlogEntry(title = 'blog_entry_title_7', content = 'blog_entry_content_7', user = users[4])) 32 | blog_entries.append(BlogEntry(title = 'blog_entry_title_8', content = 'blog_entry_content_8', user = users[4])) 33 | 34 | topics = [] 35 | topics.append(Topic(name = 'topic_name_1')) 36 | topics.append(Topic(name = 'topic_name_2')) 37 | topics.append(Topic(name = 'topic_name_3')) 38 | topics.append(Topic(name = 'topic_name_4')) 39 | topics.append(Topic(name = 'topic_name_5')) 40 | 41 | questions = [] 42 | questions.append(Question(title = 'question_title_1', content = 'question_content_1', topics = [topics[0], topics[1], ], user = users[4])) 43 | questions.append(Question(title = 'question_title_2', content = 'question_content_2', topics = [topics[1], ], user = users[4])) 44 | questions.append(Question(title = 'question_title_3', content = 'question_content_3', topics = [topics[2], ], user = users[4])) 45 | questions.append(Question(title = 'question_title_4', content = 'question_content_4', topics = [topics[3], ], user = users[4])) 46 | questions.append(Question(title = 'question_title_5', content = 'question_content_5', topics = [topics[4], ], user = users[4])) 47 | 48 | 49 | answers = [] 50 | answers.append(Answer(content = 'answer_content_1', user = users[0])) 51 | answers.append(Answer(content = 'answer_content_2', user = users[0])) 52 | answers.append(Answer(content = 'answer_content_3', user = users[0])) 53 | answers.append(Answer(content = 'answer_content_4', user = users[1])) 54 | answers.append(Answer(content = 'answer_content_5', user = users[2])) 55 | -------------------------------------------------------------------------------- /quick_orm/testsuite/models.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | quick_orm.testsuite.models 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | database models to be tested against 6 | """ 7 | from quick_orm.core import Database 8 | from sqlalchemy import Column, String, Text, DateTime, func 9 | 10 | 11 | __metaclass__ = Database.DefaultMeta 12 | 13 | 14 | @Database.many_to_many('Group') 15 | class User: 16 | name = Column(String(36), nullable = False, unique = True) 17 | 18 | 19 | class Group: 20 | name = Column(String(36), nullable = False, unique = True) 21 | 22 | 23 | @Database.many_to_one(User, backref_name = 'blog_entries') 24 | class BlogEntry: 25 | title = Column(String(64), nullable = False) 26 | content = Column(Text) 27 | 28 | 29 | class Topic: 30 | name = Column(String(64), nullable = False) 31 | 32 | 33 | @Database.many_to_one(User) 34 | class Post: 35 | content = Column(Text) 36 | 37 | 38 | @Database.many_to_many(Topic) 39 | class Question(Post): 40 | title = Column(String(64), nullable = False) 41 | 42 | class Answer(Post): 43 | pass 44 | 45 | 46 | Database.register() -------------------------------------------------------------------------------- /requires.txt: -------------------------------------------------------------------------------- 1 | SQLAlchemy==0.7.10 2 | toolkit_library>=0.4.0 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | import quick_orm 3 | from quick_orm.examples import examples_list, examples_dict 4 | 5 | def read_examples(): 6 | result = '' 7 | for example_file in examples_list: 8 | result = '{0}\r\n|\r\n\r\n{1} example\r\n{2}\r\n\r\n::\r\n\r\n {3}\r\n\r\n'.format(result, examples_dict[example_file], 9 | '-' * (len(examples_dict[example_file]) + 8), 10 | '\r\n '.join(open('quick_orm/examples/{0}.py'.format(example_file)).read().splitlines())) 11 | return result.rstrip() 12 | 13 | readme = requirements = None 14 | with open('README_template', 'r') as file: 15 | readme = file.read() 16 | readme = readme.replace('{{ examples }}', read_examples()) 17 | with open('requires.txt', 'r') as file: 18 | text = file.read().rstrip() 19 | requirements = text.splitlines() 20 | with open('quick_orm/core.py', 'r') as file: 21 | readme = readme.replace('{{ lines_count }}', str(len(file.read().splitlines()))) 22 | with open('README.rst', 'w') as file: 23 | file.write(readme) 24 | 25 | 26 | setup( 27 | name = quick_orm.__name__, 28 | version = quick_orm.__version__, 29 | url = 'https://github.com/tylerlong/quick_orm', 30 | license = 'BSD', 31 | author = quick_orm.__author__, 32 | author_email = 'tyler4long@gmail.com', 33 | description = 'Python ORM framework, quick, easy, simple yet powerful, based on sqlalchemy.', 34 | long_description = readme, 35 | packages = ['quick_orm', 'quick_orm.examples', ], 36 | install_requires = requirements, 37 | platforms = 'any', 38 | classifiers = [ 39 | 'Development Status :: 4 - Beta', 40 | 'Environment :: Web Environment', 41 | 'Intended Audience :: Developers', 42 | 'License :: OSI Approved :: BSD License', 43 | 'Operating System :: OS Independent', 44 | 'Programming Language :: Python', 45 | 'Topic :: Software Development :: Libraries :: Python Modules', 46 | ] 47 | ) --------------------------------------------------------------------------------