---
# System prepended metadata

title: 搞懂Django中的class Meta

---

# 搞懂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)
