Slide 1

Slide 1 text

I never Meta model I didn't like Dr Russell Keith-Magee DjangoCon US 2015

Slide 2

Slide 2 text

I never Meta model I didn't like Dr Russell Keith-Magee DjangoCon US 2015

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

What is meta programming?

Slide 8

Slide 8 text

An example: Python

Slide 9

Slide 9 text

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)

Slide 10

Slide 10 text

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)

Slide 11

Slide 11 text

Metaprogramming Python >>> output_point(p1) x = 3 y = 4 >>> output_point3D(p2) x = 5 y = 6 z = 7

Slide 12

Slide 12 text

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)

Slide 13

Slide 13 text

Metaprogramming Python >>> output_point2(p1) x = 3 y = 4 >>> output_point2(p2) x = 5 y = 6 z = 7

Slide 14

Slide 14 text

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)

Slide 15

Slide 15 text

Metaprogramming Python class class Point4D Point4D(Point3D): def def __init__ __init__(self, x, y, z, t): super().__init__(x, y, z) self.t = t

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

Metaprogramming Python

Slide 19

Slide 19 text

Metaprogramming Django

Slide 20

Slide 20 text

Modelforms

Slide 21

Slide 21 text

django.contrib.admin

Slide 22

Slide 22 text

django.contrib.admin

Slide 23

Slide 23 text

django.contrib.admin

Slide 24

Slide 24 text

History of Django's Meta

Slide 25

Slide 25 text

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()

Slide 26

Slide 26 text

Unofficially stable

Slide 27

Slide 27 text

Daniel Pyrathon

Slide 28

Slide 28 text

The New Meta API

Slide 29

Slide 29 text

The New Meta API meta = MyObject._meta meta.get_field(field_name) meta.get_fields( include_parents=True True, include_hidden=False False )

Slide 30

Slide 30 text

The New Meta API field.is_relation field.one_to_one field.many_to_one field.one_to_many field.many_to_many field.related_model field.auto_created field.concrete

Slide 31

Slide 31 text

The New Meta API

Slide 32

Slide 32 text

The New Meta API

Slide 33

Slide 33 text

So why bother?

Slide 34

Slide 34 text

High level frameworks and tools

Slide 35

Slide 35 text

A practical example

Slide 36

Slide 36 text

A practical example

Slide 37

Slide 37 text

A practical example

Slide 38

Slide 38 text

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) ...

Slide 39

Slide 39 text

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() ...

Slide 40

Slide 40 text

Merging records def def merge_customer merge_customer(orig, dup): dup.workorder_set.update(customer=orig) dup.invoice_set.update(customer=orig) dup.payment_set.update(customer=orig) dup.delete()

Slide 41

Slide 41 text

Time passes...

Slide 42

Slide 42 text

Adding quotes class class Quote Quote(Model): customer = ForeignKey(Customer) quote_number = CharField(max_length=100) ...

Slide 43

Slide 43 text

Adding quotes def def merge_customer merge_customer(orig, dup): dup.workorder_set.update(customer=orig) dup.invoice_set.update(customer=orig) dup.payment_set.update(customer=orig) dup.delete()

Slide 44

Slide 44 text

Adding quotes def def merge_customer merge_customer(orig, dup): dup.workorder_set.update(customer=orig) dup.invoice_set.update(customer=orig) dup.payment_set.update(customer=orig) dup.quote_set.update(customer=orig) dup.delete()

Slide 45

Slide 45 text

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()

Slide 46

Slide 46 text

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()

Slide 47

Slide 47 text

Killer app

Slide 48

Slide 48 text

Wrapping non-relational data stores

Slide 49

Slide 49 text

What does "How do I use X with Django?" actually mean?

Slide 50

Slide 50 text

What does "How do I use X with Django?" actually mean?

Slide 51

Slide 51 text

Can I get a Django form for my model? and Can I query and edit my data in Django's Admin?

Slide 52

Slide 52 text

Can I get a Django form for my model?

Slide 53

Slide 53 text

Can I query and edit my MongoDB data in Django's Admin?

Slide 54

Slide 54 text

Can I query and edit my MongoDB data in Django's Admin?

Slide 55

Slide 55 text

Dream the impossible dream

Slide 56

Slide 56 text

None of this needs to be in core

Slide 57

Slide 57 text

django-mailer https://github.com/PirosB3/django-mailer

Slide 58

Slide 58 text

django-mailer https://github.com/PirosB3/django-mailer

Slide 59

Slide 59 text

Managers class class GmailManager GmailManager(object): def def __init__ __init__(self, model): storage = Storage(settings.CREDENTIALS_PATH) self.creds = storage.get() self.model = model ...

Slide 60

Slide 60 text

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, ...)

Slide 61

Slide 61 text

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()

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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): ... ...

Slide 64

Slide 64 text

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): ...

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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()

Slide 67

Slide 67 text

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)

Slide 68

Slide 68 text

Admin class class MessageAdmin MessageAdmin(admin.ModelAdmin): fields = ('sender', 'receiver', 'body', 'snippet') list_display = ('id',) ordering = ('id',) admin.site.register(Message, MessageAdmin)

Slide 69

Slide 69 text

(... and breathe ....)

Slide 70

Slide 70 text

Call to action

Slide 71

Slide 71 text

Why not meta duck at the sprints?

Slide 72

Slide 72 text

MongoDB Google App Engine Datastore Cassandra Riak CouchDB (or any other non-relational data store)

Slide 73

Slide 73 text

LDAP Local mailbox directory File system AWS S3/EC2 resources

Slide 74

Slide 74 text

SQLAlchemy

Slide 75

Slide 75 text

Caveat emptor

Slide 76

Slide 76 text

No content

Slide 77

Slide 77 text

Thank you! [email protected] @freakboy3742