سوال مصاحبه ارثبری

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

خروجی کد از قرار زیره:

بله سورپرایز شدیم. حالا برای اینکه بفهمیم چرا اینطور شد کد زیر رو اجرا میکنیم:

و خروجی کد:

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

اما متغیری که توی کلاس تعریف شده داستانش فرق میکنه. توی کلاس A پدر متغیر val به یه آدرس توی حافظه اشاره میکنه. کلاس B و C که فرزند هستن به همون آدرس حافظه اشاره مکنه. حالا وقتی B.val رو تغییر میدیم (از اونجایی که integer توی پایتون immutable هست) اون خونه حافظه تغییر نمیکنه بلکه یک خونه جدید با مقدار جدید ساخته میشه و B.val به اون اشاره میکنه. حالا باید خونه قبلی حافظه رو پاک کنه اما چون 2 تا pointer دیگه بهش وصله اونو پاک نمیکنه (Garbage collector وقتی اون خونه حافظه رو پاک میکنه که هیچ pointer ای به اون اشاره نکنه)

حالا داستان دوم. با این منطق اگر A.val رو تغییر بدیم باید C.val تغییر نکنه چون A.val به آدرس جدید اشاره میکنه ولی خانه قبلی پاک نمیشه چون هنوز C.val به خانه قبلی اشاره میکنه. اما مساله دیگه ای هم هست این جا از ارثبری استفاده کردیم و C فرزند A هست پس حالا که A.val به خانه جدیدی از حافظه اشاره میکنه این pointer رو توی فرزندانش هم عوض میکنه و بازم C.val و A.val به یک خانه جدید یکسان اشاره میکنن. ولی B.val دیگه این اتفاق براش نمیوفته چون از قبل به خونه دیگه ای اشاره میکنه (تغییر کرده) اگر پایتون این رو جوری دیگه پیاده سازی میکرد قطعا به مشکل میخوردیم.

واقعا سوال باحالی بود.

افزودن فیلد به مدل با کمترین impact

یه مدل داریم تو جنگو که پروفایل شرکت های مختلف توی اون ذخیره میشه:

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

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

و اینطوری ازش استفاده کنم

نصب gitlab روی داکر

میخایم gitlab رو روی داکر بالا بیاریم. برای این کار از docker compose استفاده میکنیم. فایل compose.yml زیر رو مینویسیم:

این یه کانتینر از gitlab-ce میسازه که پورتهاش رو به هم مپ میکنه.

environment variable ای که استفاده شده GITLAB_HOME رو لازمه با دستور زیر تعریف کنیم

یا کار بهتر اینه که فایل custom.sh رو توی /etc/profile.d/ میسازیم و اون اکسپورت رو توش میزاریم.

قسمت environment توی فایل کامپوز برای متغیرهای توی خود کانتینر هست و ربطی به سرور اصلی نداره.

آخر هم دستور زیر کانتینر رو میسازه.

دستورات اولیه نصب Odoo

دستورات تست شده برای نصب Odoo رو توی این پست میارم:

Coroutine و Awaitables

Coroutine در واقع یک تابع هست که میتونیم اونو pause کنیم. فرض کنیم دو تا تابع داریم و میخایم کنترل اجرا بین این دو تا جابجا بشه مثل ping pong مثلا تابع اول چند خط رو اجرا کنه بعد کنترل رو به تابع دوم بده و اون چند خط اجرا کنه و … در واقع دو تا routine که اینجا بهشون میگیم تابع با هم cooperate کنن.

برای تریفش توی پایتون از async کلیدواژه استفاده میکنیم. که اون وقت اون تابع coroutine میشه. میتونیم هر جای کد coroutine ازش بخایم وایسه تا اجرای یه تابع دیگه تموم شه برای این کار await میکنیم.

مقایسه Celery و Channels و Singnal

در جدول زیر مقایسه Celery و Django Channels و Django Signals رو میبینیم

Here’s a comparison of Django Channels, Django Signals, and Celery, presented in a tabular format, focusing on their use cases, features, and suitable scenarios:

Feature/AspectDjango ChannelsDjango SignalsCelery
Primary Use CaseReal-time communication, WebSockets, and long-lived connectionsDecoupled event handling within Django applicationsAsynchronous task queue/job queue for running background tasks
CommunicationWebSockets, HTTP2, server-sent eventsIn-process communication within the same Django appDistributed communication, allowing task distribution across multiple workers
ConcurrencyHandles concurrent connections and async processingSynchronous, works within the Django request-response cycleHandles concurrent tasks, designed for parallel processing
ScalabilityScalable for handling many concurrent connectionsLimited by the Django processHighly scalable, can distribute tasks across multiple servers
Real-time UpdatesYes, supports real-time updatesNoNo, tasks are run asynchronously but not in real-time
Event HandlingHandles real-time eventsHandles events within the Django lifecycle (e.g., model save, user login)Handles events as background tasks
Persistent ConnectionsYes, supports long-lived connections (e.g., chat apps)No, operates within the request-response cycleNo, designed for discrete, short-lived tasks
IntegrationIntegrates with WebSockets, WebRTC, etc.Integrates with Django ORM and lifecycle hooksIntegrates with message brokers (RabbitMQ, Redis, etc.)
Typical Use CasesChat applications, live notifications, live dashboardsSending signals on model changes, user actionsSending emails, processing data, generating reports, scheduled tasks
ExampleLive chat app with instant messagingLogging changes to models when savedSending periodic email reports, processing user-uploaded files in the background
Fault ToleranceBasic fault tolerance, relies on infrastructureBasic, depends on Django’s fault toleranceHigh fault tolerance, can retry failed tasks, distributed task execution
DependenciesRedis, RabbitMQ (for channel layers)None beyond DjangoMessage broker (e.g., RabbitMQ, Redis), result backend (optional)
Ease of UseRequires understanding of asynchronous programmingSimple to use with DjangoRequires setup of a message broker and understanding of task queues

Detailed Use Case Examples

  1. Django Channels:
    • Chat Application: Users can send and receive messages in real-time, with updates being pushed to all connected clients instantly.
    • Live Notifications: Real-time notifications for events such as new comments, likes, or mentions in a social media application.
    • Live Dashboard: Real-time updates of metrics and analytics in a monitoring dashboard.
  2. Django Signals:
    • Model Change Notifications: Automatically trigger a function to log changes or send an email when a model instance is saved or deleted.
    • User Activity Tracking: Capture user login, logout, or profile update events to maintain an activity log.
    • Post-Processing: Perform additional actions after saving a model, such as updating related records or sending a confirmation email.
  3. Celery:
    • Email Sending: Send emails asynchronously to avoid blocking the main thread during the request-response cycle.
    • Data Processing: Handle large data processing tasks, such as generating reports or processing user-uploaded files, in the background.
    • Scheduled Tasks: Run periodic tasks like clearing expired sessions, generating nightly reports, or updating external APIs.

By comparing these tools, you can choose the most appropriate solution based on the specific requirements and characteristics of your project.

database_sync_to_async و async_to_sync

کاربرد این دوتابع در django channels هست. به طور کلی وقتی توی یه consumer که به صورت Asynchronous تعریف شده بخایم از یک تابع Synchronous استفاده کنیم باید اون رو به Asynchronous تبدیل کنیم و برعکس و کاربرد این دو تابع همینه.

میدونیم که ORM جنگو Synchronous هست بنابراین وقتی توی یه کانسومر Asynchronous ازش استفاده میکنیم باید اونو توی database_sync_to_async بزاریمو

میدونیم که Channel Layer ها ذاتا Asynchronous هستند و همه متدهای اون مثل self.send group_send و group_add همگی Async هستند بنابراین اگر بخایم توی یه کلاس sync ازشون استفاده کنیم باید از async_to_syn استفاده بشه.

لازم به ذکر است که channels.db.database_sync_to_async یک wrapper از asgiref.sync.sync_to_async هست که بعد از کانکشن دیتابیس رو هم clean میکنه

برای استفاده از اون بهتره اول خود query رو توی یه تابع بنویسیم بعد اون تابع رو بدیم به این تابع مثل:

یا

Channel Layers توی Django Channels چیه؟

توی Django Channels هر instance توی یک پروسه اجرا میشه. اگر بخایم پیامی بین پروسه های مختلف برنامه یعنی به instance های مختلف ارسال بشه باید از مکانیسم Channels layer استفاده کنیم. یعنی اگر بخایم پیامی به شخصی دیگه ارسال بشه یا به گروه خاصی ارسال بشه باید از این مکانیسم استفاده کرد.

یعنی اگر بخایم بخش های مختلف برنامه یا consumer های مختلف با هم صحبت کنن باید از Layers استفاده کنیم.

Gunicorn یا Daphne?

Gunicorn و Daphne هر دو وب سرور برای فریمورکهای پایتون هستن که روی لینوکس نصب میشن.

فرقشون چیه؟

Gunicorn برای درخواست های Syncronous و HTTP request ها استفاده میشه. تنظیمات WSGI باید ست شه.

Daphne برای درخواست های Asynchronous و Websocket Request ها کاربرد داره و تنظیمات ASGI باید روشون انجام شه.

از کدوم استفاده کنیم؟

از Daphne میشه برای هر دو استفاده کرد. اگر درخواست های Async ندارید میتونید از Gunicorn استفاده کنید. یه راه حل دیگه هم برای سولوشنهای بزرگ اینه که Nginx رو استفاده کنید و تنظیم کنید که درخواست های Websocker رو به Daphne و درخواست های HTTP رو به Gunicorn بفرسته.

وب سوکت WebSocket چیه

WebSocket یه پروتکل وبه که امکان ارسال پیام دو طرفه بین کلاینت و سرور رو میده.

توی HTTP :

  1. درخواست یه http.request از کلاینت به سرور ارسال میشه
  2. سرور یه کانکشن باز میکنه و اطلاعات لازمه رو توی scope اون سشن مینویسه
  3. پاسخ http.response را به کلاینت ارسال میکنه
  4. و کانکشن رو میبنده

اما توی وب سوکت فرق داره

  1. کلاینت یه درخواست میفرسته و اطلاعات scope رو سمت سرور ارسال میکنه
  2. سرور یه ایونت chat.recieved_message اجرا میکنه و میتونه جواب بده و اگر جواب بده ایونت chat.send_message رو اجرا کرده
  3. هر کدوم از اونها میتونن کانکشن رو ببندن

آیا Websocket ها Synchronous هستن یا Asynchronous؟

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