Django เลือก template มา render อย่างไร และทำไม Best Practice ถึงถูก
Tags: django, python, templates
Published: 2020-04-07 15:30:44


จากการลองเล่น framework ตัวหนึ่งชื่อ Django (จังโก้) เพื่อนำมาใช้ในการทำ Web GUI สำหรับ project หนึ่ง

พอลองเล่นมาถึง [Writing your first Django app, part 3](https://docs.djangoproject.com/en/3.0/intro/tutorial03/) เกิดงงเรื่อง directory structure ของ template ที่ต้องเป็น `project_name/app_name/templates/app_name/` และการสั่ง render template ต้องอ้างอิงชื่อเป็น `app_name/home.html` ซึ่งเค้าบอกว่าเป็น Best Practice 

ที่ผมงงคือ... ทำไม directory structure ต้องมี `app_name` ซ้ำ 2 ครั้งเลยล่ะ ครั้งเดียวมันน่าจะรู้แล้วไหม และ ตอนเรียก template มา render ก็ต้องใส่ `app_name` ข้างหน้าชื่อ template อีก ซึ่งเราทำที่ app นี้อยู่แล้วทำไม่ยังต้องระบุอีกล่ะ

> **OK! ถ้ามันเป็น Best Practice ความไม่ make sense ต้องมีคำอธิบาย**

มาดูกัน ผมเจออะไร (ต้องบอกก่อนว่าผมเพิ่งหัดใช้นะครับ เรื่องนี้หลายคนอาจรู้อยู่แล้ว 😁)

ใน `settings.py` ของ Django ที่เป็น "DjangoTemplate" จะเป็นแบบนี้

``` python
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
```

เราจะสนใจ 2 parameters นี้ก่อนคือ

1. `DIRS` คือ path ที่เราต้องการให้ django ไปค้นหา เป็น list of strings (default = [])
2. ` APP_DIRS` คือ เป็น boolean บอกว่าให้หา template ใน directory ของ app หรือ ไม่ (default = True)

โดย พฤติกรรมในการหา template ของ "DjangoTemplate" จะเป็นลำดับตามนี้ 

1. หาใน `DIRS` ก่อน โดยหาตามลำดับของ list ที่เรากำหนด 
เช่น `DIRS = ['path/to/template1', 'path/to/template2', 'path/to/template3']` จะหาตามลำดับดังนี้

    > /path/to/template1/
    > /path/to/template2/
    > /path/to/template3/

2. หาใน `APP_DIRS` โดยถ้าเราตั้งค่าเป็น `True` Django จะไปดูจาก ตัวแปร `INSTALLED_APPS` ใน `settings.py` ซึ่ง เป็น list of string เช่น `INSTALLED_APPS = ['app-a', 'app-b', 'app-c']` Django จะหาตามลำดับของ list นี้ ดังนี้

    > /path/to/project/app-a/templates/
    > /path/to/project/app-b/templates/
    > /path/to/project/app-b/templates/

จากตัวอย่างข้างต้น ลำดับของ directory ที่ Django ใช้หา template คือ

> /path/to/template1/
> /path/to/template2/
> /path/to/template3/
> /path/to/project/app-a/templates/
> /path/to/project/app-b/templates/   👈 เราจะเขียน template ที่นี่
> /path/to/project/app-b/templates/

ด้วย logic การทำงานแบบนี้ ถ้าเราเขียน template ที่ `app-b` ชื่อ `home.html` แล้วเราระบุ ชื่อ template ในการ render แค่ `home.html` ถ้าบังเอิญว่า `app-a` ก็มี `home.html` อยู่เช่นกัน มันจะไป render `home.html` ของ app-a แทน เพราะมันหาเจอก่อน

ด้วยพฤติกรรมการทำงานแบบนี้ เราจึงต้องทำตาม Best Practice โดยสร้าง sub directory ที่เป็นชื่อ app ใต้ directory templatate ของ app นั้นๆ ด้วย ดังนี้

> /path/to/template1/
> /path/to/template2/
> /path/to/template3/
> /path/to/project/app-a/templates/<mark>app-a</mark>/
> /path/to/project/app-b/templates/<mark>app-b</mark>/   👈 เราจะเขียน template ที่นี่
> /path/to/project/app-b/templates/<mark>app-c</mark>/

และเวลาที่เราเรียก template มาใช้งานก็จะเรียกด้วยการระบุชื่อ template ดังนี้ `app-b/home.html`

ด้วยวิธีนี้ การ render จะไม่หยิบ template มาผิด app