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

meta programing with django orm

tim
May 30, 2019

meta programing with django orm

tim

May 30, 2019
Tweet

More Decks by tim

Other Decks in Programming

Transcript

  1. ⽂字 OUTLINE ▸ When/Why? ▸ The flow of object/class creation

    ▸ Some Example ▸ Magic behind Django ORM ▸ Conclusion
  2. WHAT AND WHY ▸ Generate code on the fly. ▸

    Meta programing, decorator.. ▸ 90% time you don’t need it ▸ DRY. Reduce a lot of repeat
  3. ⽂字 ▸ type return information from __class__ ▸ __bases__ tells

    you the class’s base classes ▸ type/__class__ tells you what makes you. ▸ type/__class__ is the factory make you. ▸ You can change a class’s type by __metaclass__ attribute
  4. ⽂字 >>> list <type ‘list'> >>> list.__class__ <type 'type'> >>>

    list.__bases__ (<type ‘object'>,) >>> tuple.__class__, tuple.__bases__ (<type 'type'>, (<type ‘object'>,)) >>> dict.__class__, dict.__bases__ (<type 'type'>, (<type ‘object’>,)) >>> mylist = [1,2,3] >>> mylist.__class__ <type 'list'>
  5. HOW CLASS CAN BE MODIFIED class Dog(object): def walk(self): print("walk")

    def play(self): print("play") dog = Dog() dog.walk() # walk dog.play() # play How To Bark every action?
  6. DECORATOR? def bark(wrapped): def do_wrap(*args, **kwargs): print("bark") wrapped(*args, **kwargs) return

    do_wrap class Dog(object): @bark def walk(self): print("walk") @bark def play(self): print("play") dog = Dog() dog.walk() # bark walk dog.play() # bark play Bark repeat every where…. Can We remove it?
  7. ⽂字 class BarkingMeta(type): def __new__(mcs, name, bases, attributes): for name,

    attribute in attributes.items(): if not name.startswith('_'): attributes[name] = bark(attribute) return super(BarkingMeta, mcs).__new__(mcs, name, bases, attributes) class Dog(object): __metaclass__ = BarkingMeta def walk(self): print("walk") def play(self): print("play") dog = Dog() dog.walk() # bark walk dog.play() # bark play
  8. COMMON CLASS class Book(object): value = int(10) >>> type(Book.value) <type

    ‘int'> >>> a = Book() >>> type(a.value) <type ‘int'>
  9. DJANGO MODEL class Book(models.Model): price = models.IntegerField() In [4]: type(models.IntegerField())

    Out[4]: django.db.models.fields.IntegerField In [5]: type(Book.price) Out[5]: django.db.models.query_utils.DeferredAttribute In [6]: b = Book() In [7]: type(b.price) Out[7]: NoneType In [1]: Book.DoesNotExist Out[1]: field_experiment.models.DoesNotExist In [2]: Book.MultipleObjectsReturned Out[2]: field_experiment.models.MultipleObjectsReturned
  10. CODE class Model(six.with_metaclass(ModelBase)): def __init__(self, *args, **kwargs): pass class ModelBase(type):

    """ Metaclass for all models. """ def __new__(cls, name, bases, attrs): super_new = super(ModelBase, cls).__new__ . . .
  11. CODE IN META CLASS NEW module = attrs.pop('__module__') new_attrs =

    {'__module__': module} classcell = attrs.pop('__classcell__', None) if classcell is not None: new_attrs['__classcell__'] = classcell new_class = super_new(cls, name, bases, new_attrs) attr_meta = attrs.pop('Meta', None) abstract = getattr(attr_meta, 'abstract', False) if not attr_meta: meta = getattr(new_class, 'Meta', None) else: meta = attr_meta base_meta = getattr(new_class, '_meta', None)
  12. IN MODEL BASE NEW new_class.add_to_class('_meta', Options(meta, app_label)) if not abstract:

    new_class.add_to_class( 'DoesNotExist', subclass_exception( str('DoesNotExist'), tuple( x.DoesNotExist for x in parents if hasattr(x, '_meta') and not x._meta.abstract ) or (ObjectDoesNotExist,), module, attached_to=new_class)) new_class.add_to_class( 'MultipleObjectsReturned', subclass_exception( str('MultipleObjectsReturned'), tuple( x.MultipleObjectsReturned for x in parents if hasattr(x, '_meta') and not x._meta.abstrac ) or (MultipleObjectsReturned,), module, attached_to=new_class)) if base_meta and not base_meta.abstract: # Non-abstract child classes inherit some attributes from their # non-abstract parent (unless an ABC comes before it in the # method resolution order). if not hasattr(meta, 'ordering'): new_class._meta.ordering = base_meta.ordering if not hasattr(meta, 'get_latest_by'): new_class._meta.get_latest_by = base_meta.get_latest_by
  13. ⽂字 def subclass_exception(name, parents, module, attached_to=None): """ Create exception subclass.

    Used by ModelBase below. If 'attached_to' is supplied, the exception will be created in a way that allows it to be pickled, assuming the returned exception class will be added as an attribute to the 'attached_to' class. """ class_dict = {'__module__': module} if attached_to is not None: def __reduce__(self): # Exceptions are special - they've got state that isn't # in self.__dict__. We assume it is all in self.args. return (unpickle_inner_exception, (attached_to, name), self.args) def __setstate__(self, args): self.args = args class_dict['__reduce__'] = __reduce__ class_dict['__setstate__'] = __setstate__ return type(name, parents, class_dict)
  14. IN MODEL BASE NEW def add_to_class(cls, name, value): # We

    should call the contribute_to_class method only if it's bound if not inspect.isclass(value) and hasattr(value, 'contribute_to_class'): value.contribute_to_class(cls, name) else: setattr(cls, name, value)
  15. IN MODEL BASE NEW # Add all attributes to the

    class. for obj_name, obj in attrs.items(): new_class.add_to_class(obj_name, obj) def add_to_class(cls, name, value): # We should call the contribute_to_class method only if it's bound if not inspect.isclass(value) and hasattr(value, 'contribute_to_class'): value.contribute_to_class(cls, name) else: setattr(cls, name, value)
  16. IN FIELD def contribute_to_class(self, cls, name, private_only=False,): """ Register the

    field with the model class it belongs to. """ self.set_attributes_from_name(name) self.model = cls cls._meta.add_field(self, private=True) else: cls._meta.add_field(self) . setattr(cls, self.attname, DeferredAttribute(self.attname, cls)) # defered
  17. DEFERREDATTRIBUTE class DeferredAttribute(object): """ A wrapper for a deferred-loading field.

    When the value is read from this object the first time, the query is executed. """ def __init__(self, field_name, model): self.field_name = field_name def __get__(self, instance, cls=None): """ Retrieves and caches the value from the datastore on the first lookup. Returns the cached value. """ if instance is None: return self data = instance.__dict__ if data.get(self.field_name, self) is self: # Let's see if the field is part of the parent chain. If so we # might be able to reuse the already loaded value. Refs #18343. val = self._check_parent_chain(instance, self.field_name) if val is None: instance.refresh_from_db(fields=[self.field_name]) val = getattr(instance, self.field_name) data[self.field_name] = val return data[self.field_name]