بهترین روش استفاده از فرم ها توی جنگو

فرم در جنگو قابلیت های زیادی داره و من توی پروژه های مختلف هر بار چالش هایی داشتم که به مرور متوجه شدم که این قابلیت ها خیلی میتونه کمک کنه.

1.فرض کنید که توی یک فرم از یک مدل ما میخایم که یک فیلد رو نشون کاربر نده و اون رو پنهان کنه برای اون کار باید از HiddenInput استفاده کرد link_form.fields['provider'].widget = forms.HiddenInput()

2.فرض کنید به همون فیلد فرم میخایم یه مقدار اولیه بدیم که دیگه نیاز نباشه کاربر فیلدی رو پر کنه. link_form.initial = {'provider': svc}

3.هر فیلد توی یک فرم یک queryset داره که متونید اون رو تغییر بدید و مثلا یه فیلدش رو کم کنید یا sort کنید. self.fields['provider'].queryset.order_by('name')

4.یکی از نکات مشکلسازی که وجود داره. اگر میخاید فیلدهای یک فرم رو توی view تغییراتی بهش بدید (از اون چیزی که کاربر وارد میکنه) باید اون فیلد رو save کنید و اون instance ساخته شده رو تغییر بدید و بعدش commit کنید. link_instance = link_form.save(commit=False)

5.اگر بخایم مقدار وارد شده توسط کاربر رو بگیریم باید از cleaned_data استفاده کنیم. link_form.cleaned_data['provider'].name

6.توی خود کلاس Form میتونید برای هر فیلد محدودیت هایی ایجاد کنید و در صورت تخطی از اون محدودیت ها ValidationError برگردونید.

Form و View ای که تمامی این موارد رو توش داره رو در کدهای زیر میبینید. هر بار نیاز به زدن چنین فرمهایی بودیم بد نیست یه سری به اینا بزنیم.

و فرم مربوطه

پیش‌نمایش در زبانهٔ تازه

یه وقتهایی چند فرم رو توی یک view میخایم داشته باشیم. برای اینکه بدونیم کدوم فرم سابمیت شده باید از کلید name توی input HTML استفاده کنیم. مثلا سابمیت به صورت زیره

حالا توی ویو باید به روش زیر بنویسیم:

به قسمت if name in request.POST دقت کنید.

اگر چند فرم داشته باشیم که فیلدهایی با اسم مشابه داشته باشند باید از prefix توی فرم جنگو استفاده کنیم. اینطوری برای فیلدهای هر فرم prefix متفاوتی خواهیم داشت.

دسترسی به پارامترهای URL از توی Template Django

اگر نیاز شد که به متغیرهای توی URL دسترسی پیدا کنیم، میتونیم ببینیم که توی request چی پیدا میشه. طبیعیه که هر چیزی که توی یک فرم توی مرورگر میبینیم باید توی request پیدا بشه. برای این مورد از request.resolver_match استفاده میکنیم مثل:

چند فرم و یک view در جنگو

توی یه اپلیکیشن یه فایل HTML داریم که سه تا فرم توشه ولی فقط با یه فرم داره رندر میشه. مشکل اینجاست که اینجوری پر از باگه. حتی ممکنه فرم اول رو سابمیت کنی کد فرم بعدی هم اجرا شه در حالی که اصلا لازم نیست هم از نظر performance ای یه کد اضافی داره اجرا میشه هم ممکنه دیتای فرم بعدی رو خراب کنه. برای اینکار به سه تا input فرم ها هر کدوم یه اسم یکتا دادم مثلا:

حالا توی فرم چک میکنیم که این فیلد توی request هست یا نه:

این جوری اگر درخواست POST باشه فقط اون فرمی که سابمیت شده کدش اجرا میشه و لازم هم نیست که برای هر فرم یک view جدا هم بنویسیم.

Signal Dispatcher در جنگو

Signal Dispatcher به ما این امکان رو میده که اگر توی یک اپلیکیشن اتفاقی افتاد اپلیکیشن های دیگه یا جاهای دیگه رو notify کنیم که اتفاقی انجام شه. مثلا فرض کنید که یک نفر به ما یک پیام میفرسته. اون وقت من باید یه نوتیفیکیشن دریافت کنم. روش کار این مدلیه که یک تکه کد میتونه sender باشه و چند تابع میتونن reciever باشن. در واقع وقتی یک چند تابع به یک event حساس هستن میتونن از این قابلیت استفاده کنن.

در پروژه ای وقتی مستندات در مدلی به نام Document ذخیره میشه و همزمان یک تاریخچه از این ذخیره شدن توی DocumentChangeHistory ذخیره میشه. مشکل اینه که میخام وقتی یک داکیومنت حذف میشه تاریخچه مرتبط با اون هم پاک شه.

برای این کار باید از Signal جنگو استفاده کرد. مثلا

توی کد بالا میگیم که اگر مدل Document چیزی ازش حذف شد. post_delete .اون وقت این بلاک کد اجرا بشه که در واقع از مدل تاریخچه ردیف مرتبط رو حذف میکنه.

باید توی app.py این رو تعریف کنیم. تا کار کنه.

لینک 1

لینک 2

تغییر مسیر ذخیره فایلهای داکر

بعد از نصب داکر فایلهای مورد استفاده خود داکر از قبلی image هایی که load میکنید توی مسیر /var/lib/docker ذخیره میشه که میتونه بعد از مدتی پر شه یا اصلا شاید بخاید جای دیگه ای ذخیره بشه. من خودم چون پارتیشن دیگه ای با حجم بیشتر دارم خواستم یه دایرکتوری دیگه رو map کنم به این دایرکتوری. البته روش های مختلفی وجود داره برای تغییر مسیر اما بهترین روش با کمترین impact این روشه:

کامل ترش رو میشه توی این لینک ببینیم.

پر شدن دایرکتوری و کرش کردن سرور

بعد از اینکه توی سرور امکان لاگین وجود نداشت (سرویس freeIPA) با کنسول وصل شدم و متوجه شدم که پارتیشن /var/log/auditlog پر شده. بله یک پارتیشن پر شده و سرویس از کار افتاده بعد از مدتی سرور ریبوت و دیگه بالا نمیومد.

با crontab -e یه جاب نوشتم که پریودیک این مسیر رو خالی کنه.

تنظیمات SElinux و مشکلاتی که برای داکر داره

امروز بعد از ریبوت سرور سرویس داکر دیگه بالا نیومد و ارور زیر رو میگیرفتیم.

توی اینترنت گفته بودن که درایور رو عوض کنیم و … اما در نهایت متوجه شدم چون selinux روی premisive بوده به داکر اجازه داده نمیشه که از دیسک با LVM کانفیگ شده استفاده کنه. اروری که نشون میده:

این ارور رو باید با کامند dockerd --debug ببینید

چطور فهمیدم که مشکل از selinux هست. سرور به دلایلی ریبوت شده بود و دیگه داکر بالا نیومده بود. قبلا یکی از دوستان selinux رو فعال کرده بود و طبیعتا تا بعد از ریبوت اعمال نمیشه. متوجه شدم احتمالا مشکل از اینه.

حالا توی فایل /etc/selinux/config کافیه اونو disable کنی و ریبوت. داکر دوباره start میشه.

استقرار پروژه جنگو با بهینگی بالا

در پروژه ای که در بستر داکر روی لینوکس پیاده سازی شده است نرم افزار از performance قابل قبولی برخوردار نیست. بخشی از نرم افزار با websocket کار میکند اما اکثرا در بستر http. دیتابیس، و بخشهای دیگر مثل celery, rabbitMQ و … هر کدام یک کانتینر هستند.

ایرادات استقرار یا deployment:

جنگو در یک کانتینر با دستور runserver اجرا شده است.

کل پروژه از Daphne به عنوان اپلیکیشن وب سرور ASGI استفاده میکند در حالی که بخش کوچکی از نرم افزار asyn است و با websocket کار میکند.

وب سروری مثل Nginx یا Apache وجود ندارد که فایلهای استاتیک را ارائه دهد.

بهتر بود در بخشهای Sync از اپلیکیشن سروری مثل uWSGI که یک WSGI هست استفاده میشد.

بنابراین معماری به شکل زیر تغییر یافت:

NGINX درخواست های websocket رو به Daphne و سایر درخواست ها را به uWSGI ارسال میکند. فایل های کانفیگ از قرار زیر است.

compose.yml

nginx.conf

DockerfileNginx

DockerfileNasim

لینک های مرتبط:

لینک 1

لینک 2

لینک 3

لینک 4

لینک 5

افزایش ظرفیت پارتیشن ها توی LVM

توی یه سرور RHEL که از LVM برای مدیریت دیسک و پارتیشن استفاده میشه، لازم شد که حجم رو افزایش بدیم. یه دیسک sdb داریم که میخایم به 2 تا پارتیشن اضافه اش کنیم. LVM این کار رو انجام میده بدون اینکه پارتیشن ها نیاز به فرمت داشته باشن و داده های قدیمی از دست برن. یه چیزی مثل سرویس های ابری که هر وقت بخایم منابع اضافه میکنیم.

قبل از افزایش حجم وضعیت اینطور بود:

همینطور که میبینید /home و / هر کدوم 40 گیگابایت هستن که باید زیاد بشن.

میبینیم که /dev/sdb به دیکسها اضافه شده اما پارتیشن بندی نشده.

همینطور که میبینیم sdb اصلا به هیچ volume group ای از LVM اضافه نشده پس معلومه که قابل استفاده است.

میبینیم که یه logical volume داریم vg0 که یه دونه pv هم داره که در واقع همون sda3 هست که قبلا دیدیم و بازم مطمثن شدیم که sdb استفاده نمیشه

یه نگاه مجدد به logical volume ها میندازیم.

با دستور بالا sdb رو به یه phisical volume تعریف کردیم که بتونیم توی LVM ازش استفاده کنیم. در وا

میبینیم که sdb اضافه شده.

حالا sdb رو به volume group vg0 اضافه میکنیم که بتونیم به logical volume ها اضافه اش کنیم.

همینطور که میبینیم اضافه شده. الان vg0 حدود یک ترابایت خالی داره.

حالا 512 گیگابایتش رو به / یا همون root اضافه کردیم.

از فضای خالی باقیمانده بقیه شو به /home اضافه کردیم.

همینطور که میبینید / و /home حجمشون زیاد شده.

بلاکچین – یک تراکنش چطور انجام میشود؟

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

توی بیتکوین وقتی که تراکنش را شروع میکنیم، این تراکنش به یک صف موجود در استخر حافظه بلاکچین قرار میگیرد و بعد از اینکه یک بلاک را پر کرد منتظر میماند تا یک ماینر آن بلاک را بردارد و پردازش کند (اینکه یه ماینر اون رو برداره مثل کاری هست که معدنچی میکنه)

ماینرهای مختلف بلاکهای مختلف را برمی دارند. بعد هر کدام شروع میکنند به انجام دادن یک عملیات رمزنگاری بر روی بلاک (که بهش میگن proof of work یا POW)

بعد از اینکه این یک بلاک توسط یک ماینر رمز میشود و صحت سنجی می شود به عنوان یک بلاک قانونی در بلاکچین در نظر گرفته میشود.

بلاک های قانونی به هم زنجیر میشوند و به رشته ای از بلاک های قانونی غیرقابل تغییر شناخته میشوند.

در آخر تراکنش در کیف پول انجام میگیرد.