
ممکنه شما یه برنامه نوشتین که خیلی باحاله و قصد دارین با همه به اشتراک بذارین?یا اینکه اصلا نخواین به کسی بدین و برا خودتون نگهش دارین?. به هر حال به نظرم باید اونها رو به یه بسته جدا تبدیل کنین و تو پروژه ها تونDon't Repeat YourselfیاهمونDRYرو رعایت کنین و کدهای تکراری ننویسین.
تو این نوشته میخوایم یه پروژه ساده جنگو بیاریم بالا و باهاش یه کم بازی کنیم و آخر تبدیل به یه پکیج کنیم.
اینجا یاد میگیریم که:
- چجوری یه پروژه جنگو بیارین بالا
- مدلها رو تو جنگو تعریف کنین و تو پنل ادمین جنگو رجیسترشون کنیم.
- باfixturesآشنا میشین.
- وunittestانجام بدین.
- چجوری یک اپ رو از پروژه جدا کنین و برا اتصال دوباره اش چجوری کانفیگش کنین.
- چجوری باpipاپی که جدا کردین رو به صورت محلی نصب کنین.
- باtoxآشنا میشین تا اپ رو بر روی محیطهای مختلف تست بگیرین.
- در نهایتم که باtwineاپ رو بذارین تویPyPiتا همه ازش استفاده کنن.
بزن بریم پس✌️.
- سورس کد برنامه رو میتونین از اینجا دریافت کنین.
- تصاویری که داخل شون کد هست، من هایپرلینک کردم(رنگ آبی) به گیت هاب تا بتونین کد رو هم ببینین.
- سعی کردم آموزش فقط یاد دادن ساخت یه پکیج نباشه و یه سری توصیه ها برای بهتر کد نویسی هم داشته باشه.
میخوایم یه پروژه خیلی ساده جنگو رو بالا بیاریم. پس دستورات زیر رو اجرا بگیرین تا پروژه ایجاد بشه:
python -m pip install Djangodjango-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_apppython 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 setuptoolspython setup.py sdistpython setup.py bdist_wheeltwine 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.
هدف فقط این بود که اگه کسی یه پکیج عالی داره و فک میکنه میتونه مفید باشه برا جامعه برنامه نویسی، از این امر غافل نشه.
بهترین باشید
با❤️نوشته شده برای شما.