سعید حسنی
سعید حسنی
خواندن ۸ دقیقه·۴ سال پیش

چجوری یه پکیج برا جنگو بنویسم (گام به گام)

خروجی کار: پکیج مون رو با pip دریافت کنین :)
خروجی کار: پکیج مون رو با pip دریافت کنین :)

مقدمه:

ممکنه شما یه برنامه نوشتین که خیلی باحاله و قصد دارین با همه به اشتراک بذارین?یا اینکه اصلا نخواین به کسی بدین و برا خودتون نگهش دارین?. به هر حال به نظرم باید اونها رو به یه بسته جدا تبدیل کنین و تو پروژه ها تون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یه کوئری به دیتابیس بزنیم و میانگین معدل هر مدرسه به همراه معدل و نام بچه ها رو برگردونیم.

فایل models.py که مدل هامون رو داره نگه میداره
فایل models.py که مدل هامون رو داره نگه میداره
  • ساختنview:

حالا یهviewآماده کنیم که اطلاعات تمام بچه های مدرسه رو برامون تو خروجی برگردونه:

فایل veiws.py که به درخواست هامون پاسخ میده
فایل veiws.py که به درخواست هامون پاسخ میده
  • رجیستر کردن مدل ها در پنل ادمین جنگو:

میتونیم مدل ها رو توی پنل ادمین جنگو رجیستر کنیم.

فایل admin.py که کارش رجیستر کردن مدل در پنل ادمین هست.
فایل admin.py که کارش رجیستر کردن مدل در پنل ادمین هست.
  • ایجادurlهای برنامه:

حالا میتونیم از طریق urls به veiwکه طراحی کردیم، دست پیدا کنیم.

فایل urls.py در پوشه my_app
فایل urls.py در پوشه my_app

برای اینکه بتونیم از طریق وبسایت مون به اینurlها دسترسی داشته باشیم نیازه که اونها رو بهurlهای پروژه معرفی کنیم.

فایل urls.py تو پوشه djangoProject
فایل urls.py تو پوشه djangoProject

بریم دیتابیس رو بسازیم و قبلش یادتون نره که my_appرو به INSTALLED_APPSهامون توی فایل settings.pyاضافه کنیم.

INSTALLED_APPS = [ ... 'my_app', ]
  • ساختmigrationsها و ایجاد دیتابیس:
python manage.py makemigrations my_app
python manage.py migrate
  • مقدار دهی اولیه دیتابیس با استفاده از fixtures

یه سری دیتای فیک با استفاده ازfixtures من تولید کردم که لینک هم اینجا تو گیت هاب گذاشتم. بعد با دستور زیر این دیتاها رو که تو فایلmy_app/fixtures/fake.jsonهست، به دیتابیس اضافه میکنیم.

python manage.py loaddata my_app/fixtures/fake.json
  • مشاهده خروجی بر روی مرورگر

اگه سرور رو با دستورpython manage.py runserverاجرا بگیریم، با رفتن به آدرسschool/info اطلاعات مدرسه ها رو میتونیم ببینم.

http://127.0.0.1:8000/school/info/
http://127.0.0.1:8000/school/info/
  • تست برنامه با unittest

قبل از اینکه پکیج مون رو بسازیم نیاز داریم که یه سری تست هامون رو انجام بدیم، تا اون چیزی که از برنامه انتظار دارم رو برآورده کنه. برای همین از کتابخونهunittestیه تست ساده مینویسیم که مثلاidمدرسه اول مون رو برگردونیم و با توجه به اینکه میدونیم میانگین معدل بچه ها در اون کلاس 19.24 هست، بررسی میکنیم که آیا واقعا این میانگین بدست میاد یا نه؟

تست معدل کلاس یک با unittest
تست معدل کلاس یک با unittest

برای اجرای تست از دستور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رو پیدا کنه. بنابراین اینطوری براش کد بنویسین:

boot_my_app.py برای اتصال اپ با پروژه
boot_my_app.py برای اتصال اپ با پروژه
توابع settings.configure وdjango.setupبرای اینکه بتونیم یه اپ رو خارج از پروژه به پروژه مون متصل کنیم، طراحی شدند. میتونین برین این لینک و بیشتر در موردشون بدونین.

هر مقداری که تو تابعsettings.configureهست، دقیقا همون چیزی هست که ما تویsettings.pyپروژه داشتیم. یعنی اینکه اپ شما وقتی به یه پروژه اضافه شد، چه تنظیماتی داشته باشه رو، اینجا مشخص میکنین.

  • افزودنmigrationهای اپلیکشین

گام بعدی اینکه که از طریق management command پایتون،migrationهای درون پوشهmy_appرو بسازیم. من فایلmakemigrations_my_app.pyرو اینطوری تنظیم میکنم:

makemigrations_my_app.py
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هست.

check_my_test.py فایل
check_my_test.py فایل

برای اینکه این فایل رو اجرا بگیریم، کافیه که تو پوشه این فایل دستورpython check_my_test.pyرو بزنین وmainصدا زده میشه و تابعget_suiteفراخونی میشه.


قسمت سوم (آماده سازی جهت انتشار بر روی PyPi)


میخوایمmy_appرو بذاریم رویPyPiتا به صورت پکیج قابل استفاده برای همه باشه. برا این کار یه فایل setup.cfgو یه فایلsetup.pyتوی یه پوشه یکسان و در کنار پوشهmy_appایجاد کنیم.

ساختار پوشه ها و فایل ها تا الان
ساختار پوشه ها و فایل ها تا الان

مقادیری که تو فایلsetup.cfgوارد میکنین تو سایتPyPiبرا همه قابل نمایش هستن، مثلا اسم پکیج چیه؟ و عنوانش چیه؟ یا یه سری توضیحات در موردش بدین یا اینکه آدرس سورس کدش مثلا کجاست، یه سری توضیحات در موردش میدین، برای چه ورژنهای پایتونی مناسب هست و ... برای اینکه توضیحات در مورد پروژه بدین، یه فایلREADME.rstبسازین و هر توضیحی در مورد پکیج میخواین بدین، مثلا چی هست، و چجوری نصب میشه و ... (ازmarkdownهم میتونین کم و بیش استفاده کنین).

من فایلsetup.cfgرو به صورت زیر پیاده سازی کردم و همونطور که قبلا اشاره شد، در واقع میاد پکیج مون رو درPyPiتوصیف میکنه.

فایل setup.cfg
فایل setup.cfg

من اسم پکیج روmpkg_school_infoگذاشتم. مقدارinstall_requires داره میگه مثلا وقتی باpipداریم پکیج رو نصب میکنیم، ورژن پکیجهایی که استفاده کردیم، چی باشن وtest_suiteهم میگه برو تست ها رو از فایلcheck_my_test.pyکه قبلا نوشتم بخون.

- اسمی که انتخاب میکنین نباید قبلا استفاده شده باشه. پس قطعا شما نمیتونین اسمی که من انتخاب کردم رو بزنین. یه اسم به دلخواه خودتون بهش بدین.
- یه پارامتر دیگه (البته بهشون میگنentry) به نامtest_requiresداریم که مشخص میکنین تو تست ها از چه ورژن هایی از یه پکیج ها استاده کردین.

حالا بریم فایلsetup.pyرو هم مقدار دهی کنیم که خودش اتوماتیک میکه اطلاعاتsetup.cfgرو میخونه و استفاده شون میکنه.

فایل setup.py
فایل setup.py
  • تست لوکال با 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میگه که این یه نصب لوکال هست.

محل قرار گرفتن requirements.txt
محل قرار گرفتن requirements.txt

وقتی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ایجاد کنیم و محیط هایی که پکیج مون رو میخوایم داخلش تست کنیم، مشخص میکنیم.

محتویات فایل tox.ini
محتویات فایل tox.ini

مثلا من اینجا چهار تا محیط تست آماده کردم. محیط پایتون 3.6 و محیط پایتون 3.7 با جنگو 3 و محیط پایتون 3.8 و 3.9 با جنگو 2.1.5) . تو قسمتcommandsمشخص میکنیم که تست ها رو از فایلsetup.pyاجرا کنه. و همچنین تو فایلsetup.cfgمشخص میکنم که تست ها حالا اتوماتیک انجام بشه و نیازی به نوشتنpython check_my_test.pyنباشه:

اضافه شدن test_suite به فایل setup.cfg
اضافه شدن test_suite به فایل setup.cfg

تو نسخه جدید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.

هدف فقط این بود که اگه کسی یه پکیج عالی داره و فک میکنه میتونه مفید باشه برا جامعه برنامه نویسی، از این امر غافل نشه.


بهترین باشید

با❤️نوشته شده برای شما.

pythondjangoجنگوpipپایتون
یه برنامه نویس خوشحال ツ
شاید از این پست‌ها خوشتان بیاید