Skip to content

Latest commit

 

History

History

02- PostgreSQL

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

فصل دوم : PostgreSQL

یکی از تفاوت های اصلی بین یک اپلیکیشن ابتدایی و یک اپلیکیشن آماده به کار (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.

Docker

برای سوییچ به داکر، ابتدا با تایپ 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

حالت تفکیک شده (Detached Mode)

این بار 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

حالا زمان آن فرا رسیده است که برای ادامه ی پروژه به سمت 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 اضافه نماییم که اطلاعات دیتابیس را مقاوم نگه دارد.

Settings

با استفاده از 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 هنوز نصب نشده است.

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 جدید را وارد کنید .

image

در گوشه سمت راست بالا نشان می دهد که ما با postgresadmin وارد شده ایم نه با sqliteadmin همچنین ، می توانید بر روی برگه کاربران در صفحه اصلی کلیک کرده و از قسمت کاربران دیدن کنید تا مشاهده کنید که تنها کاربر ما حساب superuser جدید است .

image

به یاد داشته باشید که ظرف در حال اجرا را با دستور docker-compose down متوقف کنید .

$ docker-compose down

گیت

بیاید تغیرات خود را با راه اندازی اولیه Git برای این پروژه جدید ذخیره کنیم , اضافه کردن تغیرات خود, و از جمله یک پیام کامیت.

$ git init
$ git status
$ git add .
$ git commit -m 'ch2'

کد های رسمی فصل ۲ در گیتهاب در دسترس است .

نتیجه

هدف اصلی این فصل نشان دادن نحوه همکاری docker و PostgreSQL در پروژه جنگو بود . تغییر دیتابیس SQLite به PostgreSQL برای بسیاری از توسعه دهندگان در ابتدا یک جهش ذهنی است .

نکته کلیدی این است که با داکر دیگر نیازی به حضور در یک محیط مجازی محلی نداریم. داکر محیط مجازی ما و ... و همچنین دیتابیس ما و در صورت تمایل بیشتر . هاست داکر اساسا جایگزین سیستم عامل ما شده است و در داخل آن میتوانیم چندین ظرف (containers) را اجرا کنیم , مانند برنامه وب و دیتابیس ما , که همه می توانند جدا شوند و جداگانه اجرا شوند .

در فصل بعدی ما پروژه کتابفروشی آنلاین خود را آغاز می کنیم . بزن بریم!