ممکنه شما یه برنامه نوشتین که خیلی باحاله و قصد دارین با همه به اشتراک بذارین?یا اینکه اصلا نخواین به کسی بدین و برا خودتون نگهش دارین?. به هر حال به نظرم باید اونها رو به یه بسته جدا تبدیل کنین و تو پروژه ها تونDon't Repeat Yourself
یاهمونDRY
رو رعایت کنین و کدهای تکراری ننویسین.
تو این نوشته میخوایم یه پروژه ساده جنگو بیاریم بالا و باهاش یه کم بازی کنیم و آخر تبدیل به یه پکیج کنیم.
اینجا یاد میگیریم که:
- چجوری یه پروژه جنگو بیارین بالا
- مدلها رو تو جنگو تعریف کنین و تو پنل ادمین جنگو رجیسترشون کنیم.
- باfixtures
آشنا میشین.
- وunittest
انجام بدین.
- چجوری یک اپ رو از پروژه جدا کنین و برا اتصال دوباره اش چجوری کانفیگش کنین.
- چجوری باpip
اپی که جدا کردین رو به صورت محلی نصب کنین.
- باtox
آشنا میشین تا اپ رو بر روی محیطهای مختلف تست بگیرین.
- در نهایتم که باtwine
اپ رو بذارین تویPyPi
تا همه ازش استفاده کنن.
بزن بریم پس✌️.
- سورس کد برنامه رو میتونین از اینجا دریافت کنین.
- تصاویری که داخل شون کد هست، من هایپرلینک کردم(رنگ آبی) به گیت هاب تا بتونین کد رو هم ببینین.
- سعی کردم آموزش فقط یاد دادن ساخت یه پکیج نباشه و یه سری توصیه ها برای بهتر کد نویسی هم داشته باشه.
میخوایم یه پروژه خیلی ساده جنگو رو بالا بیاریم. پس دستورات زیر رو اجرا بگیرین تا پروژه ایجاد بشه:
python -m pip install Django
django-admin startproject my_app_project
حالا یک اپ رو ایجاد کنین که قراره بعدا به عنوان پکیج مون باشه:
python manage.py startapp my_app
در ادامه دو تا مدل پیاده سازی میکنیم. مدل ها در جنگو محتویات دیتابیس مون رو میسازن. مدل مدرسه و دانش آموز که با همدیگه رابطه یک به چند دارن و هر مدرسه میتونه چندین دانش آموز داشته باشه و هر دانش آموز متعلق به یک مدرسه است. برا دانش آموزا نام و معدل شون رو نگه میداریم و هدف این هست که بعدا در قسمتveiws
یه کوئری به دیتابیس بزنیم و میانگین معدل هر مدرسه به همراه معدل و نام بچه ها رو برگردونیم.
view
:حالا یهview
آماده کنیم که اطلاعات تمام بچه های مدرسه رو برامون تو خروجی برگردونه:
میتونیم مدل ها رو توی پنل ادمین جنگو رجیستر کنیم.
url
های برنامه:حالا میتونیم از طریق urls
به veiw
که طراحی کردیم، دست پیدا کنیم.
برای اینکه بتونیم از طریق وبسایت مون به اینurl
ها دسترسی داشته باشیم نیازه که اونها رو بهurl
های پروژه معرفی کنیم.
بریم دیتابیس رو بسازیم و قبلش یادتون نره که my_app
رو به INSTALLED_APPS
هامون توی فایل settings.py
اضافه کنیم.
INSTALLED_APPS = [ ... 'my_app', ]
migrations
ها و ایجاد دیتابیس:python manage.py makemigrations my_app
python manage.py migrate
یه سری دیتای فیک با استفاده ازfixtures
من تولید کردم که لینک هم اینجا تو گیت هاب گذاشتم. بعد با دستور زیر این دیتاها رو که تو فایلmy_app/fixtures/fake.json
هست، به دیتابیس اضافه میکنیم.
python manage.py loaddata my_app/fixtures/fake.json
اگه سرور رو با دستورpython manage.py runserver
اجرا بگیریم، با رفتن به آدرسschool/info
اطلاعات مدرسه ها رو میتونیم ببینم.
unittest
قبل از اینکه پکیج مون رو بسازیم نیاز داریم که یه سری تست هامون رو انجام بدیم، تا اون چیزی که از برنامه انتظار دارم رو برآورده کنه. برای همین از کتابخونهunittest
یه تست ساده مینویسیم که مثلاid
مدرسه اول مون رو برگردونیم و با توجه به اینکه میدونیم میانگین معدل بچه ها در اون کلاس 19.24 هست، بررسی میکنیم که آیا واقعا این میانگین بدست میاد یا نه؟
برای اجرای تست از دستورpython manage.py test
کمک میگیریم و خروجی نشون میده که این تست موفق بوده:
Creating test database for alias 'default'...
System check identified no issues (0 silenced)..
----------------------------------------------------------------------
Ran 1 test in 0.023s
OK
Destroying test database for alias 'default'...
پیشنهاد: توصیه میکنم برای تست ها ازpytest
استفاده کنین. برای شروع هم به نظرم این لینک خیلی خوبه.
تا اینجا اومدیم اپ مون (my_app
) رو ساختیم و دیگه وقتش رسیده که روی پای خودش وایسته. پس باید از پروژه که تا حالا ازش نگهداری میکرده!، جداش کنیم? و بتونه با کل دنیا حرف بزنه [خیلی غم انگیزه ولی برا خودش خوبه].بالاخره باید یه جایی خودش رو نشون بده، نمیشه که همش وابسته باباش باشه!?.
اول بیانmy_app
رو روی سیستم خودمون از پروژه جدا کنیم و یه سری تست ها روش انجام بدیم. برا همین من ساختار پوشه بندی رو به صورت زیر تغییر میدم. اومدم پوشهmy_app
رو در کنارmy_app_project
قرار دادم(قبلا داخلش بود).
پس یه فایل به اسم boot_my_app.py
رو تو پوشه اصلی مون که الان شده (school_info
) ایجاد میکنیم. در واقع به جنگو میخوایم بگیم که چجوریmy_app
رو پیدا کنه. بنابراین اینطوری براش کد بنویسین:
توابعsettings.configure
وdjango.setup
برای اینکه بتونیم یه اپ رو خارج از پروژه به پروژه مون متصل کنیم، طراحی شدند. میتونین برین این لینک و بیشتر در موردشون بدونین.
هر مقداری که تو تابعsettings.configure
هست، دقیقا همون چیزی هست که ما تویsettings.py
پروژه داشتیم. یعنی اینکه اپ شما وقتی به یه پروژه اضافه شد، چه تنظیماتی داشته باشه رو، اینجا مشخص میکنین.
migration
های اپلیکشینگام بعدی اینکه که از طریق management command
پایتون،migration
های درون پوشهmy_app
رو بسازیم. من فایلmakemigrations_my_app.py
رو اینطوری تنظیم میکنم:
اول اومدیم تابع boot_my_app
که توی فایل قبلی طراحیش کردیم، صدا زدیم و اپ رو به پروژه اضافه کردیم و تو خط بعدی، command
ای که استفاده کردم دقیقا معادل دستورpython manage.py makemigratins my_app
هست.
خب حالا بیان تست کنیم که آیا تست ها هم با موفقیتpass
میشن و مشکلی نباشه. پس یه فایل دیگه به اسم check_my_test.py
ایجاد میکنم و داخلش همه تست های درونmy_app
رو اجرا میگیرم. این فایل دقیقا معادلpython manage.py test
هست.
برای اینکه این فایل رو اجرا بگیریم، کافیه که تو پوشه این فایل دستورpython check_my_test.py
رو بزنین وmain
صدا زده میشه و تابعget_suite
فراخونی میشه.
میخوایمmy_app
رو بذاریم رویPyPi
تا به صورت پکیج قابل استفاده برای همه باشه. برا این کار یه فایل setup.cfg
و یه فایلsetup.py
توی یه پوشه یکسان و در کنار پوشهmy_app
ایجاد کنیم.
مقادیری که تو فایلsetup.cfg
وارد میکنین تو سایتPyPi
برا همه قابل نمایش هستن، مثلا اسم پکیج چیه؟ و عنوانش چیه؟ یا یه سری توضیحات در موردش بدین یا اینکه آدرس سورس کدش مثلا کجاست، یه سری توضیحات در موردش میدین، برای چه ورژنهای پایتونی مناسب هست و ... برای اینکه توضیحات در مورد پروژه بدین، یه فایلREADME.rst
بسازین و هر توضیحی در مورد پکیج میخواین بدین، مثلا چی هست، و چجوری نصب میشه و ... (ازmarkdown
هم میتونین کم و بیش استفاده کنین).
من فایلsetup.cfg
رو به صورت زیر پیاده سازی کردم و همونطور که قبلا اشاره شد، در واقع میاد پکیج مون رو درPyPi
توصیف میکنه.
من اسم پکیج روmpkg_school_info
گذاشتم. مقدارinstall_requires
داره میگه مثلا وقتی باpip
داریم پکیج رو نصب میکنیم، ورژن پکیجهایی که استفاده کردیم، چی باشن وtest_suite
هم میگه برو تست ها رو از فایلcheck_my_test.py
که قبلا نوشتم بخون.
- اسمی که انتخاب میکنین نباید قبلا استفاده شده باشه. پس قطعا شما نمیتونین اسمی که من انتخاب کردم رو بزنین. یه اسم به دلخواه خودتون بهش بدین.
- یه پارامتر دیگه (البته بهشون میگنentry
) به نامtest_requires
داریم که مشخص میکنین تو تست ها از چه ورژن هایی از یه پکیج ها استاده کردین.
حالا بریم فایلsetup.py
رو هم مقدار دهی کنیم که خودش اتوماتیک میکه اطلاعاتsetup.cfg
رو میخونه و استفاده شون میکنه.
pip
پکیج مون آماده ست. فقط بیان که به صورت لوکال یه تست بگیریم که مطمین بشیم همه چی اوکی هست و میتوینم اون رو بر رویPyPi
قرار بدیم.
برای اینکه تو پوشهschool_info/my_app_project
یه فایل به اسمrequirements.txt
درست کنین و برین تو پوشه که این فایل هست و دستورpip install -r requirements.txt
پکیج رو نصب کنیم.
# school_info/my_app_project/requirements.txt -e ../../school_info
تو این فایل-e
در واقع داره بهpip
میگه که این یه نصب لوکال هست.
وقتیpip install -r requirements.txt
رو زدین خروجی زیر براتون میاد و یعنی همه چی خوبه?:
Obtaining file:///C:/Users/RAYCA.CO/Project/school_info(from -r requirements.txt (line 3))
Requirement already satisfied: Django>=2.0 in c:\python39\lib\site-packages (from school-info==1.0.0->-r requirements.txt (line 3)) (2.1.5)
...
Installing collected packages: school-info
Running setup.py develop for school-info
Successfully installed school-info
tox
جهت تست بر روی محیط های مختلفقبل از اینکه پکیج رو برای همه دنیا به اشتراک بذاریم و بدیمش دست این و اون تا ازش کار بکشه?، نیاز هست که روی محیط های مختلف تست بشه. از همه مهمتر اینکه ما دوست نداریم هر دفعه که تست بخوایم بگیرم هی باید دستورpython check_my_test.py
رو بزنیم. پس بذاریم اینا خودش اتوماتیک انجام بشه و یهCI/CD
کوچیک برا خودمون داشته باشیم. برای همین ما ازtox
(و این لینک) استفاده میکنیم. برای این کار لازمه که یه فایل به نامtox.ini
در کنار فایلsetup.cfg
ایجاد کنیم و محیط هایی که پکیج مون رو میخوایم داخلش تست کنیم، مشخص میکنیم.
مثلا من اینجا چهار تا محیط تست آماده کردم. محیط پایتون 3.6 و محیط پایتون 3.7 با جنگو 3 و محیط پایتون 3.8 و 3.9 با جنگو 2.1.5) . تو قسمتcommands
مشخص میکنیم که تست ها رو از فایلsetup.py
اجرا کنه. و همچنین تو فایلsetup.cfg
مشخص میکنم که تست ها حالا اتوماتیک انجام بشه و نیازی به نوشتنpython check_my_test.py
نباشه:
تو نسخه جدیدsetup.cfg
مستقیما اومدیم تابعget_suite
رو از فایلcheck_my_test.py
فراخونی کردیم.
PyPi
(دیگه وقتشه)? اول از همه برین تو سایت PyPi و ثبت نام تون رو تموم کنین. خیلی ساده ست و کمتر از یک دقیقه. یهusername
وpassword
ازتون میخواد و یه ایمیل معتبر بهش بدین.
Twine
برای آپلودحالا وقتش رسیده که پکیج مون رو بذاریم تویPyPi
. ابزارهای مختلفی برای آپلود وجود داره، من از ابزار Twine استفاده میکنم. با چهار خط کد زیر:
python -m pip install -U wheel twine setuptools
python setup.py sdist
python setup.py bdist_wheel
twine upload dist/*
وقتی دستور twine
رو بزنین ازتون username
و password
تون رو میخواد و دیگه تموم شد. تبریک میگم بهتون. شما دیگه یه خیرخواه جهانی شدین(منظورم خَیِّر هست)?.
حالا وقتی یکی اون سر دنیا یه برنامه پایتون باز کنه، میتونه پکیج منو با دستور pip install mpkg-school-info
دریافت کنه و هر چقدر دلش بخواد میتونه ازش کار بکشه?:
Collecting mp-school-info
Downloading mp_school_info-1.0.0-py3-none-any.whl (2.4 kB)
...
Installing collected packages: mp-school-info
Successfully installed mp-school-info-1.0.0
اومدیم پروژه جنگومون رو بالا آوردیم. یه اپ داخلش ساختیم به اسمmy_app
. برنامه رو اجرا گرفتیم و دیدیم همه چی اوکی هست. بعد اپ رو از پروژه جدا کردیم و برای اینکه دوباره به همدیگه وصلشون کنیم، یه سری کافیگ ها انجام دادیم باboot_my_app
وsetup
. در نهایت هم باtwine
فرستادیمش براPyPi
.
هدف فقط این بود که اگه کسی یه پکیج عالی داره و فک میکنه میتونه مفید باشه برا جامعه برنامه نویسی، از این امر غافل نشه.
بهترین باشید
با❤️نوشته شده برای شما.