در قسمت اول سری آموزشی جنگوی زنجیر شکسته میریم سراغ تنظیمات. جایی که همه تنظیمات پیکربندی نصب و اجرای برنامه شما را در خودش نگهداری میکنه. فایل تنظیمات جنگو یا همون settings.py یک ماژول پایتون است (اسناد رسمی پایتون-ماژول ها) ، پس شما باید از دستورات پایتون برای اعمال تنظیمات استفاده کنید.
در اینجا قسمتی از فایل تنظیمات را میبینیم:
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]خب اینجا تو خط اول به ما یه هشدار امنیتی میده که حالت دیباگ را در مرحله اجرای برنامه بر روی سرور غیرفعال کنیم، چرا؟ واضحه چون اگه خاصیت دیباگ فعال باشه، هرجایی که مشکلی پیش بیاد خطا میده و با توجه به اون خطا شما خیلی راحت میتونید مشکل را برطرف کنید پس اصلا منطقی نیست که بازدید کننده ها بدون هیچ دردسری باگ های برنامه شما رو ببینن!!!
از طرف دیگه زمانی بهتون اجازه غیر فعال کردن حالت دیباگ رو میده که آدرس برنامتونو در قسمت ALLOWED_HOSTS مشخص کنید. مثلا اینطوری:
DEBUG = False
ALLOWED_HOSTS = ['www.example.com'] # دامنه شمادر همه حال DRY را رعایت کنید !!!
اگر تجربه کار با جنگو را داشته باشید حد اقل یک بار کلمه DRY را شنیدید. اگر هم نه الان بهتون میگم.
DRY چیست ؟
DRY مخفف عبارت Don’t Repeat Yourself
است یعنی کار تکراری نکن . DRY بهمون میگه تا جایی که ممکنه از اختراع دوباره چرخ اجتناب کنید.
واقعا نیازی به نوشتن کدهای تکراری وجود نداره اگر خوب و بهینه کد ها رو بنویسیم .
قانون طلایی در طراحی برنامه های جنگو
به قول جیمز بنت، توسعه دهنده ی هسته و همینطور مدیر انتشار (Release manager) جنگو ، راز طراحی و توسعه ی هنرمندانه ی یک برنامه ی جنگو ، پیروی از فلسفه ی یونیکس است .
فلسفه یونیکس مجموعهای از هنجارهای فرهنگی و رویکردهای فلسفی برای توسعه نرمافزارهای کوچک اما تواناست که بر اساس تجربیات توسعهدهندگان برجسته سیستمعامل یونیکس شکل گرفتهاست. فلسفه یونیکس بر ساختاری کوچک، ساده، واضح، پیمانهای و قابل گسترش تأکید دارد که به غیر از نویسندگان اصلی و اولیه کد، توسعهدهندگان دیگر هم بتوانند آن را به سادگی نگهداری کنند و برای اهداف مختلف از آن استفاده کنند.
داگلاس مک ایلروی ، یکی از توسعه دهندگان یونیکس ، فلسفه ی یونیکس را در این سه جمله تعریف میکند :
Write programs that do one thing and do it well
برنامههایی بنویسید که تنها یک کار را انجام دهند، اما آن کار را به خوبی انجام دهند.
Write programs to work together
برنامههایی بنویسید که بتوانند با یکدیگر کار کنند.
Write programs to handle text streams, because that is a universal interface
برنامههایی بنویسید که بتوانند جریانهای متنی را مدیریت کنند، چرا که آنها یک رابط جامع و کامل هستند.
مسیر پایتون یا PYTHONPATH چیست ؟
پایتون در مسیر های مشخص و از پیش تعیین شده ای به دنبال ماژول ها و بسته های نرم افزاری خود جست و جو میکند . از این رو PYTHONPATH یک متغیر محلی است که به وسیله آن میتوانیم دایرکتوری های بیشتری به این مسیر ها اضافه کنیم . به مثال زیر توجه کنید :
export PYTHONPATH=${PYTHONPATH}:${HOME}/py_modules در مثال بالا ، دایرکتوری py_modules در Home را به مسیر پایتون خود اضافه کردیم .
مشخص کردن تنظیمات
زمانی که از جنگو استفاده میکنیم باید مشخص کنیم از چه تنظیماتی استفاده میکنیم . این کار را با متغیرِ محلیِ DJANGO_SETTINGS_MODULE انجام میدهیم .
آدرس فایل تنظیمات در متغیر DJANGO_SETTINGS_MODULE باید در مسیر پایتون ( PYTHONPATH ) باشد . مانند : mysite.settings
ابزار django-admin
زمانی که از دستور django-admin استفاده میکنیم میتوانیم یک بار آدرس تنظیمات را به متغیر محلی دهیم ( همانطور که در پاراگراف قبل توضیح داده شد ) و یا اینکه در هر بار اجرا ، آدرس تنظیمات را به آپشن --settings دهیم (این مورد در قسمت تنظیمات پیکربندی چندگانه به همراه مثال توضیح داده شد ) .
مثال :
# مثال استفاده از DJANGO_SETTINGS_MODULE
export DJANGO_SETTINGS_MODULE=mysite.settings
django-admin runserver# مثال استفاده از آپشن --settings
django-admin runserver --settings=mysite.settings
بر روی سرور ( mod_wsgi )
در محیط سرور بهره برداری یا Live server , ما باید تنظیمات را به برنامه WSGI بدهیم . چگونه ؟؟ از طریق شئ environ از ماژول os :
import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'WSGI چیست ؟
WSGI کوتاه شده ی عبارت Web Server Gateway Interface به معنی رابط دروازه سرور وب در واقع مشخصاتی است که تعیین میکند وب سرور چگونه با وب اپلیکیشن ارتباط برقرار کند و اینکه برنامه ها ( همان وب اپلیکیشن ها ) چگونه به صورت زنجیر وار به هم متصل شوند و به یک درخواست ( Request ) پاسخ دهند .
ریشه و نحوه کار تنظیمات پیکربندی در پروژه جنگو
در فایل تنظیمات اولیه همه متغیر هایی که برای پیکر بندی اولیه نیاز هست قرار دارن و جدا از اونا ما میتونیم تنظیمات دیگری رو هم اضافه کنیم که میشه گفت همشون متغیر های پایتونی اند. هر متغیر، شامل یک مقدار پیشفرض مشخصی است که این پیشفرض ها در ماژولی در مسیر django/conf/global_settings.py تعیین شده اند.
تنظیمات پیکربندی با ران شدن سرور، کامپایل میشن و برای اعمال تغییرات، سرور باید ری استارت شه پس نباید در حالت اجرا تنظیمات را تغییر بدیم (بهتره در محیط توسعه تغییرات را اعمال کنیم)
الگوریتم جنگو برای کامپایل تنظیمات پیکربندی
جنگو کامپایل تنظیمات پیکربندی را به ترتیب زیر انجام میده :
1 - بارگزاری تنظیمات پیکر بندی از ماژول global_settings.py
2 - بارگزاری تنظیمات پیکربندی از ماژول settings.py و جایگزین کردن تنظیمات settings.py به جای تنظیماتی که در مرحله قبل توسط global_settings.py بارگزاری شدند.
مشاهده diff تنظیمات
برای مشاهده تنظیماتی که نسبت به تنظیمات پیشفرض تغییر کردن میتونیم از دستور python manage.py diffsettings استفاده کنیم.
استفاده از تنظیمات پیکربندی در جاهای دیگر
برای استفاده از تنظیمات پیکربندی در کدهای پایتونی خودمون، باید شئ settings را از ماژول django.conf فراخوانی کنیم :
from django.conf import settings
if settings.DEBUG: # مثلا اگر حالت دیباگ فعال بود
# فلان کارو انجام بدهنکته ای که باید حواسمون بهش باشه اینه که django.conf.settings یک ماژول نیست بلکه تنها یک شئ است، پس نمیتونیم مثلا ویژگی DEBUG در مثال قبل را در کد پایتونی خودمون ایمپورت کنیم :
from django.conf.settings import DEBUG # کار نخواهد کرد
Traceback (most recent call last):
File "<console>", line 1, in <module>
ModuleNotFoundError: No module named 'django.conf.settings'نکته دیگه اینکه هرگز نباید متغیر های پیکربندی را از global_settings و یا فایل تنظیمات خودمون settings.py به کد پایتونی ایمپورت کنیم چون باعث دوگانگی و اختلال در برنامه میشه. پس برای این کار تنها شئ django.conf.settings را در کد پایتونی خود فراخوانی کرده تا از این طریق به ویژگی های global_settings و settings دسترسی داشته باشیم .
تغییر تنظیمات پیکربندی در زمان اجرا
هرگز نباید تنظیمات پیکربندی را در زمان اجرا و در سایر کدهای پایتونی تغییر بدیم.مثلا تغییر تنظیمات پیکربندی از طریق view ها کار درستی نیست! چرا؟ دلیلشو قبلا گفتیم و الان میدونیم که اینکار چقدر میتونه گرون تموم شه برامون.
from django.conf import settings
settings.DEBUG = True # هرگز این کارو نکنید!!!تکرار میکنم تنظیمات پیکربندی را فقط باید از طریق settings.py ویرایش کنیم.
متغیر های موجود برای تنظیمات
اسناد رسمیِ جنگو، در این صفحه به بررسی کامل همه متغیر های موجود برای تنظیمات و مقدار پیش فرض آنها پرداخته است. البته امیدوارم در آینده نزدیک در یکی از قسمت های همین سری آموزشی بتونیم این صفحه را به طور کامل بررسی کنیم .
کنترل نسخه
کنترل نسخه (Version Control System) چیست ؟
استفاده از ابزار کنترل نسخه طی مراحل طراحی ، توسعه و استقرار پروژه میتونه خیلی به ما کمک کنه تا همه تغییرات را تحت نظر داشته باشیم و در مواقع نیاز ، این تغییرات را کنترل کنیم .
در زمان اجرای برنامه در سرور (در حالت بهره برداری از پروژه) استفاده از کنترل نسخه بسیار حیاتی است. چرا؟؟؟ چون ما میخواهیم همه جزئیات از جمله زمان، تاریخ، تغییرات تنظیمات پیکربندی، تغییر نام فایل ها و هر تغییر کوچکی توسط ابزار کنترل نسخه (مثلا گیت Git) ردیابی شوند تا هم از تغییرات اطلاع داشته باشیم و هم بتونیم در مواقع لزوم، فایل های تغییر کرده را به حالت قبلی برگردانیم .
پس میبینیم که ردیابی تغییرات در پروژه مخصوصا در وضعیت بهره برداری (Production یا Deploy) چقدر مهمه و نادیده گرفتن این موضوع چقدر میتونه کار رو برامون سخت کنه.
کتاب آموزش گیت - Pro Git book - نسخه فارسی
امنیت
خب رسیدیم به قسمت جذاب ماجرا . امنیت !!!
تا اینجا با چند نکته برای بالا بردن امنیت برنامه آشنا شدیم ، حالا باید کمی سخت گیرانه تر برخورد کنیم .
از تنظیمات محلی (Local) بدون ردیابی نسخه خودداری کنید
ما به عنوان توسعه دهنده ی برنامه ، به تنظیمات مخصوص خودمون در محیط توسعه نیاز داریم . تنظیماتی مانند فعال سازی ابزار دیباگ که در محیط توسعه به آن نیاز داریم اما در وضعیت Staging و یا بهره برداری باید آن را غیر فعال کنیم ( و یا اصلا در این وضعیت آن ها را نصب نکنیم)
همچنین برای امنیت بیشتر ، فکر خوبیه که یک سری تنظیمات خاص را از مخازن (Repositories) عمومی و حتی خصوصی دور نگه داریم . اولین نمونه ای که از این تنظیمات به ذهن میاد ، کلید خصوصی (SECRET_KEY) است . همچنین سایر کلید ها مانند کلید های API ها و … و هرگونه متغیر های از جنس Password نیز نباید در مخازن وجود داشته باشند .
راز های خود را در یک جای امن نگه دارید
SECRET_KEY در واقع یک کلید خصوصی (Private Key) است که در سیستم رمز نگاری جنگو استفاده میشود . وقتی صحبت از کلید خصوصی شماست یعنی این کلید فقط مختص شما میشه پس باید یکتا (Unique) باشه . پس نباید توسط ابزار کنترل نسخه ردیابی شه ، اگر SECRET_KEY شما توسط گیت ردیابی شه پس دیگران میتونن این کلید خصوصی شمارو داشته باشن که این اصلا جالب نیست ، فکر نکنم کسی دوست داشته باشه کلید خونشو یکی دیگه هم داشته باشه .
همچنین این خطر برای بقیه دیتا های حساس شما هم وجود داره ، مثلا رمز عبور دیتابیس ، کلید های AWS ، توکن های OAuth و ... هم باید در یک جای امن نگه داری شوند .
کلید عمومی و کلید خصوصی چیست ؟
یک راه حل ساده این کار ساخت یک ماژول local_settings.py به صورت محلی برای هر سرور و یا محیط توسعه است که توسط ابزار کنترل نسخه ردیابی نشوند . در این حالت توسعه دهنده ها میتونن تغییرات خودشونو در تنظیمات پیکربندی اعمال کنند بدون اینکه این تغییرات توسط ابزار کنترل نسخه ردیابی شوند . همچنین سرورهایی که در وضعیت Staging یا Deployment هستند هم تنظیمات پیکربندیِ محلیِ مختص خودشان را دارند که این تنظیمات توسط ابزار کنترل نسخه ردیابی نشده و به این ترتیب در مخازن عمومی و خصوصی هم وجود نخواهند داشت .
خب ، آیا با این روش به مشکل خواهیم خورد ؟ معایب این روش چیست ؟
− در این وضعیت ماژول تنظیمات پیکربندی محلی ( local_settings.py ) در همه محیط ها از جمله محیط توسعه ، Staging و بهره برداری ( Production ) توسط ابزار کنترل نسخه ردیابی نمیشوند .
− چه حسی پیدا میکنید وقتی بعد از ساعتها کلنجار رفتن با یک باگ در محیط توسعه ( Local ) ، متوجه شوید مشکل فقط از قسمتی از تنظیمات در محیط بهره برداری ( Production ) است ؟
− چقدر ناامید میشوید وقتی متوجه شوید آن باگ را که در محیط محلی پیدا کرده ، آن را برطرف کرده و این تغییرات را در نسخه بهره برداری پیاده سازی کرده اید ، واقعاً ناشی از شخصی سازی هایی است که در ماژول تنظیمات محلی خود ( local_settings.py ) انجام داده اید و اکنون سایت را با مشکل مواجه کرده است ؟
− در این حالت همه ماژول ( local_settings.py ) را در محیط های مختلف کپی میکنند که این عمل ، فلسفه DRY را نقض نمیکند ( چون در هر محیط فقط یک ماژول local_settings.py داریم ) ، اما در مقیاس بزرگ چطور ؟
همانطور که دیدیم این روش میتونه مارو به دردسر بندازه ، پس باید یک روش جدید را امتحان کنیم .
بیایید تنظیمات پیکربندی را به چند تکه تقسیم کنیم ، تکه هایی متناسب با محیط اجرا ( Development , Staging , Test , Production ) که همشون از یک ماژول اصلی که توسط ابزار کنترل نسخه ردیابی میشود ، ارث بری داشته باشند . همچنین باید مطمئن شویم که رازهایمان ( کلید ها ، توکن ها ، رمز عبور ها و … ) بصورت راز باقی بمانند .
تنظیمات پیکربندی چندگانه
به جای ماژول settings.py میتوانیم یک دایرکتوری settings داشته باشیم که همه ماژول های تنظیمات را درون خود نگه میدارد . به مثال زیر دقت کنید :
settings/
__init__.py
base.py
local.py
staging.py
test.py
production.pyفایل __init__ چیست ؟
یک فایل خالی با پسوند py است که اگر در یک دایرکتوری وجود داشته باشد ، پایتون از آن دایرکتوری به عنوان یک دایرکتوری بسته نرم افزاری ( Python package directory ) استفاده میکند .
به بیان ساده تر ، اگر این فایل داخل دایرکتوری ما وجود نداشته باشد ، نمیتوانیم این ماژول ها را در پایتون بارگزاری ( import ) کنیم .
base.py
همان ماژول اصلی و مشترک بین همه تنظیمات است که تنظیمات مشترک را در خود نگه میدارد و سایر ماژول ها از این ماژول ارث بری میکنند .
local.py
ماژول تنظیمات محیط توسعه است . این ماژول شامل تنظیماتی میشود که در محیط توسعه محلی ( Local ) از آنها استفاده میکنیم . مانند DEBUG = True ، log level و یا فعال کردن ابزارهای توسعه مانند django-debug-toolbar
بعضی توسعه دهنده ها نام های دیگری مانند dev.py برای این ماژول درنظر میگیرند .
staging.py
نسخه ی حالت نمایش ( Staging ) از اجرای نیمه خصوصی برنامه بر روی سرور اصلی ( Production server ) . این مرحله ایست که مدیران و کاربران قبل از بارگزاری نسخه نهایی برنامه بر روی سرور ، با برنامه کار میکنند .
test.py
این ماژول تنظیمات مربوط به مرحله تست برنامه را شامل میشه . مثلا استفاده از ماژول ( test runner ) ، تنظیمات لاگ ( log settings ) و …
production.py
این ماژول تنظیمات سرور بهره برداری (Live production server) است ، یعنی همه تنظیمات مربوط به مرحله بهره برداری یا به اصطلاح ( Production ) در این ماژول نگهداری میشن .
بعضی توسعه دهنده ها نام های دیگری مانند prod.py برای این ماژول درنظر میگیرند .
ممکن است نیاز باشد یک ماژول ci.py برای تنظیمات سرور داشته باشیم ، همچنین اگر پروژه مان کمی بزرگ باشد ممکن است نیاز به سرور های دیگری هم داشته باشیم که در این صورت باید در شرایط مختلف ، ماژول های متناسب بسازیم .
خب حالا ببینیم که چطور باید از این تنظیمات متفاوت استفاده کنیم .
برای این کار باید از آپشن --settings در خط فرمان استفاده کنیم .
برای مثال اجرای مترجم پایتون با تنظیمات محلی ( local ) به این صورت خواهد بود :
python manage.py shell --settings=mysite.settings.local # آدرس ماژول تنظیمات محلیو یا اجرای وب سرور آزمایشی با تنظیمات staging :
python manage.py runserver --settings=mysite.settings.stagingیک مثال برای درک بهتر تنظیمات چندگانه
خب همانطور که دیدیم ، ما به یک ماژول تنظیمات پیکربندی محلی نیاز داریم تا تنظیمات فاز توسعه خودمان را در آن تعریف کنیم . تنظیماتی مانند انتخاب بکند ایمیل ، تنظیمات دیتابیسِ مرحله توسعه ، تنظیمات حالت DEBUG و همه تنظیماتی که در محیط توسعه برنامه به آن نیاز داریم . نمونه این تنظیمات میتواند چیزی شبیه به تکه کد زیر باشد :
# settings/local.py
from .base import *
DEBUG = True
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
INSTALLED_APPS += ("debug_toolbar", )حالا وب سرور آزمایشی را با دستور زیر اجرا میکنیم :
python manage.py runserver --settings=mysite.settings.localبه هیچ عنوان از عبارات شرطی ، حلقه یا عبارات منطقی در ماژول تنظیمات پیکربندی استفاده نکنید . تنظیمات باید در ساده ترین شکل ممکن باشند .
تنظیمات پیکربندی محلیِ چندگانه
گاهی اوقات ما بر روی پروژه های بزرگی کار میکنیم که در این وضعیت ، توسعه دهندگان مختلف به تنظیمات پیکربندی محلی مختلف ( تنظیمات مخصوص به خود ) نیاز دارند و اشتراک یک ماژول dev.py راه حل مناسبی نخواهد بود .
در این وضعیت اگر ماژول های تنظیمات توسط ابزار کنترل نسخه ردیابی شوند و هر توسعه دهنده ، ماژول تنظیمات مخصوص به خود را داشته باشد مانند dev_aref.py و dev_reza.py راه حل بهتری است تا اینکه توسعه دهنده های مختلف ، ماژول local.py یا dev.py را برای خود شخصی سازی کنند (بدون ردیابی توسط ابزار کنترل نسخه)
# settings/dev_aref.py
from .local import *
# Set short cache timeout
CACHE_TIMEOUT = 30این راه حل نه تنها از این لحاظ که ماژول تنظیمات ردیابی میشوند مناسب است ، بلکه شما میتوانید تنظیماتِ توسعه دهندگانِ همکارتان را مشاهده کنید و ایرادات یکدیگر را برطرف کنید . همچنین میشود از هماهنگ بودن همه ماژول های تنظیمات محلی مطمئن شد .
دایرکتوری تنظیمات برای همچین پروژه ای ، چیزی شبیه به مثال زیر خواهد شد :
settings/
__init__.py
base.py
dev_aref.py
dev_reza.py
local.py
staging.py
test.py
production.pyاطلاعات حساس و مهم را از کد دور نگه دارید
قبلا در مورد اهمیت مخفی کردن اطلاعات حساس از فایل های برنامه صحبت کردیم ، حالا وقتشه که کمی جدی تر با این قضیه رفتار کنیم .
یکی از دلایل ضد الگوی ( Anti Pattern ) تنظیمات پیکربندی ، وجود متغیر های پیکربندی مانند کلید های خصوصی از جمله SECRET_KEY ، کلید های AWS ، کلید های API و سایر تنظیمات مخصوص سرور در ماژول تنظیمات است .
ضد الگو یا Anti Pattern چیست ؟
الگوهای طراحی معمولاً راهحلهایی با قابلیت استفاده ی مجدد در سناریوهای مختلف هستند که میتوانند برای حل مسائل رایج هم مورد استفاده قرار گیرند و به ما کمک کنند تا کدمان را بهینه کنیم. در حالی که الگوهای طراحی به دلیل استفاده از فرمولها و روشهای تست شده برای بهبود فرایند توسعه کاربردی هستند، بعضی وقتها هم ممکن است ما را دچار اشتباه کنند که در این صورت آنها را Anti Pattern (آنتی پترن یا ضد الگو) مینامیم.
منبع : سکان آکادمی
در پاراگراف زیر تعدادی از مشکلات این ضد الگو را با هم میبینیم :
− میشه گفت متغیر های پیکربندی فقط در مراحل استقرار ( Deploy ) به کار میروند ، کد ها اینطور نیستند .
− کلید های خصوصی ، مقدار هایی هستند که تنها در پیکربندی به کار میروند ، کد ها اینطور نیستند .
− راز ها همیشه باید به صورت راز باقی بمانند ! نگهداری و ردیابی آنها توسط ابزار کنترل نسخه به این معنی است که هر کسی با مجوز دسترسی به مخزن برنامه ، به این راز ها هم دسترسی خواهد داشت .
− شرکت های ارائه دهنده هاستینگ معمولا به شما یک سرور اختصاصی جداگانه نمیدهند ، اگر هم همچین دسترسی ای به شما دادند ، باید جانب احتیاط را رعایت کرد .
خب راه حل مناسب چیست ؟
استفاده از متغیر های محلی ( Local Variables ) طبق الگویی که میتونیم الگوی متغیر های محلی صداش کنیم !
همه سرور هایی که از جنگو ( پایتون ) پشتیبانی میکنند ، امکان ساخت متغیر های محلی ( Local Environment ) را نیز برای توسعه دهنده ها فراهم میکنند ، پس از این لحاظ جای نگرانی وجود نداره .
فواید استفاده از متغیر محلی برای اطلاعات حساس و کلید های خصوصی :
− دور نگه داشتن اطلاعات حساس از ماژول تنظیمات ، به ما اجازه میده تا همه ی کدهای پایتونی مخصوصا همه ی فایل های تنظیمات برنامه را بدون هیچ نگرانی توسط ابزار کنترل نسخه ردیابی کنیم .
− توسعه دهندگان به جای روش قدیمی کپی/چسباندن فایل local_settings.py.example برای محیط های توسعه محلیِ خود ، از نسخه ی تحت ردیابی settings/local.py موجود در مخازن استفاده میکنند .
− مدیران سیستم میتوانند پروژه را به سرعت بارگذاری کنند بدون اینکه تغییری در کدهای پایتونی انجام دهند .
− بسیاری از سرویس دهنده های هاستینگ ، امکانات درونی برای تنظیمات و مدیریت متغیر های محلی در اختیار شما قرار می دهند تا در استفاده از این قابلیت کمترین پیچیدگی را متحمل شوید .
توضیحات سایت ۱۲ فاکتور درباره ذخیره پیکربندی ها در متغیر محلی
استفاده از متغیر محلی برای ذخیره ی اطلاعات حساس
در سیستم های مک و یا خیلی از توزیع های گنو لینوکس که از بش ( Bash ) به عنوان شل ( Shell ) استفاده میکنند میتوانیم با افزودن کد زیر به آخر فایل پیکربندی
.bashrc یا .bash_profile و یا .profile
، اطلاعات حساس خود را به یک متغیر محلی نسبت دهیم . مثال :
$ export SOME_SECRET_KEY=1c3-cr3am-15-yummy
$ export PROJECT_FREEZER_KEY=y34h-r1ght-d0nt-t0uch-my-1c3-cr34mدر مثال بالا میبینید که دو مقدار را به دو متغیر ( SOME_SECRET_KEY ) و ( PROJECT_FREEZER_KEY ) نسبت داده ایم .
حالا میتوانیم مانند مثال های زیر ، از این متغیر ها در ماژول های خود استفاده کنیم . نمونه استفاده از متغیر محلی در مفسر پایتون به این صورت خواهد بود :
>>> import os
>>> os.environ["SOME_SECRET_KEY"]
"1c3-cr3am-15-yummy"نمونه استفاده از متغیر محلی در ماژول تنظیمات نیز به صورت زیر خواهد بود :
# Top of settings/production.py
import os
SOME_SECRET_KEY = os.environ["SOME_SECRET_KEY"]کنترل استثنا ها
تا اینجا یاد گرفتیم که چطور باید اطلاعات حساس خود ( مثلا کلید خصوصی ) را در متغیر های محلی نگهداری کنیم ، اما اگر در قطعه کدی که تحت عنوان “نمونه استفاده از متغیر محلی در ماژول تنظیمات” دیدیم ، متغیر SOME_SECRET_KEY موجود نباشد چه اتفاقی خواهد افتاد ؟
در این صورت با خطای KeyError مواجه خواهیم شد که مانع اجرای برنامه میشود . این خوبه اما باید حواسمون باشه که این خطا ، اطلاعاتی درباره اینکه کجای کار مشکل دارد به ما نمیدهد و بدون همچین اطلاعاتی ، دیباگ کردن برنامه بسیار مشکل خواهد بود . مخصوصا زمانی که در مرحله استقرار برنامه بر روی سرور و تحت فشار کاربرانی که منتظرند هر چه سریعتر این مشکل برطرف شود هستیم !
برای حل این مشکل یک راه حل خوب داریم و این راه حل چیزی نیست جز تعریف یک تابع که با try و except ، متغیر محلی را دریافت میکند و اگر متغیر را پیدا نکند ، یک خطا با پیام مشخص میدهد . همچنین میدانیم که این تابع باید در ماژول base.py تعریف شود .
یک مثال ببینیم :
# settings/base.py
import os
from django.core.exceptions import ImproperlyConfigured
def get_env_variable(var_name):
"""Get the environment variable or return exception."""
try:
return os.environ[var_name]
except KeyError:
error_msg = "Set the {} environment variable".format(var_name)
raise ImproperlyConfigured(error_msg)یک نکته مهم
در حالت عادی نباید هیچ ماژولی را از جنگو به داخل ماژول تنظیمات فراخوانی کرد اما ImproperlyConfigured یک استثنا است .
پس از این به بعد برای فراخوانی متغیر محلی مورد نظرمان ، باید تابع جدیدمان را با آرگومان متغیر محلی مورد نظر فراخوانی کنیم . برای مثال در ماژول base.py :
SOME_SECRET_KEY = get_env_variable("SOME_SECRET_KEY")حالا اگر متغیر محلی SOME_SECRET_KEY وجود نداشته باشد با همچین خطایی روبرو خواهیم شد :
django.core.exceptions.ImproperlyConfigured:
Set the SOME_SECRET_KEY environment variable.استفاده از فایل های غیر قابل اجرا برای ذخیره اطلاعات پیکربندی به جای متغیر محلی
بزرگترین ایراد استفاده از متغیر های محلی ، این است که ممکن است همیشه جواب ندهد .این اتفاق معمولا در وب سرور های آپاچی ( Apache ) و گاهی اوقات هم برای سرورهای انجین-ایکس ( Nginx ) رخ می دهد . زمانی که به همچین مشکلی برخوردیم به جای بازگشت به ضد الگوی تنظیمات محلی ( Local settings anti-pattern ) ، پیشنهاد ما استفاده از فایل های غیر قابل اجرا بدون ردیابی توسط ابزار کنترل نسخه تحت عنوان الگوی فایل اطلاعات حساس ( Secrets file pattern ) است . حالا بیایید کمی بیشتر در این مورد بحث کنیم .
برای پیاده سازی الگوی فایل اطلاعات حساس ، باید به ترتیب زیر عمل کنیم :
− ساخت یک فایل حاوی تنظیمات پیکربندی تحت فرمت هایی مانند JSON , Config , YAML , XML و …
− اضافه کردن ابزار بارگزاری اطلاعات ( در مثال بعدی از JSON استفاده شده است ) برای مدیریرت منسجم و صریح اطلاعات
− جلوگیری از ردیابی فایل ساخته شده توسط ابزار کنترل نسخه ( مثلا اضافه کردن نام فایل JSON ساخته شده به فایل .gitignore برای جلوگیری از ردیابی این فایل توسط گیت Git)
به عنوان مثال ، برای این منظور میخواهیم از فایل JSON استفاده کنیم .
برای این کار یک فایل با نام secrets.json میسازیم . پیشنهاد من این است که کد ها را در ساده ترین حالت ممکن بنویسیم تا با سادگی و سرعت بیشتری با آن کار کنیم :
{
"FILENAME": "secrets.json",
"SECRET_KEY": "1c3-cr3am-15-yummy",
"DATABASES_HOST": "127.0.0.1",
"PORT": "5432",
"DATABASE_PASSWORD": "OldGeorgie1993",
}خب حالا برای استفاده و استخراج اطلاعات از فایل secrets.json در ماژول base.py مانند مثال زیر عمل میکنیم :
# settings/base.py
import json
# Normally you should not import ANYTHING from Django directly
# into your settings, but ImproperlyConfigured is an exception.
from django.core.exceptions import ImproperlyConfigured
# JSON-based secrets module
with open("secrets.json") as f:
secrets = json.loads(f.read())
def get_secret(setting, secrets=secrets):
"""Get the secret variable or return explicit exception."""
try:
return secrets[setting]
except KeyError:
error_msg = "Set the {0} environment variable".format(setting)
raise ImproperlyConfigured(error_msg)
SECRET_KEY = get_secret("SECRET_KEY")بسیار عالی ! همانطور که میبینید موفق شدیم اطلاعات حساس را از یک فایل غیرقابل اجرای JSON به جای فایل های اجراشونده ( Executable ) و بدون ردیابی توسط کنترل نسخه ، فراخوانی کنیم .
استفاده از فایل های ملزومات چندگانه
حالا میپردازیم به چگونگی نصب فایل های مورد نیاز سرور ها . قطعا بهترین راه این است که هر سرور ( Local , Staging , Production ) فقط فایل های مورد نیاز خود را نصب کند .
برای استفاده از این الگو ، دایرکتوری requirements/ را در دایرکتوری اصلی مخزن میسازیم ، سپس یک فایل با پسوند .txt متناظر با تنظیمات پیکربندی هر سرور ایجاد میکنیم . مثال :
requirements/
base.txt
local.txt
staging.txt
production.txtدر فایل base.txt نیازمندی های مشترک همه سرور ها را وارد میکنیم . برای مثال :
Django==2.2.7
psycopg2==2.8.4
djangorestframework==3.10.0همچنین فایل local.txt باید شامل نیازمندی های محیط توسعه محلی باشد . مانند :
-r base.txt # includes the base.txt requirements file
coverage==4.5.4
django-debug-toolbar==2.1همانطور که واضح است در مثال بالا میبینیم که با دستور -r base.txt در بالای فایل local.txt ، نیازمندی های مشترک را از فایل base.txt بارگزاری کرده و نیاز مندی های جدید را در خط های پایین تر وارد کردیم .
نیازمندی های محیط بهره برداری باید نزدیک به سایر محیط ها باشد ، پس در اینجا یک فایل production.txt داریم که معمولا فقط نیازمندی های مشترک موجود در base.txt را بارگزاری میکند :
-r base.txt # includes the base.txt requirements fileنصب فایل های ملزومات چندگانه
برای نصب فایل های مورد نیاز در محیط های پایتونی ، از PIP ( Python package manager ) استفاده میکنیم . برای مثال در محیط توسعه محلی :
$ pip install -r requirements/local.txtو یا در محیط بهره برداری ( Production ) :
$ pip install -r requirements/production.txtخلاصه ی بحث
به یاد داشته باشید :
همه ی فایل ها به جز اطلاعات مهم و حساس پیکربندی باید توسط ابزار کنترل نسخه ردیابی شوند .
همه پروژه هایی که به منظور استفاده تجاری و بهره برداری در لایو سرور طراحی میشوند باید تنظیمات و لیست ملزومات ( Settings and Requirements ) چندگانه داشته باشند .
منابع
Two Scoops of Django - Daniel Roy Greenfeld Audrey Roy Greenfeld
James Bennett volunteers as both a Django core developer and as its release manager.