Django 5.2 Custom Middleware และ Signal Handling: คู่มือเตรียมสัมภาษณ์เชิงเทคนิค

คู่มือเชิงลึก Django 5.2 custom middleware และ signal handling สำหรับการสัมภาษณ์เชิงเทคนิค ครอบคลุม request pipeline, async middleware, post_save, pre_save, custom signals และแนวทางปฏิบัติที่ดีสำหรับ production

Django 5.2 custom middleware และ signal handling คู่มือเตรียมสัมภาษณ์

Custom middleware และ signal handling ใน Django 5.2 เป็นสองหัวข้อพื้นฐานที่ปรากฏในการสัมภาษณ์เชิงเทคนิคของ Django แทบทุกครั้ง การทำความเข้าใจว่า request ไหลผ่าน middleware stack อย่างไร และ signal ช่วยแยกส่วน logic ของแอปพลิเคชันอย่างไร คือสิ่งที่แยกผู้สมัครที่ท่องจำเอกสารออกจากผู้สมัครที่สร้างแอปพลิเคชัน Django ระดับ production ได้จริง ทั้ง middleware และ signal เป็นกลไกที่ทำงานอยู่เบื้องหลังของ Django application ทุกตัว แต่การเข้าใจรายละเอียดปลีกย่อยในเชิงสถาปัตยกรรมจะช่วยให้สามารถตอบคำถามสัมภาษณ์ได้อย่างมั่นใจ ไม่ว่าจะเป็นคำถามเกี่ยวกับลำดับการทำงานของ middleware, ความแตกต่างระหว่าง pre_save กับ post_save หรือการออกแบบ custom signal สำหรับ domain events

สรุปสำหรับเตรียมสัมภาษณ์

Django middleware ประมวลผล request จากบนลงล่างตามลำดับใน MIDDLEWARE list และประมวลผล response จากล่างขึ้นบน ส่วน signal ทำงานตามรูปแบบ publish-subscribe โดย sender กระจายเหตุการณ์ออกไป และ receiver ตอบสนองโดยไม่ต้องมีการเชื่อมต่อโดยตรง แนวคิดทั้งสองนี้มักปรากฏในรอบสัมภาษณ์ระดับ senior เนื่องจากเป็นการทดสอบความเข้าใจเชิงสถาปัตยกรรม ไม่ใช่แค่ความรู้ด้าน syntax เท่านั้น

กลไกการทำงานของ Django Middleware

Middleware pipeline อยู่ระหว่าง web server กับ Django views โดยแต่ละ middleware component จะห่อหุ้ม component ถัดไป สร้างโครงสร้างแบบหัวหอม (onion-like structure) ที่ request ต้องผ่านเข้าไปทีละชั้น เมื่อ request เข้ามา Django จะเรียกแต่ละ middleware ตามลำดับที่กำหนดไว้ใน settings.MIDDLEWARE จากบนลงล่าง หลังจาก view ประมวลผลเสร็จและคืนค่า response กลับมา middleware stack เดิมจะประมวลผล response ในลำดับย้อนกลับจากล่างขึ้นบน

Django 5.2 ใช้รูปแบบ middleware สมัยใหม่ที่เริ่มนำมาใช้ตั้งแต่ Django 1.10 โดยแต่ละ middleware เป็น callable ที่รับ get_response ระหว่างการ initialize และประมวลผล request/response cycle ใน method __call__ โครงสร้างพื้นฐานมีดังนี้

python
# middleware.py - Basic middleware structure
class SimpleMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration happens here at server start

    def __call__(self, request):
        # Code executed BEFORE the view (and later middleware)
        response = self.get_response(request)
        # Code executed AFTER the view (on the way back)
        return response

Method __init__ ทำงานเพียงครั้งเดียวเมื่อเซิร์ฟเวอร์เริ่มทำงาน จึงเป็นจุดที่เหมาะสำหรับการตั้งค่าที่ใช้ทรัพยากรสูง เช่น การโหลด configuration จากไฟล์ การสร้าง database connection pool หรือการ compile regular expression patterns ที่ใช้ซ้ำในทุก request ส่วน method __call__ จะถูกเรียกทุกครั้งที่มี request เข้ามา โดยโค้ดที่อยู่ก่อน self.get_response(request) จะทำงานในขาเข้า (request phase) และโค้ดที่อยู่หลังจะทำงานในขากลับ (response phase) จุดนี้เป็นแนวคิดสำคัญที่ผู้สัมภาษณ์มักถามเพื่อทดสอบความเข้าใจในลำดับการทำงานของ middleware

การสร้าง Request Logging Middleware

Request logging middleware เป็นตัวอย่างเชิงปฏิบัติที่แสดงให้เห็นวงจรชีวิตทั้งหมดของ middleware ได้อย่างชัดเจน การ implement นี้จะบันทึกข้อมูลเวลาที่ใช้ในการประมวลผล, request metadata รวมถึง HTTP method, path, IP address และ status code ของ response ทุกรายการลงใน structured log ซึ่งเป็นตัวอย่างที่ผู้สัมภาษณ์มักต้องการเห็นเพื่อประเมินความเข้าใจในระดับ production

python
# apps/core/middleware.py
import logging
import time

logger = logging.getLogger('django.request')


class RequestLoggingMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        start_time = time.monotonic()
        # Attach metadata before view processing
        request.start_time = start_time

        response = self.get_response(request)

        duration = time.monotonic() - start_time
        logger.info(
            'method=%s path=%s status=%d duration=%.3fs ip=%s',
            request.method,
            request.get_full_path(),
            response.status_code,
            duration,
            request.META.get('REMOTE_ADDR'),
        )
        return response

    def process_exception(self, request, exception):
        # Called only when the view raises an exception
        duration = time.monotonic() - getattr(request, 'start_time', 0)
        logger.error(
            'method=%s path=%s exception=%s duration=%.3fs',
            request.method,
            request.get_full_path(),
            str(exception),
            duration,
        )
        return None  # Let Django's default exception handling continue

การใช้ time.monotonic() แทน time.time() เป็นสิ่งที่ควรสังเกต เนื่องจาก monotonic clock ไม่ได้รับผลกระทบจากการปรับเวลาระบบ (system clock adjustment) ทำให้การวัดระยะเวลาแม่นยำกว่า นอกจากนี้ การแนบ start_time ไว้กับ request object ทำให้สามารถเข้าถึงค่านี้ได้ทั้งใน __call__ และ process_exception โดยไม่ต้องใช้ตัวแปร global

Hook process_exception เป็น method พิเศษของ middleware ที่ Django จะเรียกเฉพาะเมื่อ view เกิด exception ที่ไม่ได้จัดการ การ return None จะปล่อยให้ exception ไหลต่อไปยัง exception handler ตัวมาตรฐานของ Django ในขณะที่การ return HttpResponse จะตัดวงจร exception handling ทันที สิ่งนี้เป็นคำถามสัมภาษณ์ที่พบบ่อย เพราะเป็นการทดสอบว่าผู้สมัครเข้าใจ exception propagation ใน middleware stack หรือไม่

ลำดับของ Middleware: ทำไมจึงสำคัญ

ลำดับของ middleware ใน settings.py ส่งผลโดยตรงต่อพฤติกรรมของแอปพลิเคชัน การจัดลำดับผิดอาจทำให้เกิดปัญหาด้านความปลอดภัย หรือทำให้ middleware ไม่ทำงานตามที่คาดหวัง

python
# settings.py - Middleware ordering matters
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    # Custom middleware placed after Django's core stack
    'apps.core.middleware.RequestLoggingMiddleware',
]

SecurityMiddleware ควรอยู่ลำดับแรกเสมอ เนื่องจากมีหน้าที่ตั้งค่า security headers สำหรับทุก response รวมถึง HTTPS redirect SessionMiddleware ต้องอยู่ก่อน AuthenticationMiddleware เนื่องจากการยืนยันตัวตนต้องพึ่งพาข้อมูล session ที่โหลดมาจาก cookie แล้ว

คำถามสัมภาษณ์ที่พบบ่อยคือ ทำไม CsrfViewMiddleware ต้องอยู่หลัง SessionMiddleware คำตอบคือ การป้องกัน CSRF ใน Django สามารถใช้ session-based tokens ได้ ดังนั้น session จึงต้องถูกโหลดก่อน หากสลับลำดับ CSRF protection อาจทำงานไม่ถูกต้อง สำหรับ custom middleware ที่ไม่มีข้อกำหนดพิเศษด้านลำดับ แนะนำให้วางไว้ท้ายรายการ หลังจาก middleware หลักของ Django ทั้งหมด

Async Middleware ใน Django 5.2

Django 5.2 รองรับ asynchronous middleware อย่างเต็มรูปแบบ ซึ่งเป็นหัวข้อที่ผู้สัมภาษณ์ให้ความสนใจมากขึ้นเรื่อย เนื่องจาก async views และ ASGI deployment กำลังเป็นที่นิยม Async middleware มีความสำคัญอย่างยิ่งสำหรับ I/O-bound operations เช่น การเรียก external API, cache lookups หรือการตรวจสอบ rate limiting จาก Redis

python
# apps/core/middleware.py
import asyncio
import time


class AsyncTimingMiddleware:
    # Mark this middleware as async-capable
    async_capable = True
    sync_capable = False

    def __init__(self, get_response):
        self.get_response = get_response

    async def __call__(self, request):
        start = time.monotonic()
        # get_response is awaitable in async context
        response = await self.get_response(request)
        duration = time.monotonic() - start
        response['X-Request-Duration'] = f'{duration:.4f}s'
        return response

การตั้งค่า async_capable = True และ sync_capable = False จะบอก Django ว่า middleware นี้ทำงานได้เฉพาะในโหมด async เท่านั้น หากแอปพลิเคชันมีทั้ง sync views และ async views ให้ตั้งค่าทั้งสอง flag เป็น True แล้ว Django จะเลือกเรียก method ที่เหมาะสมตามประเภทของ view โดยอัตโนมัติ สิ่งสำคัญที่ควรสังเกตคือ ใน async context self.get_response(request) จะคืนค่า awaitable ดังนั้นต้องใช้ await เสมอ หากลืม await จะได้รับ coroutine object แทน response ซึ่งเป็นข้อผิดพลาดที่พบบ่อยเมื่อเริ่มต้นเขียน async middleware

สำหรับ production ที่รันบน ASGI server อย่าง Daphne หรือ Uvicorn async middleware ช่วยให้ประมวลผล request ได้พร้อมกันหลายตัวบน event loop เดียว โดยไม่ต้องพึ่งพา thread pool เหมือน WSGI ซึ่งช่วยลด memory footprint และเพิ่ม throughput ได้อย่างมีนัยสำคัญ

พร้อมที่จะพิชิตการสัมภาษณ์ Django แล้วหรือยังครับ?

ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ

Django Signals: รูปแบบ Publish-Subscribe

Django signals เป็นกลไกที่ช่วยให้ component ที่แยกส่วนกันสามารถสื่อสารกันได้โดยไม่ต้องมีการเชื่อมต่อโดยตรง (loose coupling) เมื่อ model ถูกบันทึก ลบ หรือเมื่อ request เริ่มต้นหรือเสร็จสิ้น Django จะกระจาย signal ออกไปให้ receiver ที่ลงทะเบียนไว้จัดการ การแยกส่วนนี้ทำให้สามารถเพิ่ม logic ใหม่ได้โดยไม่ต้องแก้ไขโค้ดเดิม ซึ่งสอดคล้องกับ Open-Closed Principle

Signal ที่ถูกใช้บ่อยที่สุดในการสัมภาษณ์คือ pre_save, post_save, pre_delete และ post_delete จาก django.db.models.signals นอกจากนี้ยังมี request_started และ request_finished สำหรับ HTTP request lifecycle ผู้สมัครที่สามารถอธิบายได้ว่าควรใช้ signal ใดในสถานการณ์ใด จะได้รับคะแนนสูงในการสัมภาษณ์

การใช้ post_save สำหรับการสร้าง Profile อัตโนมัติ

Pattern คลาสสิกของ post_save คือการสร้าง object ที่เกี่ยวข้องเมื่อ model instance ถูกบันทึกเป็นครั้งแรก ตัวอย่างที่พบบ่อยที่สุดคือการสร้าง user profile อัตโนมัติเมื่อมีการสร้าง User ใหม่ แนวทางนี้ช่วยให้ User model สะอาด (clean) และมอบหมายความรับผิดชอบในการสร้าง profile ให้กับ signal layer โดยไม่ต้องแก้ไข User model โดยตรง

python
# apps/accounts/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth import get_user_model
from apps.accounts.models import UserProfile

User = get_user_model()


@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    # 'created' is True only on INSERT, False on UPDATE
    if created:
        UserProfile.objects.create(
            user=instance,
            display_name=instance.get_full_name() or instance.username,
        )

Parameter created แบบ boolean เป็นจุดสำคัญที่ช่วยแยกแยะระหว่าง record ใหม่ (INSERT) กับการอัปเดต record ที่มีอยู่แล้ว (UPDATE) การตรวจสอบ flag นี้ทุกครั้งจะป้องกันการสร้าง profile ซ้ำเมื่อมีการ save User instance ในภายหลัง ส่วน parameter **kwargs เป็นสิ่งจำเป็นสำหรับ signal receiver ทุกตัว เนื่องจาก Django อาจเพิ่ม keyword arguments ใหม่ในเวอร์ชันอนาคต การละเว้น **kwargs จะทำให้โค้ดเสียหายเมื่ออัปเกรด Django

การใช้ pre_save สำหรับ Data Validation และ Transformation

Signal pre_save ทำงานก่อนที่ Django จะเขียนข้อมูลลงฐานข้อมูล จึงเหมาะอย่างยิ่งสำหรับการแปลงข้อมูล (data transformation) ก่อนบันทึก ตัวอย่างที่แสดงด้านล่างคือการสร้าง URL slug อัตโนมัติจาก title ของบทความ พร้อมกลไกตรวจสอบความซ้ำกัน

python
# apps/blog/signals.py
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.utils.text import slugify
from apps.blog.models import Article


@receiver(pre_save, sender=Article)
def auto_generate_slug(sender, instance, **kwargs):
    if not instance.slug:
        base_slug = slugify(instance.title)
        slug = base_slug
        counter = 1
        # Ensure slug uniqueness
        while Article.objects.filter(slug=slug).exclude(pk=instance.pk).exists():
            slug = f'{base_slug}-{counter}'
            counter += 1
        instance.slug = slug

โค้ดนี้ตรวจสอบก่อนว่า slug ยังว่างอยู่หรือไม่ (if not instance.slug) เพื่อไม่ให้เขียนทับ slug ที่ผู้ใช้กำหนดเอง จากนั้นใช้ loop ตรวจสอบความซ้ำกัน โดย .exclude(pk=instance.pk) จะไม่นับ record ปัจจุบัน ทำให้ไม่เกิดปัญหาเมื่อ save record เดิมซ้ำ สิ่งที่ควรสังเกตคือการแก้ไข instance.slug โดยตรง จะส่งผลต่อข้อมูลที่ถูกเขียนลงฐานข้อมูล เนื่องจาก pre_save ทำงานก่อนคำสั่ง SQL INSERT/UPDATE

การลงทะเบียน Signal ใน AppConfig.ready()

แนวทางที่ Django แนะนำอย่างเป็นทางการสำหรับการลงทะเบียน signal คือการ import signal module ภายใน method AppConfig.ready() วิธีนี้รับประกันว่า signal receiver จะถูกเชื่อมต่อเพียงครั้งเดียว หลังจากที่ app registry โหลด model ทั้งหมดเรียบร้อยแล้ว ทำให้ไม่เกิดปัญหา AppRegistryNotReady หรือ signal ที่ลงทะเบียนซ้ำ

python
# apps/accounts/apps.py
from django.apps import AppConfig


class AccountsConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'apps.accounts'

    def ready(self):
        # Import signals module to register receivers
        import apps.accounts.signals  # noqa: F401

Comment # noqa: F401 บอก linter ว่าการ import โดยไม่ใช้ตัวแปรนี้เป็นเรื่องตั้งใจ เนื่องจากจุดประสงค์ของ import คือการ execute module-level code ใน signals.py ซึ่งจะทำให้ decorator @receiver ทำงานและลงทะเบียน handler กับ signal ที่กำหนดไว้ หากไม่มีการ import นี้ signal receiver จะไม่ถูกเชื่อมต่อ และ logic ที่เขียนไว้จะไม่ทำงาน

การสร้าง Custom Signals สำหรับ Domain Events

นอกเหนือจาก built-in model signals แล้ว Django ยังรองรับการสร้าง custom signals สำหรับจัดการ domain-specific events เช่น การชำระเงินสำเร็จ การเปลี่ยนแปลงสถานะ subscription หรือการส่งการแจ้งเตือน การใช้ custom signal ช่วยแยก business logic ของแต่ละ domain ออกจากกัน ทำให้ระบบขยายตัวได้ง่ายโดยไม่ต้องแก้ไขโค้ดเดิม

python
# apps/orders/signals.py
from django.dispatch import Signal

# Define custom signals with documentation
order_completed = Signal()  # Sent after payment confirmation
order_refunded = Signal()   # Sent after refund processing


# apps/orders/services.py
from apps.orders.signals import order_completed


def complete_order(order):
    order.status = 'completed'
    order.save()
    # Dispatch signal with relevant context
    order_completed.send(
        sender=order.__class__,
        order=order,
        total=order.total_amount,
    )


# apps/notifications/receivers.py
from django.dispatch import receiver
from apps.orders.signals import order_completed


@receiver(order_completed)
def send_order_confirmation_email(sender, order, **kwargs):
    # Email logic decoupled from order processing
    from apps.notifications.services import send_email
    send_email(
        to=order.customer.email,
        template='order_confirmation',
        context={'order': order},
    )

ตัวอย่างนี้แสดงให้เห็นการแยกส่วนที่ชัดเจนระหว่างสาม module ได้แก่ orders/signals.py ที่ประกาศ signal, orders/services.py ที่ dispatch signal หลังจากประมวลผล order เสร็จ และ notifications/receivers.py ที่รับ signal และจัดการส่ง email โดยไม่ต้องรู้รายละเอียดของ order processing แนวทางนี้ทำให้สามารถเพิ่ม receiver ใหม่ในอนาคตได้ เช่น การส่ง push notification หรือการอัปเดต analytics โดยไม่ต้องแก้ไข complete_order function เลย

ข้อผิดพลาดที่พบบ่อยในการสัมภาษณ์เกี่ยวกับ Middleware และ Signals

Signals กับ QuerySet.update()

Method QuerySet.update() และ bulk_create() ของ Django จะข้าม method save() ของ model ทั้งหมด ซึ่งหมายความว่า signal pre_save และ post_save จะไม่ถูกเรียกใช้งาน เช่นเดียวกับ delete() บน QuerySet เทียบกับการเรียก delete() บน model instance ตัวเดียว นี่เป็นหนึ่งในคำถามเกี่ยวกับ Django signal ที่ถูกถามบ่อยที่สุดในการสัมภาษณ์ การไม่ทราบข้อจำกัดนี้อาจทำให้ signal handler ที่เขียนไว้ถูกข้ามไปโดยไม่รู้ตัว

ข้อผิดพลาดอื่นที่พบบ่อยในระดับ senior interview มีดังนี้

  • Circular imports ใน signals: การ import model ที่ระดับ module ใน signal files อาจทำให้เกิด error AppRegistryNotReady ควร import ภายใน receiver function หรือใช้ AppConfig.ready() เสมอ
  • การขาด **kwargs ใน receiver: การละเว้น **kwargs จาก signature ของ receiver function จะทำให้เกิดข้อผิดพลาดเมื่อ Django เพิ่ม parameter ใหม่ให้กับ signal ในอนาคต ซึ่งเป็นข้อกำหนดที่ผู้สมัครหลายคนมองข้าม
  • Logic หนักใน signal handlers: Signal ทำงานแบบ synchronous ภายใน database transaction เดียวกันโดยค่าเริ่มต้น หาก signal handler ทำงานหนัก เช่น เรียก external API หรือส่ง email โดยตรง จะทำให้ response time ของ view สูงขึ้น สำหรับ operation ที่ใช้เวลานานควร offload ไปยัง task queue เช่น Celery หรือ Django Q
  • Middleware ที่แก้ไข response content: การเปลี่ยนแปลง response.content หลังจากที่ view ตั้งค่าไว้แล้ว อาจทำให้ streaming responses เสียหาย และทำให้ Content-Length header ไม่ตรงกับขนาดจริงของ response body
  • การลงทะเบียน signal ซ้ำ: หากใช้ connect() แทน decorator @receiver อาจเกิดกรณีที่ handler ถูกลงทะเบียนหลายครั้ง (เช่น เมื่อ Django reload module) การตั้ง dispatch_uid จะป้องกันปัญหานี้ได้

Middleware กับ Signals: เมื่อใดควรใช้อะไร

การเลือกใช้ middleware หรือ signal ขึ้นอยู่กับลักษณะของงานที่ต้องการทำ ตารางด้านล่างสรุปการเปรียบเทียบที่ช่วยในการตัดสินใจ

| ประเด็น | Middleware | Signals | |---|---|---| | การแก้ไข request/response | ทำได้โดยตรง | ทำไม่ได้ | | Authentication และ authorization | เหมาะสมอย่างยิ่ง | ไม่เหมาะสม | | Model lifecycle hooks | ทำไม่ได้ | เหมาะสมอย่างยิ่ง | | Cross-cutting concerns (logging, timing) | เหมาะสมอย่างยิ่ง | ทำได้แต่ไม่เหมาะ | | Decoupled business logic | ไม่เหมาะสม | เหมาะสมอย่างยิ่ง | | Async support ใน Django 5.2 | รองรับเต็มรูปแบบ | รองรับเต็มรูปแบบ (asend/asend_robust) | | ขอบเขตการทำงาน | ทุก HTTP request | เหตุการณ์เฉพาะ (save, delete, custom) | | จุดเริ่มต้นการทำงาน | HTTP request เข้ามา | เหตุการณ์ถูก dispatch |

กฎทั่วไปคือ หากต้องการจัดการ HTTP request/response ให้ใช้ middleware หากต้องการตอบสนองต่อการเปลี่ยนแปลงของ model หรือ domain event ให้ใช้ signal การใช้ signal สำหรับ cross-cutting concern อย่าง logging ถึงแม้จะเป็นไปได้ แต่จะทำให้โค้ดซับซ้อนโดยไม่จำเป็น เนื่องจาก middleware ถูกออกแบบมาเพื่องานประเภทนี้โดยเฉพาะ

บทสรุป

ประเด็นสำคัญที่ควรจดจำสำหรับการสัมภาษณ์เชิงเทคนิคเกี่ยวกับ Django middleware และ signals มีดังนี้

  • Django middleware สร้าง pipeline แบบหัวหอมที่ประมวลผล request จากบนลงล่างและ response จากล่างขึ้นบน โดย self.get_response(request) คือจุดแบ่งระหว่างขั้นตอนก่อนและหลัง view ลำดับใน settings.MIDDLEWARE จึงมีผลต่อพฤติกรรมของระบบโดยตรง
  • Custom middleware เหมาะสำหรับ cross-cutting concerns เช่น logging, timing, security headers, rate limiting และ request modification ที่ต้องทำงานกับทุก HTTP request
  • Django 5.2 รองรับ async middleware อย่างเต็มรูปแบบผ่าน async_capable flag และ async __call__ method ช่วยให้จัดการ I/O-bound operations ได้โดยไม่บล็อก event loop สำหรับแอปพลิเคชันที่รันบน ASGI server
  • Signal post_save และ pre_save จัดการ model lifecycle events โดย pre_save เหมาะสำหรับการแปลงข้อมูลก่อนเขียนลงฐานข้อมูล ส่วน post_save เหมาะสำหรับการสร้าง related objects และส่งการแจ้งเตือน
  • QuerySet.update(), bulk_create() และ QuerySet.delete() ข้าม signal ทั้งหมด ซึ่งเป็นข้อจำกัดที่ถูกถามบ่อยที่สุดในการสัมภาษณ์เกี่ยวกับ Django signals
  • ควรลงทะเบียน signal ใน AppConfig.ready() เสมอ เพื่อรับประกันว่า handler จะถูกเชื่อมต่อเพียงครั้งเดียวหลังจากโหลด model ทั้งหมดแล้ว
  • Custom signals ช่วยแยก domain events ออกจาก business logic หลัก ทำให้ระบบขยายตัวได้ง่ายตามหลัก Open-Closed Principle โดยไม่ต้องแก้ไขโค้ดเดิม

เริ่มฝึกซ้อมเลย!

ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ

แท็ก

#django
#python
#middleware
#signals
#interview

แชร์

บทความที่เกี่ยวข้อง

เตรียมสัมภาษณ์งาน Django ครอบคลุม ORM, Middleware และแนวคิด Django REST Framework

คำถามสัมภาษณ์งาน Django: ORM, Middleware และ DRF เจาะลึก

คำถามสัมภาษณ์งาน Django ครอบคลุมการปรับแต่ง ORM ด้วย select_related และ prefetch_related, สถาปัตยกรรม middleware, ประสิทธิภาพ serializer ของ Django REST Framework, permissions และรูปแบบ pagination

คำถามสัมภาษณ์งาน Django และ Python - คู่มือฉบับสมบูรณ์

คำถามสัมภาษณ์งาน Django และ Python: 25 คำถามยอดนิยมประจำปี 2026

25 คำถามสัมภาษณ์งาน Django และ Python ที่พบบ่อยที่สุด ORM, views, middleware, DRF, signals และการปรับแต่งประสิทธิภาพ พร้อมคำตอบละเอียดและตัวอย่างโค้ด

การปรับแต่งคิวรี Django ORM เพื่อประสิทธิภาพสูงสุด

Django ORM: เพิ่มประสิทธิภาพคิวรีให้ทำงานเต็มขีดความสามารถ

คู่มือฉบับสมบูรณ์สำหรับเพิ่มประสิทธิภาพคิวรีของ Django ORM ทั้ง select_related, prefetch_related, ดัชนี การวิเคราะห์ปัญหา N+1 และเทคนิคขั้นสูงสำหรับแอปพลิเคชันที่ต้องการประสิทธิภาพสูง