ปรับปรุงการตั้งค่า Django ของคุณด้วย Type Hints: A Pydantic Tutorial
เผยแพร่แล้ว: 2022-07-22โครงการ Django เคยทำให้ฉันหงุดหงิดเพราะฉันขาดวิธีการที่แข็งแกร่งและปรับขนาดได้เพื่อเพิ่มสภาพแวดล้อมใหม่ ด้วยการนำคำใบ้ประเภท pydantic และ Python มารวมกัน ฉันได้สร้างรากฐานอันทรงพลังที่ฉันต้องการ
ตามที่อธิบายไว้ใน PEP 484 คำแนะนำประเภทสนับสนุนการวิเคราะห์แบบสถิต แต่คำอธิบายประกอบเดียวกันนี้ยังพร้อมใช้งานในขณะใช้งานจริง แพ็คเกจของบริษัทอื่น เช่น การตรวจสอบประเภทรันไทม์ของข้อเสนอ pydantic ที่ใช้ข้อมูลเมตาเพิ่มเติมนี้ Pydantic ใช้คำแนะนำประเภท Python เพื่อช่วยจัดการข้อมูลเมตาของการตั้งค่าและดำเนินการตรวจสอบข้อมูลรันไทม์
กวดวิชา pydantic นี้จะแสดงผลเชิงบวกที่กว้างขวางของการใช้การจัดการการตั้งค่า pydantic กับ Django
การกำหนดค่าของเราเป็นไปตามแนวทางปฏิบัติที่ดีที่สุดที่อธิบายไว้ในเว็บไซต์แอพ Twelve-Factor:
- กำหนดคอนฟิกูเรชันที่ไม่คงที่และเป็นความลับเป็นตัวแปรสภาพแวดล้อม
- ในสภาพแวดล้อมการพัฒนา กำหนดตัวแปรสภาพแวดล้อมในไฟล์
.env
และเพิ่ม ..env
ไปที่.gitignore
- ใช้กลไกของผู้ให้บริการระบบคลาวด์เพื่อกำหนด (ความลับ) ตัวแปรสภาพแวดล้อมสำหรับสภาพแวดล้อม QA การจัดเตรียม และการใช้งานจริง
- ใช้ไฟล์
settings.py
ไฟล์เดียวที่กำหนดค่าตัวเองจากตัวแปรสภาพแวดล้อม - ใช้ pydantic เพื่ออ่าน ตรวจสอบ ตรวจสอบ และตัวแปรสภาพแวดล้อม typecast บนตัวแปร Python ที่กำหนดการกำหนดค่า Django
นักพัฒนาซอฟต์แวร์บางรายสร้างไฟล์การตั้งค่าหลายไฟล์ เช่น settings_dev.py
และ settings_prod.py
น่าเสียดายที่แนวทางนี้ไม่สามารถปรับขนาดได้ดี ทำให้เกิดความซ้ำซ้อนของโค้ด ความสับสน ข้อบกพร่องที่หายาก และความพยายามในการบำรุงรักษาที่สูงขึ้น
การใช้แนวทางปฏิบัติที่ดีที่สุดที่กล่าวข้างต้น การเพิ่มสภาพแวดล้อมจำนวนเท่าใดก็ได้เป็นเรื่องง่าย มีการกำหนดไว้อย่างดี และป้องกันข้อผิดพลาดได้ แม้ว่าเราจะสำรวจการกำหนดค่าสภาพแวดล้อมที่ซับซ้อนกว่านี้ได้ แต่เราจะเน้นที่สองส่วนเพื่อความชัดเจน: การพัฒนาและการผลิต
สิ่งนี้มีลักษณะอย่างไรในทางปฏิบัติ?
การจัดการการตั้งค่า Pydantic และตัวแปรสภาพแวดล้อม
ตอนนี้เราเน้นที่ตัวอย่างทั้งในด้านการพัฒนาและการผลิต เราแสดงให้เห็นว่าแต่ละสภาพแวดล้อมกำหนดค่าการตั้งค่าต่างกันอย่างไร และ pydantic รองรับแต่ละสภาพแวดล้อมอย่างไร
แอปพลิเคชันตัวอย่างของเราต้องการฐานข้อมูลที่สนับสนุน Django ดังนั้นเราจึงจำเป็นต้องจัดเก็บสตริงการเชื่อมต่อฐานข้อมูล เราย้ายข้อมูลการกำหนดค่าการเชื่อมต่อฐานข้อมูลไปยังตัวแปรสภาพแวดล้อม DATABASE_URL
โดยใช้แพ็คเกจ Python dj-database-url โปรดทราบว่าตัวแปรนี้เป็นประเภท str
และจัดรูปแบบดังนี้:
postgres://{user}:{password}@{hostname}:{port}/{database-name} mysql://{user}:{password}@{hostname}:{port}/{database-name} oracle://{user}:{password}@{hostname}:{port}/{database-name} sqlite:///PATH
ในสภาพแวดล้อมการพัฒนาของเรา เราสามารถใช้อินสแตนซ์ PostgreSQL ที่มี Docker เพื่อความสะดวกในการใช้งาน ในขณะที่ในสภาพแวดล้อมการใช้งานจริง เราจะชี้ไปที่บริการฐานข้อมูลที่จัดเตรียมไว้
ตัวแปรอื่นที่เราต้องการกำหนดคือบูลีน DEBUG
ต้องไม่เปิดแฟล็ก DEBUG ใน Django ในการปรับใช้ที่ใช้งานจริง มีวัตถุประสงค์เพื่อรับข้อเสนอแนะเพิ่มเติมในระหว่างการพัฒนา ตัวอย่างเช่น ในโหมดดีบัก Django จะแสดงหน้าข้อผิดพลาดโดยละเอียดเมื่อมีข้อยกเว้นเกิดขึ้น
ค่านิยมการพัฒนาและการผลิตที่แตกต่างกันสามารถกำหนดได้ดังนี้
ชื่อตัวแปร | การพัฒนา | การผลิต |
---|---|---|
DATABASE_URL | postgres://postgres:mypw@localhost:5432/mydb | postgres://foo1:foo2@foo3:5432/foo4 |
DEBUG | True | False |
เราใช้โมดูลการจัดการการตั้งค่า pydantic เพื่อจัดการชุดค่าตัวแปรสภาพแวดล้อมที่แตกต่างกันเหล่านี้ขึ้นอยู่กับสภาพแวดล้อม
ขั้นตอนการเตรียมการ
เพื่อนำไปปฏิบัติ เราเริ่มกำหนดค่าสภาพแวดล้อมการพัฒนาของเราโดยการสร้างไฟล์ .env
ไฟล์เดียวที่มีเนื้อหานี้:
DATABASE_URL=postgres://postgres:mypw@localhost:5432/mydb DEBUG=True
ต่อไป เราเพิ่มไฟล์ .env
ลงในไฟล์ .gitignore
ของโปรเจ็กต์ ไฟล์ .gitignore
หลีกเลี่ยงการบันทึกข้อมูลที่อาจมีความละเอียดอ่อนในการควบคุมแหล่งที่มา
ในขณะที่วิธีนี้ใช้ได้ผลดีในสภาพแวดล้อมการพัฒนาของเรา ข้อมูลจำเพาะของสภาพแวดล้อมการผลิตของเราใช้กลไกที่แตกต่างออกไป แนวทางปฏิบัติที่ดีที่สุดของเรากำหนดว่าตัวแปรสภาพแวดล้อมการผลิตใช้ความลับของสภาพแวดล้อม ตัวอย่างเช่น ใน Heroku ความลับเหล่านี้เรียกว่า Config Vars และกำหนดค่าผ่าน Heroku Dashboard มีให้สำหรับแอปพลิเคชันที่ปรับใช้เป็นตัวแปรสภาพแวดล้อม:
หลังจากนั้น เราจำเป็นต้องปรับการกำหนดค่าของแอปพลิเคชันเพื่ออ่านค่าเหล่านี้จากสภาพแวดล้อมใดสภาพแวดล้อมหนึ่งโดยอัตโนมัติ
การกำหนด settings.py
. ของ Django
เริ่มต้นด้วยโครงการ Django ใหม่เพื่อให้มีโครงสร้างที่จำเป็นสำหรับตัวอย่างของเรา เรานั่งร้านโครงการ Django ใหม่ด้วยคำสั่งเทอร์มินัลต่อไปนี้:
$ django-admin startproject mysite
ตอนนี้เรามีโครงสร้างพื้นฐานของโปรเจ็กต์สำหรับโปรเจ็กต์ mysite
แล้ว โครงสร้างไฟล์ของโครงการมีดังนี้:
mysite/ manage.py mysite/ __init__.py settings.py urls.py asgi.py wsgi.py
ไฟล์ settings.py
มีโค้ดสำเร็จรูปที่ช่วยให้เราจัดการการกำหนดค่าแอปพลิเคชันได้ มีการตั้งค่าเริ่มต้นที่กำหนดไว้ล่วงหน้ามากมายที่เราต้องปรับให้เข้ากับสภาพแวดล้อมที่เกี่ยวข้อง
ในการจัดการการตั้งค่าแอปพลิเคชันเหล่านี้โดยใช้ตัวแปรสภาพแวดล้อมและ pydantic ให้เพิ่มโค้ดนี้ที่ด้านบนสุดของไฟล์ settings.py
:
import os from pathlib import Path from pydantic import ( BaseSettings, PostgresDsn, EmailStr, HttpUrl, ) import dj_database_url # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent class SettingsFromEnvironment(BaseSettings): """Defines environment variables with their types and optional defaults""" DATABASE_URL: PostgresDsn DEBUG: bool = False class Config: """Defines configuration for pydantic environment loading""" env_file = str(BASE_DIR / ".env") case_sensitive = True config = SettingsFromEnvironment() os.environ["DATABASE_URL"] = config.DATABASE_URL DATABASES = { "default": dj_database_url.config(conn_max_age=600, ssl_require=True) } DEBUG = config.DEBUG
รหัสนี้ทำสิ่งต่อไปนี้:
- กำหนดคลาส
SettingsFromEnvironment
สืบทอดจากคลาส BaseSettings ของBaseSettings
- กำหนด
DATABASE_URL
และDEBUG
ตั้งค่าประเภทและตัวเลือกเริ่มต้นโดยใช้คำแนะนำประเภท Python - กำหนดคลาส
Config
ที่บอกให้.env
ค้นหาตัวแปรในไฟล์ .env หากไม่มีอยู่ในตัวแปรสภาพแวดล้อมของระบบ - สร้างอินสแตนซ์คลาส
Config
ลงในอ็อบเจ็กต์config
; ตัวแปรที่ต้องการจะพร้อมใช้งานเป็นconfig.DATABASE_URL
และconfig.DEBUG
- กำหนดตัวแปร Django ปกติ
DATABASES
และDEBUG
จากสมาชิกการกำหนดconfig
เหล่านี้
รหัสเดียวกันทำงานในทุกสภาพแวดล้อม และ pydantic จะดูแลสิ่งต่อไปนี้:
- ค้นหาตัวแปรสภาพแวดล้อม
DATABASE_URL
และDEBUG
- หากกำหนดเป็นตัวแปรสภาพแวดล้อม เช่น ในการผลิต จะใช้สิ่งเหล่านั้น
- มิฉะนั้น จะดึงค่าเหล่านั้นจากไฟล์ .
.env
- หากไม่พบค่า จะทำสิ่งต่อไปนี้:
- สำหรับ
DATABASE_URL
จะแสดงข้อผิดพลาด - สำหรับ
DEBUG
จะกำหนดค่าเริ่มต้นFalse
- สำหรับ
- หากพบตัวแปรสภาพแวดล้อม ระบบจะตรวจสอบประเภทฟิลด์และให้ข้อผิดพลาดหากรายการใดรายการหนึ่งไม่ถูกต้อง:
- สำหรับ
DATABASE_URL
จะตรวจสอบว่าประเภทฟิลด์เป็น URL แบบPostgresDsn
- สำหรับ
DEBUG
จะตรวจสอบว่าประเภทฟิลด์เป็น pydantic boolean ที่ถูกต้องและไม่เข้มงวด
- สำหรับ
โปรดสังเกตการตั้งค่าที่ชัดเจนของตัวแปรสภาพแวดล้อมของระบบปฏิบัติการจากค่าการกำหนดค่าสำหรับ DATABASE_URL
อาจดูเหมือนซ้ำซ้อนในการตั้งค่า os.environ["DATABASE_URL"] = config.DATABASE_URL
เนื่องจาก DATABASE_URL
ถูกกำหนดให้เป็นตัวแปรสภาพแวดล้อมภายนอกแล้ว อย่างไรก็ตาม สิ่งนี้ทำให้ pydantic แยกวิเคราะห์ ตรวจสอบ และตรวจสอบความถูกต้องของตัวแปรนี้ได้ หากตัวแปรสภาพแวดล้อม DATABASE_URL
หายไปหรือจัดรูปแบบไม่ถูกต้อง pydantic จะแสดงข้อความแสดงข้อผิดพลาดที่ชัดเจน การตรวจสอบข้อผิดพลาดเหล่านี้มีค่ามากเมื่อแอปพลิเคชันย้ายจากการพัฒนาไปสู่สภาพแวดล้อมที่ตามมา
หากไม่ได้กำหนดตัวแปร ค่าดีฟอลต์จะถูกกำหนดหรือข้อผิดพลาดจะแจ้งให้กำหนด พรอมต์ที่สร้างขึ้นจะระบุรายละเอียดประเภทตัวแปรที่ต้องการด้วย ประโยชน์ด้านข้างของการตรวจสอบเหล่านี้คือสมาชิกในทีมใหม่และวิศวกร DevOps ค้นพบได้ง่ายขึ้นว่าต้องกำหนดตัวแปรใดบ้าง วิธีนี้ช่วยหลีกเลี่ยงปัญหาที่ยากต่อการค้นหาซึ่งส่งผลให้เมื่อแอปพลิเคชันทำงานโดยไม่มีตัวแปรทั้งหมดที่กำหนดไว้
แค่นั้นแหละ. ขณะนี้แอปพลิเคชันมีการใช้งานการจัดการการตั้งค่าที่บำรุงรักษาได้โดยใช้ settings.py
เวอร์ชันเดียว ความงามของแนวทางนี้คือช่วยให้เราสามารถระบุตัวแปรสภาพแวดล้อมที่ถูกต้องในไฟล์ .env
หรือวิธีการอื่นๆ ที่ต้องการได้ผ่านสภาพแวดล้อมการโฮสต์ของเรา
เส้นทางที่ปรับขนาดได้
ฉันใช้การจัดการการตั้งค่า Django ด้วยการพิมพ์รันไทม์ pydantic ในโครงการ Django ทั้งหมดของฉัน ฉันพบว่ามันนำไปสู่ฐานโค้ดที่เล็กกว่าและบำรุงรักษาได้มากกว่า นอกจากนี้ยังมีวิธีการที่มีโครงสร้างที่ดี จัดทำเอกสารด้วยตนเอง และปรับขนาดได้เพื่อเพิ่มสภาพแวดล้อมใหม่
บทความถัดไปในชุดนี้เป็นบทช่วยสอนทีละขั้นตอนเกี่ยวกับการสร้างแอปพลิเคชัน Django ตั้งแต่เริ่มต้น พร้อมการจัดการการตั้งค่า pydantic และปรับใช้กับ Heroku
บล็อก Toptal Engineering ขอขอบคุณ Stephen Davidson สำหรับการตรวจสอบตัวอย่างโค้ดที่นำเสนอในบทความนี้
เวอร์ชันก่อนหน้าของบทความนี้เน้นย้ำถึงการเปิดตัว Python ที่เฉพาะเจาะจง เนื่องจาก Python รองรับคำแนะนำประเภทมาหลายปี เราจึงสรุปข้อความแนะนำโดยลบการอ้างอิงเวอร์ชันนี้