Issue: Make common bulk operations on models and tables easier to do and harder to screw up
Author: tmoertel
State: closed
Created: 2012-05-21 00:06:42

In this branch, I've made a few small additions to peewee to make some common model and table operations easy, automatic, and hard to screw up:
- finding all your models; and
- creating/dropping their tables.

Instead of having to maintain boilerplate code to do that job, now you can just let peewee take care of it:

```
models = peewee.discover_models(exclude=[BaseModel])
peewee.create_model_tables(models, fail_silently=True)
```

That's it. Peewee will automatically gather your models, sort them topologically, and create their tables for you (in the right order).

For a real-world example of the kind of code savings this leads to, see
[how it streamlines peewee's own BaseModelTestCase](https://github.com/tmoertel/peewee/commit/753d599e90e7cb99ce05fc8b904a00bb0b683184#L1R236).

Cheers,
Tom


Wow, this is pretty interesting stuff.  I'm a little bit grossed out by inspecting stack frames, I wonder if it might make sense to use the model metaclass to track subclasses or something explicit even.  Not sure about merging yet, i've been playing with a branch features/extras where i've added a support for "signals" a-la django, and this might be a good thing to add there.  Perhaps a module named 'discover.py' and a model subclass that can track subclasses somehow, then the utilities can live there.


Thanks for the feedback.

Yeah, the part of the code I'm least happy with is the stack-inspection stuff. Alternatives:
1. Always require the caller to supply a module to scan, eliminating the need to detect the caller's module.
2. Change the interface to take not a module but any collection of objects, which is then filtered to return only the models within it. That way, you can find the models in the current module by passing `globals()` and in any other module by passing `vars(the_other_module)`:
   
   models1 = find_models(globals().items())  # current module
   models2 = find_models(vars(some_other_module).items())

Of course, these API designs are based on the belief that people tend to organize their models within modules. If it turns out that something other than modules makes a more natural unit of organization, I probably ought to make the discovery API ask for that thing instead. 

And maybe _base model_ is that more natural thing. If so, I can modify the discovery logic to take a base model:

```
models = discover_submodels(BaseModel)
```

If peewee already tracked the subclasses derived from a base model, I could use that knowledge for the discovery process. For now, however, I could just scan the base model's module for submodels. The discovery wouldn't cross module boundaries, but maybe it doesn't need to.

What do you think about these alternative approaches?

In any case, I put the convenience methods for creating/dropping tables into separate commits so that they can be used independently of the discovery logic. Do you have any interest in those methods?


I thought a bit more about what you wrote. Your idea of finding all the models derived from a base model seems pretty good, so I whipped up an implementation:`find_submodels`. I also renamed the module-based discovery method to `find_models_in_module` for parity and got rid of the stack-inspection logic.


Any reason not to use `__subclasses__` and call it recursively?


Nope, `__subclasses__` works just great. See most-recent commit for a revised `find_submodels` that uses it.

I've made the exclusion semantics such that if you exclude a model, its descendants are also excluded. I'm not sure about this choice, however. I suspect that some people create base models in the middle of their model hierarchies for sharing purposes.


I've added a generic function to find subclasses recursively: e6b199074023237d9c1bdeef5bb2b13d3b47fd13

I've added your model sorting function: a43911fee25a15e6dd47f

And lastly, the create/drop many: f8482d0728bcc6e

Thanks for suggesting these improvements!


You're welcome! Thanks for peewee :-)