# 搞懂Django中的class Meta - edited: 2019.10.24 --- ## Resource - 關於@staticmethod及@classmethod - [@staticmethod和@classmethod的用法](https://blog.csdn.net/GeekLeee/article/details/52624742) - [python @classmethod 的使用场合](https://blog.csdn.net/dyh4201/article/details/78336529) - [ Python 文章收集 : 深刻理解 Python 中的元類 (metaclass) ](http://puremonkey2010.blogspot.com/2015/02/python-python-metaclass.html) - [必看!Python “黑魔法” 之 Meta Classes](https://segmentfault.com/a/1190000004426130) - [廖雪峰-使用元类](https://www.liaoxuefeng.com/wiki/1016959663602400/1017592449371072) >- 廖雪峰這篇下半段, 有針對Django ORM的實做解析, 必看 ## Intro - 我們在Django中宣告Model時, 一般會如下定義我們的Model, ex:宣告一個Person ``` from django.db import models class Person(models.Model): name = models.CharField(max_length=30) age = models.IntegerField() # 定義一個class Meta並宣告資料要呈現的方式, ex: ordering... # Meta支援的功能可參考: # http://www.liujiangblog.com/course/django/99 class Meta: ordering = ['pub_date'] ``` - 使用上相當方便,宣告`class Meta`後,緊跟著加入資料的功能(ex: 排序),即可取得預期的排序資料 ## 問題探討: - 問題來了, 這個`class Meta`有什麼關係?? - 嚴格上來說,`class Meta`這個`子class`,與python的` __metaclass__`<font color='blue'>並無直接關係,而是屬於間接關係</font> - 在宣告新的model class時,我們繼承`models.Model`,這個父類別使用的metaclass為`ModelBase`,這個metaclass就是在背後協助`models.Model`來處理`Meta`支援的項目 - 來看一下[Django-git上的程式碼](https://github.com/django/django/blob/stable/2.0.x/django/db/models/base.py), 相關說明寫於code的commend中 - `django/db/models/base.py` ``` # === 71 line, class ModelBase === class ModelBase(type): #<--注意!他是類(type), 也就是python最底層的物件 """Metaclass for all models.""" def __new__(cls, name, bases, attrs): super_new = super().__new__ ... # === 89 line, 取得 Meta這個class物件 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 # <-- 有抓到Meta就給meta吧 ... # === 134 line, 處理class Meta底下的功能, === 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 ... ... # === 393 line, class Model === class Model(metaclass=ModelBase): # <-- 使用ModelBase當作metaclass ... ``` ## 小結: - 這也是為什麼, 其他django內建的model class, ex: ` django.contrib.auth.models.User`, 均可支援`class Meta`, 因為追溯源頭, 是`ModelBase`幫了一把!! ``` from django.contrib.auth.models import User User.__base__ # <--User繼承AbstractUser >>> <class 'django.contrib.auth.models.AbstractUser'> from django.contrib.auth.models import AbstractUser AbstractUser.__base__ # <--AbstractUser繼承AbstractBaseUser >>> <class 'django.contrib.auth.base_user.AbstractBaseUser'> from django.contrib.auth.models import AbstractBaseUser AbstractBaseUser.__base__ # <--AbstractBaseUser繼承至Model, 所以都支援class Meta啦!! >>> <class 'django.db.models.base.Model'> ``` ## 補充說明:什麼是python的類(type)? - 簡單來說, 所有的東西, 都源自於同一個根, 這個根在python中, 就稱為`類(type)` - 開啟terminal觀察一下: ``` >>> a=5 # 宣告一個int物件, 並查看他的型態 >>> a.__class__ <class 'int'> # 沒錯!型態是int >>> a.__class__.__class__ # 那int底層的型態又是什麼呢? <class 'type'> # 答案就是我們討論的 type ``` - 從上面的例子我們可以發現, 所有型態物件的`根`, 就是`類type` - 要搞懂類, 你可以看[ Python 文章收集 : 深刻理解 Python 中的元類 (metaclass) ](http://puremonkey2010.blogspot.com/2015/02/python-python-metaclass.html)