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

meta programing with django orm

Avatar for tim tim
May 30, 2019

meta programing with django orm

Avatar for tim

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]