├── MANIFEST.in ├── .gitignore ├── CHANGES.rst ├── setup.py ├── LICENSE ├── README.rst ├── django_orm_magic.py └── documentation.ipynb /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | include CHANGES.rst -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | *.sqlite 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | 22 | # Installer logs 23 | pip-log.txt 24 | 25 | # Unit test / coverage reports 26 | .coverage 27 | .tox 28 | nosetests.xml 29 | 30 | # Translations 31 | *.mo 32 | 33 | # Mr Developer 34 | .mr.developer.cfg 35 | .project 36 | .pydevproject 37 | -------------------------------------------------------------------------------- /CHANGES.rst: -------------------------------------------------------------------------------- 1 | 2 | Changelog 3 | ========= 4 | 5 | 0.3.1 / 2014-01-21 6 | ------------------ 7 | 8 | - Fix bug `#4 `_ 9 | (now install via pip works) 10 | 11 | 12 | 0.3 / 2013-12-14 13 | ---------------- 14 | 15 | - Added `%django_settings` magic 16 | - Added a tutorial notebook 17 | 18 | 0.2 / 2013-12-11 19 | ----------------- 20 | 21 | - Moved the code to github. 22 | - Packaged and registered in PyPi 23 | 24 | 25 | 0.1 / 2013-10-29 26 | ------------------- 27 | 28 | - First public release (as a gist_) 29 | 30 | .. _gist: https://gist.github.com/mgaitan/7207448 -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from setuptools import setup 3 | 4 | long_description = (open('README.rst').read() + '\n\n' + 5 | open('CHANGES.rst').read()) 6 | 7 | setup( 8 | name='django-orm-magic', 9 | version='0.3.1', 10 | description="An extension for IPython that help to define django's models in " 11 | "your interactive session.", 12 | long_description=long_description, 13 | author='Martin Gaitan', 14 | author_email='gaitan@gmail.com', 15 | url='https://github.com/mgaitan/django-orm-magic', 16 | license='BSD', 17 | keywords="ipython notebook django orm standalone", 18 | py_modules=['django_orm_magic'], 19 | install_requires=['ipython', 'django'], 20 | classifiers=[ 21 | 'Development Status :: 4 - Beta', 22 | 'Environment :: Web Environment', 23 | 'Framework :: IPython', 24 | 'Framework :: Django', 25 | 'License :: OSI Approved :: BSD License', 26 | 'Operating System :: OS Independent', 27 | 'Programming Language :: Python', 28 | 'Topic :: Database', 29 | 'Topic :: Scientific/Engineering :: Information Analysis' 30 | ], 31 | ) 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Martín Gaitán 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | * Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ================= 2 | Django ORM Magic 3 | ================= 4 | 5 | .. image:: https://img.shields.io/pypi/v/django-orm-magic.svg 6 | :target: https://pypi.python.org/pypi/django-orm-magic 7 | :alt: Latest PyPI version 8 | 9 | .. image:: https://img.shields.io/pypi/dm/django-orm-magic.svg 10 | :target: https://pypi.python.org/pypi/django-orm-magic 11 | :alt: Number of PyPI downloads 12 | 13 | 14 | :author: Martín Gaitán 15 | :homepage: https://github.com/mgaitan/django-orm-magic 16 | :documentation: see `this notebook`__ 17 | 18 | __ documentation_ 19 | .. _documentation: http://nbviewer.ipython.org/urls/raw.github.com/mgaitan/django-orm-magic/master/documentation.ipynb 20 | 21 | 22 | Define your django models in an IPython cell and use them on the fly. 23 | Let the magic do the boring part. 24 | 25 | Django ORM isn't conceived to be used standalone. Even for a trivial case, you need to configure a database, create an app, etc. This magic handle that automatically, and then import every model to your interactive session. 26 | 27 | 28 | Install 29 | ======= 30 | 31 | You can install or upgrade via pip 32 | 33 | pip install -U django-orm-magic 34 | 35 | or directly from the repository using the `%install_ext` magic command:: 36 | 37 | In[1]: %install_ext https://raw.github.com/mgaitan/django-orm-magic/master/django_orm_magic.py 38 | 39 | 40 | Basic usage 41 | =========== 42 | 43 | Once it's installed, you can load it with ``%load_ext django_orm_magic``. Then define your models in a cell started with the cell magic ``%%django_orm``. 44 | For example:: 45 | 46 | In[2]: %load_ext django_orm_magic 47 | 48 | 49 | In[3]: %%django_orm 50 | 51 | from django.db import models 52 | 53 | class Poll(models.Model): 54 | question = models.CharField(max_length=200) 55 | pub_date = models.DateTimeField('date published') 56 | 57 | class Choice(models.Model): 58 | poll = models.ForeignKey(Poll) 59 | choice_text = models.CharField(max_length=200) 60 | votes = models.IntegerField(default=0) 61 | 62 | 63 | And it's done. Every model is synced in a sqlite database named ``db.sqlite`` in your current path and imported automatically:: 64 | 65 | 66 | In[4]: Poll.objects.all() 67 | Out[4]: [] 68 | 69 | In[5]: from django.utils import timezone 70 | p = Poll(question="What's new?", pub_date=timezone.now()) 71 | p.save() 72 | 73 | 74 | See the documentation_ for further details. 75 | 76 | See here_ for another example 77 | 78 | .. _here: http://nbviewer.ipython.org/gist/mgaitan/7224431#modelando-resultados.gob.ar-en-una-base-de-datos 79 | -------------------------------------------------------------------------------- /django_orm_magic.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os.path 3 | import tempfile 4 | 5 | import django 6 | from django.conf import settings 7 | from django.core.management import call_command 8 | from django.utils.crypto import get_random_string 9 | from IPython.core import magic_arguments 10 | from IPython.core.magic import Magics, magics_class, cell_magic, line_cell_magic 11 | from django.utils import six 12 | 13 | if six.PY3: 14 | from importlib import reload 15 | 16 | 17 | chars = 'abcdefghijklmnopqrstuvwxyz0123456789' 18 | 19 | DEFAULT_SETTINGS = {'SECRET_KEY': get_random_string(50, chars), 20 | 'DATABASES': { 21 | 'default': { 22 | 'ENGINE': 'django.db.backends.sqlite3', 23 | 'NAME': 'db.sqlite' #':memory:' 24 | } 25 | }, 26 | 'INSTALLED_APPS': ("orm_magic",) 27 | } 28 | 29 | 30 | @magics_class 31 | class DjangoOrmMagics(Magics): 32 | 33 | def _import_all(self, module, verbosity=0): 34 | imported = [] 35 | for k, v in module.__dict__.items(): 36 | if not k.startswith('__'): 37 | self.shell.push({k: v}) 38 | imported.append(k) 39 | if verbosity > 0 and imported: 40 | print("\nOk. The following django models" 41 | "are ready to use: %s" % ", ".join(imported)) 42 | 43 | @cell_magic 44 | def django_orm(self, line, cell): 45 | j = os.path.join 46 | if not settings.configured: 47 | try: 48 | del self.shell.db['orm_magic_path'] 49 | except KeyError: 50 | pass 51 | settings.configure(**DEFAULT_SETTINGS) 52 | temp_project_path = self.shell.db['orm_magic_path'] = tempfile.mkdtemp() 53 | sys.path.append(temp_project_path) 54 | tmp, temp_project = os.path.split(temp_project_path) 55 | 56 | temp_dir = j(temp_project_path, 'orm_magic') 57 | os.makedirs(temp_dir) 58 | open(j(temp_project_path, '__init__.py'), 'a').close() 59 | open(j(temp_dir, '__init__.py'), 'a').close() 60 | 61 | else: 62 | temp_project_path = self.shell.db['orm_magic_path'] 63 | temp_dir = j(temp_project_path, 'orm_magic') 64 | 65 | module_path = j(temp_dir, 'models.py') 66 | with open(module_path, 'w') as fh: 67 | fh.write('\n' + cell) 68 | 69 | django.setup() 70 | call_command('makemigrations', 'orm_magic', verbosity=0, interactive=False) 71 | call_command('migrate', 'orm_magic', verbosity=0, interactive=False) 72 | 73 | try: 74 | reload(orm_magic_models) 75 | except UnboundLocalError: 76 | from orm_magic import models as orm_magic_models 77 | self._import_all(orm_magic_models) 78 | sys.path.pop() 79 | 80 | 81 | def load_ipython_extension(ip): 82 | """Load the extension in IPython.""" 83 | ip.register_magics(DjangoOrmMagics) 84 | -------------------------------------------------------------------------------- /documentation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "name": "" 4 | }, 5 | "nbformat": 3, 6 | "nbformat_minor": 0, 7 | "worksheets": [ 8 | { 9 | "cells": [ 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "#Django ORM Magic\n", 15 | "\n", 16 | "django-orm-magic is an [IPython](http://ipython.org) extension that help to use the django ORM in your interactive session. \n", 17 | "\n", 18 | "Define your django models in an IPython cell and use them on the fly. Let the magic do the boring part.\n", 19 | "\n", 20 | "##Introduction\n", 21 | "\n", 22 | "Django ORM isn't not conceived to be used standalone. Even for a trivial use case, you need to configure a database, create an app, setup an eviroment variable, etc. This magic handle that steps automatically and then import every model ready to use in your interactive session.\n", 23 | "\n", 24 | "\n", 25 | "## How it works\n", 26 | "\n", 27 | "It adds a `%%django_orm` cell magic that handle all the setup needed to use django models on the fly\n", 28 | "\n", 29 | "\n", 30 | "* Author: Mart\u00edn Gait\u00e1n ( gaitan AT gmail.com ) \n", 31 | "* Homepage: https://github.com/mgaitan/django-orm-magic\n", 32 | "* License: BSD\n", 33 | "\n", 34 | "Feedback, report of issues and pull requests are welcome!" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "metadata": {}, 40 | "source": [ 41 | "## Install or upgrade\n", 42 | "\n", 43 | "You can install or upgrade via `pip`\n", 44 | "\n", 45 | " pip install -U django-orm-magic\n", 46 | "\n", 47 | "or directly from the repository using `%install_ext` magic command." 48 | ] 49 | }, 50 | { 51 | "cell_type": "code", 52 | "collapsed": false, 53 | "input": [ 54 | "# https://raw.github.com/mgaitan/django-orm-magic/master/django_orm_magic.py\n", 55 | "%install_ext django_orm_magic.py" 56 | ], 57 | "language": "python", 58 | "metadata": {}, 59 | "outputs": [ 60 | { 61 | "output_type": "stream", 62 | "stream": "stdout", 63 | "text": [ 64 | "Installed django_orm_magic.py. To use it, type:\n", 65 | " %load_ext django_orm_magic\n" 66 | ] 67 | } 68 | ], 69 | "prompt_number": 1 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "## Load \n", 76 | "\n", 77 | "Then you are ready to load the magic " 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "collapsed": false, 83 | "input": [ 84 | "%load_ext django_orm_magic" 85 | ], 86 | "language": "python", 87 | "metadata": {}, 88 | "outputs": [], 89 | "prompt_number": 2 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "metadata": {}, 94 | "source": [ 95 | "To load it each time IPython starts, list it in your configuration file:\n", 96 | "\n", 97 | " c.InteractiveShellApp.extensions = [\n", 98 | " 'django_orm_magic'\n", 99 | " ]" 100 | ] 101 | }, 102 | { 103 | "cell_type": "markdown", 104 | "metadata": {}, 105 | "source": [ 106 | "## Basic usage\n", 107 | "\n", 108 | "You are ready to define your models starting with the cell magic ``%%django_orm``\n" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "collapsed": false, 114 | "input": [ 115 | "%%django_orm\n", 116 | "\n", 117 | "from django.db import models\n", 118 | "\n", 119 | "class Poll(models.Model):\n", 120 | " question = models.CharField(max_length=200)\n", 121 | " pub_date = models.DateTimeField('date published')\n", 122 | "\n", 123 | "class Choice(models.Model):\n", 124 | " poll = models.ForeignKey(Poll)\n", 125 | " choice_text = models.CharField(max_length=200)\n", 126 | " votes = models.IntegerField(default=0)" 127 | ], 128 | "language": "python", 129 | "metadata": {}, 130 | "outputs": [], 131 | "prompt_number": 3 132 | }, 133 | { 134 | "cell_type": "markdown", 135 | "metadata": {}, 136 | "source": [ 137 | "And it's done. By default, every model is synced in a sqlite database named ``db.sqlite`` in your current path and imported automatically" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "collapsed": false, 143 | "input": [ 144 | "Poll.objects.all()" 145 | ], 146 | "language": "python", 147 | "metadata": {}, 148 | "outputs": [ 149 | { 150 | "metadata": {}, 151 | "output_type": "pyout", 152 | "prompt_number": 4, 153 | "text": [ 154 | "[]" 155 | ] 156 | } 157 | ], 158 | "prompt_number": 4 159 | }, 160 | { 161 | "cell_type": "code", 162 | "collapsed": false, 163 | "input": [ 164 | "from django.utils import timezone\n", 165 | "p = Poll(question=\"What's new?\", pub_date=timezone.now())\n", 166 | "p.save()" 167 | ], 168 | "language": "python", 169 | "metadata": {}, 170 | "outputs": [], 171 | "prompt_number": 5 172 | }, 173 | { 174 | "cell_type": "code", 175 | "collapsed": false, 176 | "input": [ 177 | "Poll.objects.all()" 178 | ], 179 | "language": "python", 180 | "metadata": {}, 181 | "outputs": [ 182 | { 183 | "metadata": {}, 184 | "output_type": "pyout", 185 | "prompt_number": 6, 186 | "text": [ 187 | "[]" 188 | ] 189 | } 190 | ], 191 | "prompt_number": 6 192 | }, 193 | { 194 | "cell_type": "code", 195 | "collapsed": false, 196 | "input": [ 197 | "_.delete()" 198 | ], 199 | "language": "python", 200 | "metadata": {}, 201 | "outputs": [], 202 | "prompt_number": 7 203 | }, 204 | { 205 | "cell_type": "markdown", 206 | "metadata": {}, 207 | "source": [ 208 | "## Custom settings\n", 209 | "\n", 210 | "If you want to connect to another database, add third django apps or so, you can customize the ``settings.py`` file using the magic ``%django_settings``. \n", 211 | "\n", 212 | "``%django_settings`` could be used in three ways: \n", 213 | "\n", 214 | "* ``%django_settings`` alone, load the current settings as a template in a new cell\n", 215 | "* ``%django_settings --default`` delete any customization and return to the default settings\n", 216 | "* ``%%django_settings`` take the content of the cell as the new settings. \n", 217 | "\n" 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "collapsed": false, 223 | "input": [ 224 | "%django_settings" 225 | ], 226 | "language": "python", 227 | "metadata": {}, 228 | "outputs": [], 229 | "prompt_number": 3 230 | }, 231 | { 232 | "cell_type": "code", 233 | "collapsed": false, 234 | "input": [ 235 | "%%django_settings\n", 236 | "\n", 237 | "DATABASES = {\n", 238 | " 'default': {\n", 239 | " 'ENGINE': 'django.db.backends.sqlite3',\n", 240 | " 'NAME': 'db.sqlite' #':memory:'\n", 241 | " }\n", 242 | " }\n", 243 | "INSTALLED_APPS = (\"orm_magic\",)\n" 244 | ], 245 | "language": "python", 246 | "metadata": {}, 247 | "outputs": [] 248 | }, 249 | { 250 | "cell_type": "markdown", 251 | "metadata": {}, 252 | "source": [ 253 | "Then you can edit the cell above with your own settings. \n", 254 | "\n", 255 | "\n", 256 | "
\n", 257 | " Attention! Don't remove `orm_magic` from `INSTALLED_APPS`. This is the app that will contains your own models.\n", 258 | "
" 259 | ] 260 | }, 261 | { 262 | "cell_type": "code", 263 | "collapsed": false, 264 | "input": [ 265 | "%%django_settings\n", 266 | "\n", 267 | "DATABASES = {\n", 268 | " 'default': {\n", 269 | " 'ENGINE': 'django.db.backends.sqlite3',\n", 270 | " 'NAME': ':memory:'\n", 271 | " }\n", 272 | " }\n", 273 | "INSTALLED_APPS = (\"django.contrib.auth\", \"django.contrib.contenttypes\", \"orm_magic\",)" 274 | ], 275 | "language": "python", 276 | "metadata": {}, 277 | "outputs": [ 278 | { 279 | "output_type": "stream", 280 | "stream": "stdout", 281 | "text": [ 282 | "Settings for %%django_orm configured succesfully\n" 283 | ] 284 | } 285 | ], 286 | "prompt_number": 4 287 | }, 288 | { 289 | "cell_type": "code", 290 | "collapsed": false, 291 | "input": [ 292 | "%%django_orm\n", 293 | "\n", 294 | "from django.db import models\n", 295 | "from django.contrib.auth.models import User\n", 296 | "\n", 297 | "class Poll2(models.Model):\n", 298 | " user = models.ForeignKey(User)\n", 299 | " question = models.CharField(max_length=200)" 300 | ], 301 | "language": "python", 302 | "metadata": {}, 303 | "outputs": [], 304 | "prompt_number": 5 305 | }, 306 | { 307 | "cell_type": "code", 308 | "collapsed": false, 309 | "input": [ 310 | "from django.contrib.auth.models import User # TO DO: load any third party model used automatically" 311 | ], 312 | "language": "python", 313 | "metadata": {}, 314 | "outputs": [], 315 | "prompt_number": 6 316 | }, 317 | { 318 | "cell_type": "code", 319 | "collapsed": false, 320 | "input": [ 321 | "User.objects.create(username='mgaitan')" 322 | ], 323 | "language": "python", 324 | "metadata": {}, 325 | "outputs": [ 326 | { 327 | "metadata": {}, 328 | "output_type": "pyout", 329 | "prompt_number": 7, 330 | "text": [ 331 | "" 332 | ] 333 | } 334 | ], 335 | "prompt_number": 7 336 | }, 337 | { 338 | "cell_type": "markdown", 339 | "metadata": {}, 340 | "source": [ 341 | "To restore the default settings" 342 | ] 343 | }, 344 | { 345 | "cell_type": "code", 346 | "collapsed": false, 347 | "input": [ 348 | "%django_settings --default" 349 | ], 350 | "language": "python", 351 | "metadata": {}, 352 | "outputs": [ 353 | { 354 | "output_type": "stream", 355 | "stream": "stdout", 356 | "text": [ 357 | "Deleted custom settings. Back to default for %%django_orm\n" 358 | ] 359 | } 360 | ], 361 | "prompt_number": 8 362 | }, 363 | { 364 | "cell_type": "code", 365 | "collapsed": false, 366 | "input": [ 367 | "%django_settings" 368 | ], 369 | "language": "python", 370 | "metadata": {}, 371 | "outputs": [], 372 | "prompt_number": 9 373 | }, 374 | { 375 | "cell_type": "code", 376 | "collapsed": false, 377 | "input": [ 378 | "%%django_settings\n", 379 | "\n", 380 | "DATABASES = {\n", 381 | " 'default': {\n", 382 | " 'ENGINE': 'django.db.backends.sqlite3',\n", 383 | " 'NAME': 'db.sqlite' #':memory:'\n", 384 | " }\n", 385 | " }\n", 386 | "INSTALLED_APPS = (\"orm_magic\",)\n" 387 | ], 388 | "language": "python", 389 | "metadata": {}, 390 | "outputs": [] 391 | }, 392 | { 393 | "cell_type": "code", 394 | "collapsed": false, 395 | "input": [], 396 | "language": "python", 397 | "metadata": {}, 398 | "outputs": [] 399 | } 400 | ], 401 | "metadata": {} 402 | } 403 | ] 404 | } --------------------------------------------------------------------------------