Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Stupid ORM Tricks

Stupid ORM Tricks

SFPython, November 2011

Jacob Kaplan-Moss

November 03, 2011
Tweet

More Decks by Jacob Kaplan-Moss

Other Decks in Technology

Transcript

  1. "[Django has] an ORM with a beautiful interface - for

    very basic things. But once you try to do the extraordinary with it… it's frustrating to say the least." — Adam Gomaa
  2. Hi, Internets! If you were here in person, you'd hear

    me say that most code in this talk is not production-worthy. But since you're not here, this'll have to do. The code presented here is untested, and uses internal, undocumented interfaces. At best, it's a hack; at worst, it'll bring your site down. It's not the kind of code you can just copy and paste! Please don't reuse code from this talk in production without considering it carefully! Only once you complete understand how it can fail — it will — should you consider using this code in your own projects. Thank you!
  3. from django.db import models class InternationalPhoneNumberField(models.Field): __metaclass__ = models.SubfieldBase description

    = "An international phone number." def __init__(self, *args, **kwargs): ... def to_python(self, value): ... def get_prep_value(self, value): ... def formfield(self, **kwargs): ... def get_internal_type(self): ...
  4. class InternationalPhoneNumberField(models.Field): ... def get_prep_value(self, value): if not isinstance(value, phonenumbers.PhoneNumber):

    value = self.to_python(value) if value: return utils.format_number(value) else: return None
  5. try: import south.modelsinspector rules = [ # Field classes this

    rule applies to [InternationalPhoneNumberField], # Positional arguments (none). [], # Keyword arguments. { 'max_length': ["max_length", {"default": 20}], 'default_region': [ "default_region", {"default": utils.guess_default_region()} ], }, ] south.modelsinspector.add_introspection_rules( [rules], ["^internationalphone\.models"] ) except ImportError: pass
  6. >>> class Foo(object): ... pass ... >>> f = Foo()

    >>> type(f) <class '__main__.Foo'> >>> type(Foo) <type 'type'> >>> isinstance(Foo, type) True
  7. >>> help(type) Help on class type in module __builtin__: class

    type(object) | type(object) -> the object's type | type(name, bases, dict) -> a new type
  8. >>> help(type) Help on class type in module __builtin__: class

    type(object) | type(object) -> the object's type | type(name, bases, dict) -> a new type
  9. >>> from adhocdb.models import DB >>> lightroom = DB(alias='lightroom', ...

    path='.../Lightroom 3 Catalog.lrcat') >>> lightroom.model_names() ['Adobe_AdditionalMetadata', 'Adobe_imageDevelopBeforeSettings', 'Adobe_imageDevelopSettings', 'Adobe_imageProperties', 'Adobe_images', ... ] >>> Image = lightroom.model('Adobe_images') >>> Image.objects.count() 4775
  10. from django import db from django.conf import settings from django.db

    import models class DB(object): def __init__(self, alias, path): self.alias = alias settings.DATABASES[self.alias] = { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': path, } if self.alias in db.connections._connections: del db.connections._connections[self.alias] self._model_cache = None
  11. from django import db from django.conf import settings from django.db

    import models class DB(object): ... def model_names(self): conn = db.connections[self.alias] introspection = conn.introspection return map(str, introspection.table_names())
  12. from django import db from django.conf import settings from django.db

    import models class DB(object): ... def model_for_table(self, table_name, **namespace): introspection = db.connections[self.alias].introspection cursor = db.connections[self.alias].cursor() namespace.update({ '__module__': __name__, 'Meta': type("Meta", (object,), {'db_table': table_name}), '_adhocdb_alias': self.alias, }) for row in introspection.get_table_description(cursor, table_name): rname, rtype, _, _, _, _, nullok = row try: classname = introspection.DATA_TYPES_REVERSE[rtype] fieldclass = getattr(models, classname) except (KeyError, AttributeError): fieldclass = models.TextField namespace[rname] = fieldclass(null=nullok) return type(table_name, (models.Model,), namespace)
  13. #  \d  wiki            Table  "public.wiki"

       Column    |    Type      |  Modifiers   -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐  name          |  text        |  not  null  version    |  integer  |  not  null  time          |  bigint    |    author      |  text        |    ipnr          |  text        |    text          |  text        |    comment    |  text        |    readonly  |  integer  |   Indexes:        "wiki_pk"  PRIMARY  KEY,  btree  (name,  version)        "wiki_time_idx"  btree  ("time")
  14. #  select  django_id,  author  from  wiki_django_view  limit  5;    

             django_id              |              author                 -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐  CMSAppsComparison.73    |  [email protected]  DevelopersForHire.694  |  knlg2011  Tutorials.60                    |  anonymous  DevelopersForHire.725  |  jwilk  SummerOfCode2011.25      |  ramiro
  15. "Django is a high-level Python Web framework that encourages rapid

    development and clean, pragmatic design."