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

I never Meta model I didn't like

I never Meta model I didn't like

For the 2014 Google Summer of Code, Daniel Pyrathon formalized one of the pieces of Django that was most used unstable APIs in Django: the Meta interface. This code is now part Django 1.8. In this talk, Russell Keith-Magee will take you for a walk around the facilities provided by the Meta interface, and show how you can use those interfaces to make any data store quack like a Django-shaped duck.

Russell Keith-Magee

September 07, 2015
Tweet

More Decks by Russell Keith-Magee

Other Decks in Programming

Transcript

  1. Metaprogramming Python class class Point Point: def def __init__ __init__(self,

    x, y): self.x = x self.y = y class class Point3D Point3D(Point): def def __init__ __init__(self, x, y, z): super().__init__(x, y) self.z = z >>> p1 = Point(3, 4) >>> p2 = Point3D(5, 6, 7)
  2. Metaprogramming Python def def output_point output_point(p): print("x = %s" %

    p.x) print("y = %s" % p.y) def def output_point3D output_point3D(p): output_point(p) print("z = %s" % p.z)
  3. Metaprogramming Python >>> output_point(p1) x = 3 y = 4

    >>> output_point3D(p2) x = 5 y = 6 z = 7
  4. Metaprogramming Python def def output_point2 output_point2(p): print("x = %s" %

    p.x) print("y = %s" % p.y) if if isinstance(p, Point3d): print("z = %s" % p.z)
  5. Metaprogramming Python >>> output_point2(p1) x = 3 y = 4

    >>> output_point2(p2) x = 5 y = 6 z = 7
  6. Metaprogramming Python def def output_point3 output_point3(p): print("x = %s" %

    p.x) print("y = %s" % p.y) if if hasattr(p, 'z'): print("z = %s" % p.z)
  7. Metaprogramming Python def def output_point4 output_point4(p): for for attr in

    in sorted(p.__dict__): print('%s = %s' % ( attr, getattr(p, attr) ))
  8. Metaprogramming Python def def output_point4 output_point4(p): for for attr in

    in sorted(p.__dict__): print('%s = %s' % ( attr, getattr(p, attr) ))
  9. The Old Meta API meta = MyObject._meta meta.get_field(name) meta.get_field_by_name(name) meta.get_fields_with_model()

    meta.get_concrete_fields_with_model() meta.get_m2m_with_model() meta.get_all_related_objects() meta.get_all_related_objects_with_model() meta.get_all_related_many_to_many_objects() meta.get_all_related_m2m_objects_with_model() meta.get_all_field_names()
  10. Merging records class class Customer Customer(Model): name = CharField(max_length=100) ...

    class class WorkOrder WorkOrder(Model): customer = ForeignKey(Customer) order_id = CharField(max_length=100) ...
  11. Merging records class class Invoice Invoice(Model): customer = ForeignKey(Customer) invoice_number

    = CharField(max_length=100) ... class class Payment Payment(Model): customer = ForeignKey(Customer) received = DateField() ...
  12. Metaprograming merge def def merge_customer merge_customer(orig, dup): for for f

    in in orig._meta.get_fields(): if if f.one_to_many and and f.auto_created: getattr( dup, f.get_accessor_name() ).update(customer=orig) dup.delete()
  13. Metaprograming merge def def merge_anything merge_anything(orig, dup): for for f

    in in orig._meta.get_fields(): if if f.one_to_many and and f.auto_created: getattr( dup, f.get_accessor_name() ).update(**{ f.field.name: orig }) dup.delete()
  14. Can I get a Django form for my model? and

    Can I query and edit my data in Django's Admin?
  15. Managers class class GmailManager GmailManager(object): def def __init__ __init__(self, model):

    storage = Storage(settings.CREDENTIALS_PATH) self.creds = storage.get() self.model = model ...
  16. Managers class class ThreadManager ThreadManager(GmailManager): def def get_queryset get_queryset(self): ...

    return return ThreadQuerySet(self.creds, ...) class class MessageManager MessageManager(GmailManager): def def get_queryset get_queryset(self): ... return return MessageQuerySet(self.creds, ...)
  17. Queries and QuerySets class class GmailQuery GmailQuery(object): ... class class

    GmailQuerySet GmailQuerySet(list): def def __init__ __init__(self, *args, **kwargs): self.model = kwargs.pop('model') self.creds = kwargs.pop('creds') super().__init__(*args, **kwargs) self.query = GmailQuery()
  18. Queries and QuerySets class class ThreadQuerySet ThreadQuerySet(GmailQuerySet): def def get

    get(self, *args, **kwargs): thread_id = kwargs['id'] thread = get_thread_by_id( self.creds, thread_id ) ... return return thread
  19. Queries and QuerySets class class MessageQuerySet MessageQuerySet(GmailQuerySet): def def get

    get(self, *args, **kwargs): ... def def filter filter(self, *args, **kwargs): ... return return MessageQuerySet( model=self.model, credentials=self.credentials, selected_thread=selected_thread ) def def count count(self, *args, **kwargs): ... ...
  20. Meta class class GmailOptions GmailOptions(Options): ... def def _get_fields _get_fields(self,

    reverse=True, forward=True): ... def def get_field get_field(self, field_name): ... class class ThreadOptions ThreadOptions(GmailOptions): ... class class MessageOptions MessageOptions(GmailOptions): ...
  21. Models class class GmailModel GmailModel(object): _deferred = False False _state

    = ModelState() ... class class constructor constructor(type): def def __new__ __new__(cls, name, bases, attrs): dm = attrs.pop('_default_manager') klass = super().__new__( cls, name, bases, attrs) klass._default_manager = dm(klass) return return klass
  22. Models class class Message Message(GmailModel): __metaclass__ = constructor _default_manager =

    MessageManager _meta = MessageOptions() class class Thread Thread(GmailModel): __metaclass__ = constructor _default_manager = ThreadManager _meta = ThreadOptions()
  23. Admin class class MessageInline MessageInline(admin.TabularInline): model = Message class class

    ThreadAdmin ThreadAdmin(admin.ModelAdmin): inlines = [MessageInline] fields = ('number_of_messages',) list_display = ('id',) ordering = ('id',) admin.site.register(Thread, ThreadAdmin)
  24. Admin class class MessageAdmin MessageAdmin(admin.ModelAdmin): fields = ('sender', 'receiver', 'body',

    'snippet') list_display = ('id',) ordering = ('id',) admin.site.register(Message, MessageAdmin)