یکی از تفاوت های اصلی بین یک اپلیکیشن ابتدایی و یک اپلیکیشن آماده به کار (production-ready) جنگو، در دیتابیس های آنان است. جنگو بدلیل راحت بودن، سریع بودن و file-based بودن SQLite، از این دیتابیس بصورت پیشفرض برای توسعه ی محلی (local development) بهره می برد و آن را به گزینه ی مناسبی تبدیل می کند. علاوه بر آن، این دیتابیس نیاز به هیچ گونه نصب و پیکربندی ندارد.
هرچند این دیتابیس معایب خاص خود را دارد. بطور کلی، SQLite دیتابیس مناسبی برای سایت های رده بالا و حرفه ای نیست. اما برای پیاده کردن ایده های اولیه این دیتابیس می تواند پاسخ گو باشد. بطور کلی SQLite بصورت خیلی کم و محدود برای پروژه های بزرگ مورد استفاده قرار می گیرد.
جنگو چهار دیتابیس را پشتیبانی می کند: SQLite, PostgreSQL, MySQL و Oracle.ما در این کتاب از PostgreSQL بدلیل معروف بودن آن استفاده خواهیم کرد. زیبایی Django ORM در آن است که اگر حتی از Oracle یا MySQL به جای PostgreSQL استفاده کنیم؛ تفاوتی در کد ما ایجاد نمی کند. Django ORM کار ترجمه کد از زبان برنامه نویسی پایتون، به زبان دیتابیس ها را به راحتی مدیریت می کند و این بسیار شگفت انگیز است.
چالشی که این سه دیتابیس برای ما ایجاد می کنند این است که اگر بخواهید یک production environment را بر روی کامپیوتر خود (local computer) ایجاد کنید؛ باید هر کدام از این دیتابیس ها را نصب و بصورت محلی (local) آن ها را اجرا نمایید و ما دقیقا می خواهیم همین عمل را انجام دهیم. در حالی که جنگو جزییات سوییچ بین دیتابیس ها را مدیریت می کند اما زمانی که شما از SQLite برای توسعه ی محلی (local development) و از دیتابیسی دیگر برای محصول نهایی استفاده می کنید؛ ممکن است به باگ های خیلی کوچک و اجتناب ناپذیر برخورد کنید که پیدا کردن و رفع آن ها ممکن است شما را به دردسر بیندازد. بنابراین استفاده از یک دیتابیس هم برای توسعه ی محلی و هم برای محصول نهایی راه حل بهتری است.
در این فصل ابتدا ما یک پروژه ی جنگو را با استفاده از دیتابیس SQLite توسعه می دهیم و سپس بر روی مباحث Docker و PostgreSQL سوییچ می کنیم.
ابتدا با استفاده از command line بر روی پوشه ی code در desktop سوییچ کنید. شما این کار را می توانید با دو روش انجام دهید. یا با تایپ کردن cd ..
می توانید از Desktop/code/hello
به Desktop/code
هدایت شوید و یا با تایپ
cd ~/Desktop/code/
می توانید به آدرس مورد نظرتان هدایت شوید. سپس یک directory به اسم postgresql
بسازید.
Command Line
$ cd ..
$ mkdir postgresql && cd postgresql
حالا جنگو را نصب کنید، shell
را راه اندازی کنید و یک پروژه ی کوچک به نام postgresql_project
بسازید. یادتان باشد که .
را در آخر command وارد کنید.
Command Line
$ pipenv install django==2.2.7
$ pipenv shell
(postgresql) $ django-admin startproject postgresql_project .
حالا می توانیم دیتابیس را migrate
کنیم تا آن را راه اندازی نماییم و سپس با استفاده از ،runserver
سرور محلی (local server) را راه اندازی کنیم.
به طور کلی، من اجرای migrate را بر روی پروژه های جدید پیشنهاد نمی کنم؛ مگر این که مدل اختصاصی کاربر پیکربندی شده باشد. در غیر این صورت، جنگو، دیتابیس را به مدل داخلی کاربر متصل می کند که در این حالت اصلاح و تغییر آن در ادامه ی این پروژه، کار دشوار و پیچیده ای خواهد بود. این مبحث را بصورت جزیی تر در فصل 3 بیان خواهیم کرد. چون قصد اصلی ما در این فصل صرفا پیاده سازی هدفمان می باشد؛ بنابراین استفاده از مدل پیش فرض کاربر یک استثنا می باشد.
Command Line
(postgresql) $ python manage.py migrate
(postgresql) $ python manage.py runserver
اگر دستورات به درستی اجرا شوند، شما به آدرس http:https://127.0.0.1:8000/ در مرورگرتان هدایت می شوید.شاید نیاز باشد که شما حداقل یک بار صفحه را ریفرش کنید اما بعد از این کار، شما باید با صفحه ی خوش آمد گویی جنگو مواجه شوید.
سرور محلی (local server) را با control + c
متوقف کنید و سپس با دستور ls
همه ی فایل ها و directory ها را لیست کنید.
Command Line
(postresql) $ ls
Pipfile Pipfile.lock db.sqlite3 manage.py postgresql_project.
برای سوییچ به داکر، ابتدا با تایپ exit
از محیط مجازی (virtual environment) خارج شده و سپس فایل هایی با اسم Dockerfile
و docker-compose.yml
ایجاد کنید. این فایل ها بترتیب، Docker image
و container
را کنترل می کنند.
Command Line
(postgresql) $ exit
$ touch Dockerfile
$ touch docker-compose.yml
Dockerfile
زیر، همانند فایلی است که در فصل اول مورد استفاده قرار گرفته بود.
Dockerfile
# Pull base image
FROM python:3.7
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# Set work directory
WORKDIR /code
# Install dependencies
COPY Pipfile Pipfile.lock /code/
RUN pip install pipenv && pipenv install --system
# Copy project
COPY . /code/
حالا image اولیه را با استفاده از دستور docker build .
ایجاد نمایید.
آیا متوجه شدید که Dockerfile
این بار image را بسیار سریع تر ایجاد کرد؟ این اتفاق به این دلیل است که داکر در همان ابتدا، در کامپیوتر شما بصورت محلی به دنبال یک image خاص می گردد. اگر image مورد نظر را بصورت محلی پیدا نکرد؛ سپس آن را دانلود می کند. چون بسیاری از این image ها از فصل قبل بر روی کامپیوتر بوده اند، بنابراین داکر نیازی نمی بیند که آن ها را دوباره دانلود نماید.
حالا نوبت به فایل docker-compose.yml
می رسد؛ که این فایل نیز همان فایلی است در فصل اول مورد استفاده قرار گرفته بود.
docker-compose.yml
version: '3.7'
services:
web:
build: .
command: python /code/manage.py runserver 0.0.0.0:8000
volumes:
- .:/code
ports:
- 8000:8000
این بار container را با حالت تفکیک شده راه اندازی می کنیم؛ که در این حالت نیاز به یکی از دو فلگ (flag) -d
یا -detach
داریم. لازم بذکر است که این دو فلگ کار مشابه ای را انجام می دهند.
command line
$ docker-compose up -d
حالت تفکیک شده، container را در پس زمینه اجرا می کند. این بدان معنی است که ما می توانیم تنها از یک command line استفاده کنیم، بدون این که نیازی به command line دیگری باشد. این مهم باعث می شود که وقت ما بیهوده صرف سوییچ کردن بین command line ها نشود. از طرفی دیگر بدی این روش این است که اگر اروری بوجود آید؛ این ارور همیشه نمایش داده نمی شود. بنابراین اگر،در برخی موارد، صفحه ی نمایش کامپیوتر شما با این کتاب همخوانی نداشت؛ با تایپ کردن docker-compose logs
خروجی فعلی را چاپ کنید و خطا ها و ارور های آن را برطرف نمایید.
به احتمال زیاد شما با پیام Warning: Image for service web was built because it did not already exist
در زیر command مواجه خواهید شد. داکر بصورت اتوماتیک یک image درون container ساخته است. همانطور که در ادامه ی این کتاب خواهیم دید؛ اضافه کردن فلگ --build
، زمانی که پکیج های نرم افزار (software package) آپدیت هستند، لازم است. چرا که داکر، بصورت پیش فرض، بدنبال کپی حافظه ی محلی (local cached copy) نرم افزار می گردد و از آن برای ارتقا عملکرد استفاده می کند.
برای این که اطمینان پیدا کنیم که همه چیز بدرستی کار می کند به آدرس http:https://127.0.0.1:8000/ در درون مرورگرتان برگردید؛ صفحه را ریفرش کنید تا دوباره با صفحه ی خوش آمد گویی جنگو مواجه شوید.
از آن جایی که از داکر استفاده می کنیم، ما باید دستورات سنتی را با docker-compose exec [service]
آغاز کنیم؛ که در آن اسم سرویس را مشخص می نماییم. برای مثال، برای ساختن یک اکانت superuser، به جای وارد کردن python manage.py createsuperuser
،دستور آپدیت شده بصورت زیر می باشد(با استفاده از سرویس web
).
Command Line
$ docker-compose exec web python manage.py createsuperuser
برای یوزرنیم، sqliteadmin, [email protected]
را به عنوان ایمیل آدرس و پسورد را بنا بر میل خودتان انتخاب کنید. من معمولا از testpass123
استفاده می کنم.
پس از اینکار به قسمت ادمین در آدرس http:https://127.0.0.1:8000/admin بروید و سپس وارد شوید.
Django admin login
سپس شما به صفحه ی اصلی ادمین هدایت می شوید. دقت کنید که sqliteadmin
، اسم یوزرنیم شما می باشد.
Django sqliteadmin
اگر شما بر روی دکمه ی Users
کلیک نمایید؛ شما به صفحه ی یوزر ها هدایت می شوید که در آنجا می توانیم تایید کنیم که تنها یک یوزر ساخته شده است.
Admin Users page
در اینجا مهم است که یکی از جنبه های دیگر جنگو را مورد توجه قرار دهیم: تا به اینجا ما در حال آپدیت کردن دیتابیس – فایل db.sqlite3
- در درون داکر بودیم. این بدان معنی است که فایل db.sqlite3
به مرور زمان در حال تغییر بوده است. به لطف volumes
که بر روی docker-compose.yml
سوار است؛ تغییرات هر فایل در فایل db.sqlite3
در درون کامپیوتر شما نیز کپی می شود. شما می توانید از داکر خارج شوید؛ shell
را راه اندازی، و سرور را با python manage.py runserver
راه اندازی کنید. و شما در آخر همان admin login مشابه را مشاهده می کنید. این اتفاق به این دلیل است که دیتابیس SQLite زیرین یکی است.
حالا زمان آن فرا رسیده است که برای ادامه ی پروژه به سمت PostgreSQL سوییچ کنیم. این کار سه قدم دیگر را در بر می گیرد:
- وفق دهنده ی دیتابیس (database adapter)
Psycopg2
را نصب کنید. با این کار python می توانید با PostgreSQL ارتباط برقرار کند. - کانفیگ DATABASE را در فایل
settings.py
آپدیت کنید. - PostgreSQL را بصورت محلی اجرا نمایید.
حالا اجرای docker container را با استفاده از docker-compose down
متوقف کنید.
command line
$ docker-compose down
Stopping postgresql_web_1 ... done
Removing postgresql_web_1 ... done
Removing network postgresql_default
سپس در درون فایل docker-compose.yml
یک سرویس به نام db
اضافه نمایید. این بدان معنی است که دو سرویس کاملا جداگانه وجود خواهد داشت؛ هر کدام دارای یک container که در درون Docker host اجرا می شوند:سرویس web
برای سرور محلی Django و سرویس db
برای دیتابیس PostgreSQL.
PostgreSQL دارای آخرین ورژن است؛ یعنی ورژن 11. اگر ورژن خاصی را مشخص نکنیم و به جای آن تنها از postgres
استفاده کنیم؛ آخرین نسخه ی PostgreSQL دانلود خواهد شد. بنابراین در آینده ممکن است PostgreSQL نسخه ی 12 برای شما نصب گردد که پیش نیاز های دیگری خواهد داشت.
در آخر، خط depends-on
را به سرویس web
اضافه خواهیم کرد؛ چرا که این سرویس بنا بر نوع دیتابیس اجرا می گردد. این بدان معنی است که db
قبل از web
شروع بکار خواهد کرد.
docker-compose.yml
version: '3.7'
services:
web:
build: .
command: python /code/manage.py runserver 0.0.0.0:8000
volumes:
- .:/code
ports:
- 8000:8000
depends_on:
- db
db:
image: postgres:11
حالا docker-compose up -d
را اجرا نمایید. با این کار، docker image ما rebuild خواهد شد و دو container را راه اندازی می کند. یکی PostgreSQL را در db
اجرا می کند و دیگری جنگو web
سرور را اجرا می کند.
در این جا مهم است به این نکته دقت کنیم که production database هایی نظیر PostgreSQL، file-based نیستند. این دیتابیس کاملا در سرویس db
اجرا شده و بصورت موقت می باشد. زمانی که docker-compose down
را اجرا می کنیم، تمام دیتای درون آن از بین می رود. این کاملا بر خلاف کد ما در درون web container می باشد که دارای یک volumnes
سوار بر سینک محلی (sync local) و کد داکر است.
در فصل بعدی یاد خواهیم گرفت که چگونه volumes mount را برای سرویس db
اضافه نماییم که اطلاعات دیتابیس را مقاوم نگه دارد.
با استفاده از text editor، فایل postgresql-project/settings.py
را باز نمایید و سپس به سمت پایین scroll نمایید تا با کانفیگ DATABASE
مواجه شوید. Setting فعلی به شرح زیر است:
Code
# postgresql_project/settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
جنگو بصورت پیش فرض از sqlite3
به عنوان موتور دیتابیس (database engine) استفاده می کند. آن را db.sqlite3
نام گذاری کنید و در BASE_DIR
قرار دهید؛ که این دایرکتوری، دایرکتوری پروژه ی ما می باشد.
از آن جایی که ساختار دایرکتوری بگونه ای است که باعث سردرگمی می شود، ضمن این نکته لازم است که دایکرتوری پروژه جایی است که فایل های postgresql_project
، manage.py
، Pipfile
، Pipfile.lock
و db.sqlite3
در آن واقع هستند.
command line
(postgresql) $ ls
Dockerfile Pipfile.lock docker-compose.yml postgresql_project
Pipfile db.sqlite3 manage.py
برای این که به PostgreSQL سوییچ کنیم، ما کانفیگ ENGINE را آپدیت خواهیم کرد.PostgreSQL موارد NAME
، USER
، PASSWORD
، HOST
و PORT
را از شما می خواهد.
برای راحتی عبارت postgresql
را برای سه مورد اول وارد نمایید؛ db
که اسم service set در فایل docker-compose.yml
می باشد را روبروی HOST وارد نمایید. در آخر PORT
را 5432
قرار دهید؛ که port پیش فرض PostgreSQL می باشد.
Code
# postgresql_project/settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'postgres',
'USER': 'postgres',
'PASSWORD': 'postgres',
'HOST': 'db',
'PORT': 5432
}
}
اگر پیج را ریفرش کنید، با ارور مواجه خواهید شد.
Django error
چه اتفاقی افتاده است؟ زمانی که ما Docker را در حالت detached mode همراه با فلگ -d
اجرا می کنیم، ممکن است خطا در وهله ی اول برای ما واضح و روشن نباشد. نوبت آن است که log ها را بررسی نماییم.
Command Line
$ docker-compose logs
...
web_1 | django.core.exceptions.ImproperlyConfigured: Error loading psycopg2
module: No module named 'psycopg2'
همانطور که می بینید، خروجی های زیادی تولید شده اند؛ اما اگر به قسمت پایین web_1 دقت نمایید، خطا به ما می گوید که درایور Psycopg
هنوز نصب نشده است.
PostgreSQL دیتابیسی است که از آن می توان تقریبا در هر زبان برنامه نویسی استفاده کرد. اما به این مورد فکر کنید که چگونه یک زبان برنامه نویسی می تواند به دیتابیس متصل گردد.
جواب database adaptor است. دقیقا کاری است که Psycopg انجام می دهد. Psycopg معروف ترین و پرطرفدار ترین database adaptor در python می باشد. اگر مایل هستید که اطلاعات بیشتری را در مورد کارکرد Psycopg بدانید، می توانید توضیحات بیشتری را در سایت رسمی دنبال نمایید.
Psycopg را می توان با Pipenv نصب کرد. در command line دستور زیر را وارد نمایید. با این کار Psycopg در درون Docker host نصب می گردد.
Command Line
$ docker-compose exec web pipenv install psycopg2-binary==2.8.3
ممکن است بپرسید که چرا Psycopg باید در Docker نصب گردد؟ جواب کوتاه این است که نصب پکیج های نرم افزاری در درون Docker و rebuild کردن image از اول باعث می شود که از اختلالات احتمالی ناشی از Pipfile.lock
در امان بمانیم.
تولید Pipfile.lock
شدیدا به نوع سیستم عامل (os) مورد استفاده بستگی دارد. ما کل سیستم عامل را در درون Docker تعریف می کنیم؛ به علاوه ی Python 3.7
. اما اگر شما Psycopg2 را بصورت محلی بر روی کامپیوترتان نصب کنید، که دارای environment متفاوتی می باشد؛Pipfile.lock
منتج شده بصورت دیگری خواهد شد. بعد از آن volumes
بر روی فایل docker-compose.yml
سوار می گردد؛ که بصورت اتوماتیک فایل های سیستمی Docker و فایل های محلی را سینک می کند. این کار باعث می گردد که Pipfile.lock
محلی ورژن را در درون Docker باز نویسی کند. بعد از آن، docker container سعی در اجرا کردن فایل اشتباهی Pipfile.lock
می کند!
یک راه برای جلوگیری کردن از این مشکل، نصب کردن پکیج های نرم افزاری در درون Docker به جای نصب محلی می باشد.
اگر شما صفحه را ریفرش نمایید، دوباره با خطا روبرو خواهید شد. اجازه دهید log ها را چک کنیم.
Command Line
$ docker-compose logs
دقیقا مشابه قبلی است. چرا این اتفاق رخ داد؟ Docker بصورت اتوماتیک image ها را cache می کند. مگر این که به دلایل عملکردی، موردی تغییر کند. ما این را می خواهیم که همراه با Pipfile
و Pipfile.lock بصورت اتوماتیک image مان rebuild شوند. اما بدلیل این که خط آخر Dockerfile
، COPY . /code/
می باشد؛ فقط فایل ها کپی می شوند. بنابراین image در ساختار داخلی خودش rebuild نمی کند. مگر این که ما این کار را بصورت دستی و با اضافه کردن فلگ --build
انجام دهیم.
به عنوان یادآوری: زمانی که می خواهید یک پکیج نرم افزاری را اضافه کنید؛ ابتدا آن را در درون Docker نصب نمایید؛ Container ها را متوقف و بصورت دستی image را rebuild کنید. سپس Container را مجددا راه اندازی نمایید. در طول این کتاب، این چرخه را بار ها و بار ها تکرار خواهیم کرد.
Command Line
$ docker-compose down
$ docker-compose up -d --build
اگر صفحه را مجددا ریفرش کنید، در آدرس http:https://127.0.0.1:8000/ با صفحه ی خوش آمد گویی جنگو مواجه خواهید شد. به این دلیل که این بار، جنگو، بصورت موفقیت آمیز بوسیله ی Docker به PostgreSQL متصل شده است.
با این حال از آنجا که ما از PostgreSQL استفاده می کنیم نه SQLite دیتابیس ما خالی است . اگر نگاه کنید گزارش های فعلی را دوباره با تایپ کردن
docker-compose logs
مشاهده خواهید کرد . شکایاتی مانند "شما 18 مهاجرت (migrations) های اعمال نشده دارید".
برای تقویت این نکته به admin در http:https://127.0.0.1:8000/admin/ مراجعه کنید و وارد شوید . آیا حساب کاربری superuser ما از sqliteadmin
و testpass123
کار می کند ؟
پاسخ منفی است ! ما در admin/
ارور ProgrammingError
میبینیم . برای حل کردن این مشکل , ما میتوانیم هم مهاجرت بکنیم و هم یک کاربر superuser
در داخل داکر بسازیم که به دیتابیس PostgreSQL دسترسی خواهد داشت .
$ docker-compose exec web python manage.py migrate
$ docker-compose exec web python manage.py createsuperuser
ما باید چگونه superuser را بنامیم (صدا بزنیم) ؟ بیایید از postgresqladmin
استفاده کنیم و برای آزمایش ایمیل را به [email protected]
و رمز عبور را به testpass123
تنظیم کنید .
در مرورگر خود به صفحه admin
در آدرس http:https://127.0.0.1:8000/admin/ بروید و اطلاعات superuser جدید را وارد کنید .
در گوشه سمت راست بالا نشان می دهد که ما با postgresadmin
وارد شده ایم نه با sqliteadmin
همچنین ، می توانید بر روی برگه کاربران در صفحه اصلی کلیک کرده
و از قسمت کاربران دیدن کنید تا مشاهده کنید که تنها کاربر ما حساب superuser جدید است .
به یاد داشته باشید که ظرف در حال اجرا را با دستور docker-compose down
متوقف کنید .
$ docker-compose down
بیاید تغیرات خود را با راه اندازی اولیه Git برای این پروژه جدید ذخیره کنیم , اضافه کردن تغیرات خود, و از جمله یک پیام کامیت.
$ git init
$ git status
$ git add .
$ git commit -m 'ch2'
کد های رسمی فصل ۲ در گیتهاب در دسترس است .
هدف اصلی این فصل نشان دادن نحوه همکاری docker و PostgreSQL در پروژه جنگو بود . تغییر دیتابیس SQLite به PostgreSQL برای بسیاری از توسعه دهندگان در ابتدا یک جهش ذهنی است .
نکته کلیدی این است که با داکر دیگر نیازی به حضور در یک محیط مجازی محلی نداریم. داکر محیط مجازی ما و ... و همچنین دیتابیس ما و در صورت تمایل بیشتر . هاست داکر اساسا جایگزین سیستم عامل ما شده است و در داخل آن میتوانیم چندین ظرف (containers) را اجرا کنیم , مانند برنامه وب و دیتابیس ما , که همه می توانند جدا شوند و جداگانه اجرا شوند .
در فصل بعدی ما پروژه کتابفروشی آنلاین خود را آغاز می کنیم . بزن بریم!