جنگولرن
3.79K subscribers
288 photos
73 videos
31 files
553 links
آموزش Django و بستگان
Download Telegram
🧨 شناخت و پیشگیری از یکی از رخنه (Exploit) های متداول وب اپ‌ها: XSS


📃 به عنوان توسعه‌دهنده وب بهتره سعی کنیم تا کدی که توسعه میدیم تا حدامکان #Secure (امن) باشه و در مقابل رخنه‌ها و حملات ساده و متداول آسیب پذیر (#vulnerable) نباشه (و شانس رخنه رو کم کنیم).

استفاده موفق از آسیب پذیری Cross-site-scripting یا XSS باعث میشه اپ شما کد های مخرب یا ناخواسته #Javascript رو برای دیگر کاربران نمایش بده و اجرا کنه.

🎃 کد جاوا اسکریپ مخرب میتونه:
اطلاعات کاربران رو بدزده (یا زمینه‌ساز حملات پیچیده‌تری بشه)
فعالیت هایی رو انجام بده که ناخواسته اند و مورد تایید یا نیاز کاربر نیستند.

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

⬅️ به صورت خیلی خلاصه،‌ اگر کد شما هر محتوا یا ورودی که از سمت کاربر بیاد رو بدون "تمیز کردن” و بررسی صحت (#Validation) ذخیره کنه یا نمایش بده،‌ این رخنه میتونه براش اتفاق بیفته.

⚔️ به عنوان اولین لایه پیشگیری، در وب فریمورک #Django ، فرض براینه که هرچه که در context برای رندر شدن در اختیارش میگذارید، "ناامن” است و با کمک متد html.escape() همه کاراکتر های html را به معادلشون تبدیل میکنه.
مثلا:
<img src="https://lnkd.in/eE-enrjb"/>
به
&lt;img src=&quot;https://lnkd.in/eUCsnhnx;
تبدیل میشه.

🌪 اما متاسفانه این کافی نیست:
1- این پیشفرض فقط شامل Django template engine میشود، مثلا اگر Angular یا React یا Vue استفاده میکنید،‌ احتمالا شامل این موضوع نیستید.

2- افزونه یا اپ های شخص ثالثی که استفاده میکنیم ممکنه شامل این آسیب پذیری باشند.

3- اگر از ()mark_safe یا همون "Safe string" ها در متدها و #template ها استفاده میکنید،‌ دیگه خروجی شما شامل این تبدیل نمیشه. (پیشنهاد میکنم تاجایی که میتونید از این متدها استفاده نکنید.)

4- بعضی از attr ها یا روش استفاده ما از متغیرها در template، ممکنه شامل این پیشفرض نشوند یا دورش بزنند،
مثلا:
<img src={{ profile_photo_url }}>
میتونه با سواستفاده تبدیل شه به
<”img src=”/img/home-bg.jpg onload=alert(1)>

5- کاراکتر backtick و همینطور دیتایی که با base64 انکود بشه escape نمیشود، در واقع محتوایی که کاراکترهای quote یا html نداشته باشه چیزی برای escape شدن نداره،‌ مثلا:
<script>spinner.minimum = {{ minimum_price }} ;</script>
میتونه تبدیل شه به:
<script>spinner.minimum = 0; alert(1) ;</script>

سعی میکنم در پست دیگه ای، با جزئیات بیشتری روش‌ها و راههایی که میتونیم تا حد ممکن از XSS در اپ Django مان پیشگیری (#mitigate ) کنیم رو جمع آوری کنم و بنویسم.

لینک مقاله اصلی که ازش الهام گرفتم:
https://lnkd.in/erpqQREc

از لینکدین Alireza Amouzadeh
تشکر از @alireza_amouzadeh

لینک:
https://www.linkedin.com/posts/alireza-amouzadeh_xss-exploitation-in-django-applications-activity-6948679390654291968-6_IX?utm_source=linkedin_share&utm_medium=member_desktop_web
👍8👎1
Forwarded from سید فرندز / برنامه نویسی / هک و امنیت / تکنولوژی (Reza Amin)
#django
#python

Django Lifecycle

این پکیج چیه کاربردش چیه؟

تفاوت اصلی میان Django Lifecycle و سیگنال‌ها (Signals) در Django این است که Django Lifecycle یک رویکرد ساختارمندتر و اصولی‌تر را برای مدیریت چرخه عمر اجزا فراهم می‌کند. با استفاده از Django Lifecycle، شما می‌توانید کدهای مربوط به هر مرحله از چرخه عمر را به صورت مستقیم در کلاس‌های مدل یا دیگر اجزا تعریف کنید و این اجزا را به یک مدل متصل کنید. این اجازه را به شما می‌دهد که کدهای مربوط به هر مرحله را در یک مکان مرتبط و خوانا نگه دارید و از تفکیک بخش‌ها بهره‌مند شوید.

مثالی از این بخش:

به عنوان مثال، فرض کنید که می‌خواهید هر زمان که یک مدل از نوع Post ایجاد یا به‌روزرسانی می‌شود، یک عملیات خاصی انجام دهید. با استفاده از Django Lifecycle، می‌توانید این عملیات را به صورت مستقیم در مدل Post تعریف کنید، به عنوان مثال:

from django.db import models
from django_lifecycle import LifecycleModel, hook

class Post(LifecycleModel):
title = models.CharField(max_length=100)
content = models.TextField()

@hook(BEFORE_UPDATE, when="content", has_changed=True)
def update_history(cls, old_instance, instance, field_name):
History.objects.create(
post=instance,
old_content=old_instance.content,
new_content=instance.content
)


post = Post.objects.create(title="Title", content="Content")


post.content = "Updated Content"
post.save()


در این مثال، یک مدل به نام Post تعریف شده است که از کلاس LifecycleModel ارث‌بری کرده است. با استفاده از دکوراتور hook، یک عملیات update_history برای مدل تعریف شده است. این عملیات هر زمان که محتوای یک نمونه از مدل تغییر می‌کند، یک رکورد جدید به جدول History اضافه می‌کند که تاریخچه تغییرات محتوا را ثبت می‌کند.

اطلاعات بیشتر راجبش:

https://github.com/rsinger86/django-lifecycle

@SEYED_BAX
👍81👎1
Forwarded from سید فرندز / برنامه نویسی / هک و امنیت / تکنولوژی (Reza Amin)
#python
#django

Generic relations

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

برای استفاده از generic relations در جنگو، شما باید از بسته contenttypes استفاده کنید که به شما اجازه می دهد از مدل ContentType استفاده کنید. این مدل اطلاعاتی درباره مدل های نصب شده در پروژه شما را ذخیره می کند و شما می توانید با استفاده از سه فیلد زیر یک رابطه عمومی با هر مدلی برقرار کنید:

یک فیلد ForeignKey که به مدل ContentType اشاره می کند (معمولا با نام content_type نامگذاری می شود).
یک فیلد PositiveIntegerField که کلید اصلی شیء مرتبط را ذخیره می کند (معمولا با نام object_id نامگذاری می شود).
یک فیلد GenericForeignKey که با استفاده از دو فیلد قبلی یک رابطه با شیء مرتبط ایجاد می کند (معمولا با نام content_object نامگذاری می شود).

مثلا فرض کنید ما یک مدل Comment داریم که می خواهیم بتواند به هر مدلی نظر دهد. ما می توانیم از generic relations برای این منظور استفاده کنیم:

from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType

class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
text = models.TextField()
date = models.DateTimeField(auto_now_add=True)

content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey()

خوب حالا اینطور می توانیم برای یک پست نظر ثبت کنیم :
post = Post.objects.get(pk=1) 

comment = Comment.objects.create(user=user, text="Nice post!", content_object=post)


اگر بخواهیم برای هر مدل دیگری هم که داریم مانند دوره آموزشی و مقاله یا هر مدل دیگری می توان از همین طریق نظر ثبت کرد .

@SEYED_BAX
👍6
جنگولرن
poetry.pdf
amjadi_precommit.pdf
4.1 MB
مطلبی از لینکدین Mohammad Amin Amjadi در رابطه با pre-commit

هر چقدر حجم کد بیشتر بشه یا تیم بزرگ‌تر
بشه چالش و دغدغه‌ها بیشتر هم میشن و ریوو کردن کد هم خودش به چالشی بزرگ‌تر تبدیل
میشه که روز به روز تایم بیشتری می‌گیره و از طرفی احتمال از قلم افتادن توافق‌ها،
نکات و اصول هم بیشتر میشه.

اینجاست که pre-commit به دادمون میرسه. در این پست در ابتدا سعی می‌کنم برخی از نکات و مواردی که خودم در تیم‌ها سعی می‌کردم رعایت کنم رو مطرح کنم [امیدوارم خودشون مفید و آموزنده باشن] و در نهایت به نحوه ستاپ و نوشتن pre-commit می‌پردازم و اشاره‌ای به pre-push در یه مثال کاربردی و مورد نیاز و همین طور جاب qa می‌کنم.


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


#git #gtilab #pre_commit #pre_push #python #django
6👍1
Forwarded from CodeCrafters (Behzad Azadi)
طراحی میکروسرویس با جنگو بخش دوم میکروسرویس چیست

میکروسرویس ها روندهای جدید توسعه هستند. امروزه شرکت ها معماری میکروسرویس را برای توسعه پروژه ترجیح می دهند. این یک راه حل بسیار فشرده برای یک پروژه است. مدیریت ماژول را آسان می کند و اجرای پروژه را سریعتر می کند. همچنین به توسعه سریعتر پروژه کمک می کند. این دلایل ذکر شده و بسیاری موارد دیگر باعث تقاضای میکروسرویس می شود.

معرفی میکروسرویس
برای میکروسرویس تعریف خاصی وجود ندارد و ممکن هست هر فرد به شکلی آنرا تعریف کند اما دو تعریف عمده آن به شکل زیر است
۱-میکروسرویس‌ها، سرویس‌های کوچک و مستقلی هستند که باهم کار میکنند
۲-میکروسرویس‌ها معماری سرویس گرا با زمینه‌های محدود هستند،بطور مستقل و با یک جزء دیگر در داخل ارتباط برقرار میکنند، این معماری بسیار خودکار و و سیستم‌های نرم افزاری را تکامل می‌دهد

بیایید ببینیم که آیا از معماری سرویس گرا (SOA) آگاه هستید یا خیر، سپس ماژولار بودن پروژه و ارتباط از طریق پیام را نیز می دانید. اگر از شیوه های DevOps آگاه هستید، در مورد استقرار خودکار نیز می دانید. هر دو بیشتر به رویکرد میکروسرویس نزدیک هستند.


سه اصل مهم در طراحی میکروسرویس‌ها:
۱-از میکروسرویس برای استقرار سیستم‌ها و پروژه‌های بزرگ استفاده کنید: برای تمام مقیاس‌های پروژه استفاده از میکروسرویس اشتباه است، بلکه مناسب پروژه‌های بزرگ است که مدیریت آن چالش برانگیز باشد، اما خود این موضوع هم سردرگم کننده هستش اینکه کدوم پروژه رو کوچیک، متوسط، بزرگ بنامیم، منتها اگر سیستم ما دارای بار بالایی از درخواست کاربر است و نیاز به مقیاس پذیری دارد، میتوانیم رویکرد میکروسرویس رو پیاده سازی کنیم(در حجم تعداد اپ نیز تعداد اپ بالا میتواند به این منزله باشد)

۲-این رویکرد هدف‌محور است: مهم نیست که وقتی با مشکل مواجه می‌شویم، باید از رویکرد میکروسرویس پیروی کنیم. امروزه بسیاری از متخصصان برای توسعه پروژه به این رویکرد اشاره می کنند زیرا هدف آنها تنها ارائه راه حل مشکلات نیست، همچنین برای دید بیشتر و حفظ سهولت در ماژول ها، چنین معماری را در عمل به کار می گیرند.

۳-قابلیت تعویض ماژول ها: در پروژه های توسعه یافته قبلی که با رویکرد میکروسرویس ساخته نشده اند، امکان تغییر هر جزء از پروژه کمتر است. قبل از تغییر سازنده کامپوننت باید در مورد تغییرات برنامه ریزی کند، وابستگی کد خاصی را پیدا کند، در صورت عدم موفقیت کد به هر دلیلی، مراحل بازگشت را فهرست کند و تاثیر کد را مشخص کند. پس از همه، تغییرات نیاز به انجام عملیات تست واحد و سپس آزمایش ادغام با کل محصول دارند


در برخی سناریوها، پروژه ها بسیار پیچیده هستند یا وابستگی‌های حیاتی شدیدی دارند، در چنین شرایطی در مورد مسائل آینده یا افزایش حجم بار، ما مجبوریم به جای تغییر، اجزا را حفظ کنیم، که این بزرگترین ضرر یک رویکرد است

در میکروسرویس ها، کل پروژه را بر اساس ماژول ها به یک جزء‌های کوچک تقسیم می کنیم. بطوری که امکان تکرارپذیری را بر روی هر جزء فراهم می کند که تعمیر و نگهداری پروژه رو راحتتر میکند

اپلیکیشن‌های میکروسرویس دارای بخش‌های مهم زیر هستند:
● به اندازه های کوچک تقسیم می شوند
● برای برقراری ارتباط نیازمند انتقال پیام هستند
● مقید به زمینه‌ها هستند
● توسعه انها بصورت مستقل هستش
● استقرار هر بخش مستقل هست
● متصل به کنترل کننده مرکزی نیستند
● ساخت ها توسط فرآیندهای خودکار مستقر می شوند

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

تمرکززدایی:به این معنی است کل کارهای داخلی پروژه شامل اجرای هر ماژول، مدیریت وظایف و جابجایی کامل پروژه دیگر توسط یک سیستم واحد، مدیریت و کنترل نمیشود

استقلال: به این معنی است که به تیم‌های توسعه خود برای تولید نرم افزار ایمان داشته باشیم

مزیت این دو رویکرد این است که تغییرات در نرم افزار آسانتر و سریعتر میشود و امکان تصمیم گیری سریعتر را فراهم میکند


ادامه در وبسایت

لینک وبسایت


چندتا نکته بگم
بخش دوم و سوم کتاب بشدت پربار و پر از تحربه‌هایی هست که در دنیای واقعی با اون سروکار داریم و درکی از مسائل برامون مشخص نبود، من سعی کردم در حد توان بخش‌های مهم رو برسونم


#microservice
#django

@code_crafters
👍1
یک نکته درباره primary key داخل #جنگو اینه که شما میتونید برای هر app به شکل خاص primary key خاص خودش رو داشته باشید. مثلا برای یک app از big integer استفاده کنید و برای یکی از UUID این رفتار رو برای هر اپ میتونید به مشخص کردن AppConfig.default_auto_field مشخص کنید. حالا #django این قابلیت رو هم بهتون میده که جدا از انتخاب دونه دونه برای هر اپ یک حالت گلوبال در نظر بگیرید که اگر مخصوص اپ ست نکرده باشید از اون استفاده میکنه که پیش‌فرض خود جنگو BigIntegerField در نظر میگیره و میشه با تغییر دادن DEFAULT_AUTO_FIELD داخل settings.py به شکل گلوبال تغییرش داد

@TorhamDevCH
👍1
این توانایی که خود #جنگو بهتون میده ولی از trick دیگه هم میتونید استفاده کنید و اون هم ساختن یک مدل ابسترکت و بقیه مدل‌ها ازش ارث ببرن.
مثال:

from django.db import models 
from uuid import uuid4


class Base(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)

class Meta:
abstract = True


از اونجایی که اگه داخل مدل #django فیلدی داشته باشید که داخلش primary_key مقدار True داشته باشه جنگو دیگه از اون مقدار دیفالتی که مشخص کردید (هردو حالت دو پیام بالا) استفاده نمیکنه و میاد از این فیلد استفاده میکنه. حالا شما میتونید بقیه مدل‌هاتون رو از این مدل ارث بری کنید و دیگه نگران مقدار id نباشید.

@TorhamDevCH
3👍3
خب من یک چیز خیلی جالب و در این حال گیج‌کننده درباره ORM #جنگو فهمیدم.

تو پیام قبلی گفتم که کوئری‌های #django دقیقا چه زمانی واقعا اجرا میشن. اما اینجا یک نکته دیگه‌ای هم هست، جنگو نتیجه کوئری‌هارو کش میکنه.

و این خیلیییییییییییی مهمه، یعنی بعضی از جاها که فکر میکنید جنگو قرار دیتابیس هیت کنه هیت نمیکنه و از کش استفاده میکنه و بعضی جاها که فکر میکنید قرار کش استفاده کنه واقعا هیت میکنه. دونستن و فهمیدن این که چه زمانی کش استفاده میکنه چه زمانی نه حدود ۲ ساعت از من زمان گرفت :) ولی تو این پست توضیح میدم چیزی که فهمیدم رو.

اول این قانون تو ذهنتون داشته باشید: هر چیزی که باعث ایجاد یک QuerySet جدید بشه، باعث هیت به دیتابیس خواهد شد اگر اون کوئری اجرا بشه.

به مثال زیر دقت کنید:

users = User.objects.all()
print(users)

if users:
for u in users:
print(u)


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

اما اگر من بگم فقط دو بار دیتابیس هیت میکنه چی؟

بزارید توضیح بدم. تو خط اول ما صرفا کوئری رو ساختیم و هیچ هیتی به دیتابیس نزدیم. تو خط دوم ما کوئری پرینت کردیم و اینجا اولین هیت به دیتابیس خورده میشه، ولی یک نکته اینجاست وقتی شما یک کوئری رو پرینت میکنید جنگو نمیاد کل کوئری رو اجرا کنه چون منطقی نیست!، مثلا اگر کوئری شما هزارتا خروجی داشته باشه شما اون هزارتا رو که داخل پرینت نمیخایید، در نتیجه جنگو فقط یک بخش از کوئری رو ران میکنه یا به عبارت دیگه از LIMIT استفاده میکنه!. تو این خط هیچ کش کردنی اتفاق نمیوفته(جلوتر میگم چرا)

خط بعدی ما از if استفاده کردیم و اینجا یک هیت دیگه به دیتابیس میخوره اما این‌بار کل کوئری اجرا میشه و اینجاست که جنگو ریزالت کوئری میگیره و داخل کش ذخیره میکنه. تو خط بعدی که اومدیم حلقه زدیم روی کوئری جنگو دیگه نمیاد به دیتابیس درخواست بزنه و از کش استفاده میکنه!

در نتیجه اینجا فقط ۲ بار دیتابیس هیت میخوره.

ادامه داخل پیام بعد...

@TorhamDevCH
👍9
یک مبحثی که خیلی وقت‌ها آدم‌های رو داخل #جنگو گیج میکنه موضوع Aggregation هستش. برای مثال کوئری پایین:


>>> from django.db.models import Avg, Max, Min
>>> Book.objects.aggregate(Avg("price"), Max("price"), Min("price"))
# {'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

خب این کوئری مشخصه چه کاری داره انجام میده، همه میتونن بفهمنش مخصوصا وقتی خروجی کوئری رو میبینن، اما اگر ازشون بپرسید خب Aggregation چی هستش هیچ ایده ای ندارن! و این ماجرا از ضعف در دانش SQL سر چشمه میگیره. چون خیلی از آدم‌هایی که دارن #django کار میکنن مستقیم سراغ جنگو اومدن و نرفتن چیزهای دیگه رو مطالعه کنن و یاد بگیرن.

اسم Aggregation داخل ORM جنگو مستقیما از SQL میاد. در SQL یک سری فانکشن وجود داره که بهشون Aggregation functions میگن و کارشون خلاصه سازی اطلاعات:
MIN() - returns the smallest value within the selected column
MAX() - returns the largest value within the selected column
COUNT() - returns the number of rows in a set
SUM() - returns the total sum of a numerical column
AVG() - returns the average value of a numerical column

و خب شما میتونید داخل کوئری‌های SQL ازشون استفاده کنید و دیتا خروجی رو خلاصه سازی کنید و یا یک آمار ازش دربیارید. مثلا میانگین قیمت کتاب‌های تو سال اخیر و ...
یک کوئری مثال برای Aggregation میتونه این باشه:

SELECT AVG(Price) as price_avg FROM Books WHERE puddate='2023-01-01';


خب از اونجایی که ORM جنگو در نهایت قرار کار همین SQL نوشتن برای شما انجام بده و کوئری‌ شمارو به SQL تبدیل کنه شما دقیقا همین کوئری میتونید داخل جنگو به این صورت بنویسید:


>>> from django.db.models import Avg
>>> from datetime import datetime
>>> Books.objects.filter(pubdate=datetime(2023, 1, 1)).aggregate(price_avg=Avg("price"))

میتونید لیست فانکشن‌های Aggregation خود SQL داخل این لینک ببینید و ساپورت جنگو هم میتونید داخل این لینک ببینید.

در نهایت از دانش SQL غافل نباشید و حتما یادش بیگیرید. هرچی بیشتر SQL بدونید زندگی راحت‌تری خواهید داشت.

@TorhamDevCH
4👍3
کوئری raw زدن در #جنگو

درسته #django یک ORM خوب داره و تقریبا تمام چیزهایی که لازم دارید رو ساپورت میکنه اما شما میتونید داخل جنگو مستقیم کوئری SQL ران کنید!

ران کردن این کوئری‌ها به صورت raw میتونه تو دو لایه در جنگو انجام بشه. یک در لایه مدل خودتون و دومی تو لایه پایین تر مستقیم با کانکشن دیتابیس.

تو حال اول جنگو سعی میکنه که خروجی SQL را براتون Map کنه و خروجی دوباره مدل براتون برگردونه حتی وقتی دارید raw میزنید مثال:

مدل فرضی:

class Person(models.Model):
first_name = models.CharField(...)
last_name = models.CharField(...)
birth_date = models.DateField(...)


کوئری مثال:


Person.objects.raw("SELECT * FROM myapp_person")


این کوئری دقیقا معادل objects.all() و جنگو خروجی رو بر اساس اسم فیلدها مپ میکنه به مدل. این مهمه ها! بر اساس اسم فیلد‍! یعنی شما میتونید کوئری رو حتی رو یک تیبل دیگه بزنید و تا زمانی که اسم فیلدا خروجیتون با مدل یکی باشه جنگو اون‌هارو مپ میکنه. مثال:


>>> Person.objects.raw(
... """
... SELECT first AS first_name,
... last AS last_name,
... bd AS birth_date,
... pk AS id,
... FROM some_other_table
... """
... )


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


>>> name_map = {"first": "first_name", "last": "last_name", "bd": "birth_date", "pk": "id"}
>>> Person.objects.raw("SELECT * FROM some_other_table", translations=name_map)


میتونید از پارامتر translations استفاده کنید برای اینکار.

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

برای مثال:


>>> for p in Person.objects.raw("SELECT id, first_name FROM myapp_person"):
... print(
... p.first_name, # This will be retrieved by the original query
... p.last_name, # This will be retrieved on demand
... )
...




برای مثال داخل این raw کوئری ما فیلد last_name رو انتخاب نکردیم. حالا چه اتفاقی افتاده؟ همچنان اگه شما فیلد last_name صدا بزنید مشکلی پیش نمیاد و دریافتش میکنید ولیییییی جنگو از اونجایی که اون فیلد داخل کوئری وارد نکرده بودید و خروجیش رو نداشته خودش میاد همون لحضه دوباره یک درخواست به دیتابیس میزنه و اون دریافت میکنه!


لایه خود کانکشن

اگه این مپینگ رو نمیخایید و کلا میخوایید یک کوئری مستقیم بزنید مثل زمانی که از یک کتابخونه معمولی تو پایتون برای دیتابیس استفاده میکنید میتونید از connection در جنگو استفاده کنید! مثال:


from django.db import connection


def my_custom_sql(self):
with connection.cursor() as cursor:
cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz])
cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz])
row = cursor.fetchone()

return row


خروجی یک لیست از نتایج خواهد بود. البته میتونید با یک حرکت ساده این لیست مپ کنید خودتون و در نهایت یک دیکشنری داشته باشید.


def dictfetchall(cursor):
"""
Return all rows from a cursor as a dict.
Assume the column names are unique.
"""
columns = [col[0] for col in cursor.description]
return [dict(zip(columns, row)) for row in cursor.fetchall()]


و‌ آخرین نکته اینکه وقتی دارید از raw استفاده میکنید دیگه اسلایس کردن رو لول کوئری لیمیت نمیزاره و بهتره از LIMIT استفاده کنید داخل خود کوئری SQL

@TorhamDevCH
👍5
Transaction per-request in #django


#جنگو قابلیت انجام ترنزکشن با دیتابیس به شما میده در چندید حالت مختلف یکی از حالت‌ها ترنزاکشن بر هر ریکوئستِ، یعنی چی؟ یعنی جنگو برای هر ریکوئستی که شما میگیرید یک ترنزاکشن باز میکنه یا به عباری برای هر ویو فانکشن شما یک atomic() ران میکنه! این قابلیت به شکل پیشفرض غیر فعاله ولی میتونید با اضافه ATOMIC_REQUESTS داخل کانفیگ دیتابیسی که میخوایید این حرکت باهاش بزنید این قابلیت فعال کنید.
این کار هر ریکوئست شمارو داخل یک ترنزاکشن warp میکنه و اگر ویو شما هر اروری بده ( ارور raise کنه ) ترنزاکشن rollback میخوره و تغییرات اعمال نمیشه.

@TorhamDevCH
👍3
Transaction in #django

اگه نمیدونید به طور کلی ترنزاکشن چیه میتونید به این لینک سر بزنید. ولی خیلی خلاصه بخوام بگم به انجام چندتا کوئری مختلف ولی در یک unit ترنزاکشن میگن، یعنی شما ۳ تا کوئری انجام میدی، اگه حتی یکدونه از اون ۳ تا ارور بخوره بقیه ۲ تا هم هر تغییری داده باشن اون برمیگردونن یا به اصطلاح Rollback میکنه.

حالا #جنگو این قابلیت به شما میده که از این فیچر دیتابیس‌ها داخل کدتون استفاده کنید برای این کار میتونید از transaction.atomic استفاده کنید، این فانکشن هم قابلیت استفاده به عنوان دکوریتور رو داره هم کانتکس منیجر


به عنوان دکوریتور:
@transaction.atomic()
def something():
do_database_update()
do_database_delete()


یا به عنوان کانتکس منیجر:

def something():
with transaction.atomic():
do_database_update()
do_database_delete()

تو هر دو کد اگر اتفاقی داخل یکی از فانکشن ها بیوفته یعنی اگر ارور raise بشه کوئری‌ها و تغییراتی که رو دیتابیس اعمال شده همه rollback میشه و برمیگرده، شما میتونید چندتا ترنزاکشن به صورت nested هم انجام بدید برای مثال:

with transaction.atomic():
do_update():
with transaction.atomic():
do_delete()

تو اینجا باید یک نکته رو در نظر داشته باشید که جنگو هربار که یک nested میزنید از بیرون به داخل میره و هربار که وارد یک مرحله عمیق تر میشه یک savepoint میسازه از نستد بالایی یعنی اول ترنزاکشن آپدیت انجام میده بعد savepoint میسازه که بفهمه چیکار کرده بعد میره دومی، میتونید ساخت savepoint رو غیر فعال کنید با پاس دادن savepoint=false به فانکشن اتومیک که توصیه جنگو اینه که اینکار نکنید مگر اینکه واقعا مشکل پرفورمنس بخورید.

در پست بعدی درباره Transaction.on_commit صحبت میکنم :)

@TorhamDevCH
👍3
مشکل concurrency چیه و چطوری میتونیم داخل #جنگو حلش کنیم؟

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

فرض کنید شما یک سیستم بانکی دارید و داخل این بانک یک حساب مشترک دارید بین user1 و user2. هردو این یوزرها به این حساب دسترسی دارن یوزر اول پرداخت کننده است و یوزر دوم برداشت کننده حالا فکر کنید بالانس(موجودی) حساب ۱۰۰۰ دلاره.

در همین لحظه که ما هستیم user1 میخواد ۱۰۰ دلار به حساب واریز کنه و دقیقا همزمان باهاش user2 میخواد ۱۰۰ دلار برداشت کنه. سوال اینه که در آخر این برداشت و واریز موجود یا همون بالانس حساب چقدر خواهد بود؟ جواب منطقی ما اینه که بالانس همون هزار دلار خواهد بود چون ۱۰۰ دلار اومد و ۱۰۰ دلار هم رفت و آره! اگه برنامه ما فقط و فقط یک پروسس باشه این ۲ درخواست به ترتیب اجرا خواهد شد و بالانس همون هزار دلار میشه اما اگه برنامه بیشتر از یک پروسس باشه چه اتفاقی میوفته؟

خوب بیایید فرض کنیم این بار درخواست اول به پروسس شماره ۱ و درخواست دوم به پروسس شماره ۲ میره و این دو همزمان از دیتابیس بالانس حساب میخونن تا درنهایت بهش جمع و منها بزنن دیگه. مراحل این خواهد شد.

۱. یوزر اول درخواست میزنه و موجودی ۱۰۰۰ دلار رو دریافت میکنه
۲. یورر اول موجودی آپدیت میکنه به ۱۱۰۰ دلار .

۳. یوزر دوم دقیقا تو همون لحظه درخواست میزنه و قبل اپدیت شدن موجودی اون میگیره و نتیجه موجودی برای ۱۰۰۰ دلاره
۴. ازش ۱۰۰ تا کم میکنه و ۹۰۰ رو به عنوان بالانس ذخیره میکنه.


و درنهایت موجودی شد ۹۰۰ دلار! این ماجرا میتونست برعکس هم اتفاق بیوفته و موجودی بشه ۱۱۰۰ دلار که همش نتیجه یک چند صدم ثانیه اختلاف بین ریکوئست یک و دو بود. نکته اصلی این بود که اینا قبل اینکه اون یکی موجودی آپدیت موجودی رو میخوندن و داخل مموری ذخیره میکردند در نتیجه تو اپدیت هم اشتباه اپدیت میکردند.


حالا که فهمیدید ماجرا از چه قرار شما میتونید داخل #django با استفاده از select_for_update و ترنزاکشن که پست‌ها قبل توضیح دادم این ماجرا حل کنید. ( در حقیقت جنگو ماجرا رو حل نمیکنه بلکه با استفاده از متدها شما به جنگو میگید کوئری بسازه که نتیجه اش این بشه که دیتابیس براتون یک لاک رو row که میخوایید آپدیت کنید بگیره)


حالا روش استفاده و توضیحات بیشتر میتونید تو مقاله پایین بخونید. این پست خلاصه‌ای کوتاه از مقاله زیر بود ( خود مقاله هم کوتاه)

https://www.sankalpjonna.com/learn-django/managing-concurrency-in-django-using-select-for-update

@TorhamDevCH
👍71
آپدیت کردن چند آبجکت به صورت همزمان در #جنگو

فریم‌ورک #django قابلیت آپدیت کردن دیتاها رو به روش ها مختلف داره که خیلی ها یا ازش بی خبر ان یا استفاده نمیکنن. بیایید ببینیم هر کدوم رو کجا استفاده کنی بهتره :)

مدل فرضی:

class Records(models.Model):
name = models.Charfield()
balance = models.InetegerField()
country =models.CharField()

خب فرض کنید ما یک هدیه به مناسب عید نو روز میخواییم به کاربرا بدیم، مثلا میخاییم نفری ۲ هزار تومن هدیه بدیم D:

حالا چند روش وجود داره.

روش اول ( نوب):
users = Records.objects.all()

for user in users:
user.balanc = user.balance + 2
user.save()

خیلی ساده و البته درب و داغون در خیلی جهات. مشکل اول اینه که ما رو همه کاربرا حلقه میزنیم و هر بار آپدیت رو روی کاربرا صدا میزنیم یعنی برای هر یوزر یک درخواست اپدیت به دیتابیس میره که اگه ۱ میلیون یوزر داشته باشیم ۱ میلیون درخواست میره :).

( تو اینه پست به اینکه باید از F استفاده کنید یا کانکارنسی و اینا هندل کنید اشاره نمیکنم، پست‌ها قبلی بخونید)

حالا روش بهتر چیه؟

روش بهتر:

user = Records.objects.update(balance=F("balance") + 2 )

همینقدر ساده :)


سناریو دوم: با بک‌اند یک بازی خفن رو داریم توسعه میدیم، داخل این بازی هر هفته یک ایونت اتفاق میوفته که افرادی که اون رو تموم کنن در آخر هفته یک تایتل به کنار اسمشون اضافه میشه و همچنین اگه امتیاز بالاتر از ۱۰ کسب کرده باشن به بالانس پول داخل گیم‌شون هم ۱۰۰ تا گلد اضافه میشه.


حالا بیایید فقط کوئری آپدیت این بهش ببینیم، فرض کنید این کوئری آخر هفته اجرا میشه. ( این فیلدا تو مدل فرض نداریم دیگه خودتون فرض کنید هست 😂❤️)

users = Records objects.fileter(done_weekly=True)

for user in users:
user.name = "Grunt " + user.name
if user.weekly_score >= 10:
user.balance = user.balance + 100

user.save()


خب همینطور که خیلی معلومه مشکلات فراوان داخلش هست. بزرگ‌ترین مشکلش اینه که هر بار برای هر کاربر یک درخواست آپدیت میدیم که میشهه همون مشکل بالا، آما آیا این بار میشه از روش بالا استفاده کرد و اینو فیکسش کرد؟ نه
روش بالا زمانی کاربرد داره که فیلدها قراره یک مقداری ثابتی به همشون داده بشه، اینجا بعضی ها ۱۰۰ تا گلد میگیرن بعضی ها نه پس کار نمیکنه، اینجا ما میتونیم از فانکشن bulk_update جنگو استفاده کنیم.

همون حلقه بالا رو میزنید با این تفاوت که داخلش .save رو صدا نمیزنید و تمام آبجکت‌ها رو داخل مموری آپدیت میکنید و بعد همچین حرکتی میزنید:

Records.objects.bulk_update(updated_users_list)

و تموم همرو با هم آپدیت میکنید با یک درخواست اینجا حتی میتونید یک قدم جلوتر برید و با اضافه کردن updated_fields به ورودی فانشکن و مشخص کردن اینکه دقیقا دوتا فیلد بالانس و نام فقط قرار آپدیت بشه بهترش کنید!

از این به بعد بهتر آپدیت کنید :)

@TorhamDevCH
👍13
Forwarded from Ninja Learn | نینجا لرن (Mohammad Eslami)
💎 سایت مپ (sitemaps) درجنگو 💎

امروز میخوایم با همدیگه یکی از ابزارهای خیلی کاربردی در دنیای Django رو بشناسیم. چیزی که بهمون کمک میکنه تا سایت‌مون رو بهتر و سریع‌تر برای موتورهای جستجو بهینه کنیم.

حالا Django sitemaps چیه؟
خب، فرض کنین سایتتون یه باغچه بزرگه. هر صفحه سایتتون هم یه گل قشنگ تو این باغچه. حالا Django sitemaps مثل یه نقشه کامل از این باغچه‌ست که به موتورهای جستجو میگه هر گل کجا قرار داره و چه ویژگی‌هایی داره. ️
این نقشه به موتورهای جستجو کمک میکنه تا راحت‌تر توی سایت شما گشت بزنن و صفحات مهم رو سریع‌تر پیدا کنن. اینجوری هم کاربران زودتر به اطلاعات دلخواهشون میرسن و هم سایتتون رتبه بهتری تو نتایج جستجو میگیره.

چرا از Django sitemaps استفاده کنیم؟

1⃣ بهبود سئو: با ایجاد یک sitemap کامل، موتورهای جستجو راحت‌تر صفحات سایتتون رو ایندکس میکنن و در نتیجه سایتتون توی نتایج جستجو بالاتر میاد.

2⃣ افزایش سرعت لود: sitemap به موتورهای جستجو میگه کدوم صفحات مهم‌ترن و باید زودتر بررسی بشن. اینجوری موتورهای جستجو وقت کمتری رو برای پیدا کردن صفحات مهم تلف میکنن.

3⃣ مدیریت بهتر صفحات: با استفاده از sitemaps میشه به راحتی صفحات سایت رو مدیریت کرد و صفحات اضافی یا قدیمی رو حذف کرد.

چطور Django sitemaps رو پیاده‌سازی کنیم؟ ️

1⃣ نصب پکیج: اول از همه باید پکیج django-sitemaps رو نصب کنیم. برای این کار کافیه تو ترمینال پروژهتون این دستور رو

بزنید:

pip install django-sitemaps 



2⃣ اضافه کردن به INSTALLED_APPS: بعد از نصب پکیج، باید اون رو به لیست اپلیکیشن‌های پروژه اضافه کنیم. تو فایل settings.py پروژهتون، 'django_sitemaps' رو به لیست INSTALLED_APPS اضافه کنید.

3⃣ ایجاد کلاس Sitemap: حالا باید یه کلاس از کلاس Sitemap که توی پکیج django_sitemaps تعریف شده، ایجاد کنیم. تو این کلاس، مشخص میکنیم کدوم مدل‌ها رو میخوایم توی sitemapمون قرار بدیم و چه فیلدهایی رو برای هر مدل میخوایم ایندکس کنیم.

from django.contrib.sitemaps import Sitemap
from .models import Post

class PostSitemap(Sitemap):

     changefreq = "weekly"
     priority   = 0.9

      def items(self):
           return Post.objects.all()

      def lastmod(self, obj):
           return obj.publish_date

      def location(self, item):
           return
reverse('home:post-detail',kwargs={'    slug':item.slug})
   

 
4⃣ ایجاد URL برای Sitemap: در نهایت باید یه URL برای دسترسی به sitemap ایجاد کنیم. این کار رو توی فایل urls.py پروژه انجام میدیم.

from django.contrib.sitemaps.views import sitemap from django.urls import path 
from .sitemaps import PostSitemap

sitemaps = {'posts': PostSitemap}

urlpatterns = [path('sitemap.xml', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap')]

5⃣ ارسال Sitemap به موتورهای جستجو: بعد از ایجاد sitemap، آدرس اون رو به کنسول جستجوی گوگل و سایر موتورهای جستجو ارسال کنید.

⭕️ نکته: برای اینکه موتورهای جستجو به طور خودکار sitemap رو پیدا کنن، بهتره از فایل robots.txt استفاده کنید و آدرس sitemap رو داخل اون قرار بدید.

خلاصه:
خب Django sitemaps یه ابزار قدرتمند برای بهبود سئوی سایت هست. با استفاده از این ابزار، موتورهای جستجو بهتر میتونن محتوای سایتتون رو درک کنن و در نتیجه سایتتون توی نتایج جستجو بالاتر میاد.

امیدوارم این آموزش براتون مفید بوده باشه. اگه سوالی داشتین، حتما بپرسید.

بنظرتون پست بعدی راجب چی باشه؟

#Django #sitemap #seo #python #برنامه_نویسی
13👍4🔥3🤔1
💎 شورت کات ها درجنگو 💎
توی این پست میخوام درمورد یکسری شورتکات هایی که کمتر کسی بهشون توجه میکنه رو معرفی کنم

1⃣ render_to_string 🧩
این شورتکات یه پله بالا تر از render هست. اگه می‌خوای تمپلیت رو به یه رشته (string) تبدیل کنی، مثلا برای ارسال ایمیل یا ساختن پیام خاص، render_to_string کارت رو راه میندازه. خیلی شیک و مجلسی می‌تونی تمپلیت رو رندر کنی و به جای HTML کامل، فقط رشته رو داشته باشی:

from django.template.loader import render_to_string

def send_email():
    message = render_to_string('email_template.html', {'key': 'value'})
    # حالا می‌تونی message رو به عنوان متن ایمیل بفرستی


2⃣ resolve_url 🔗
اگه تا حالا از reverse استفاده کردی، این یکی هم خیلی شبیه به اونه ولی یه خورده هوشمندتر. resolve_url می‌تونه هم نام ویو رو به URL تبدیل کنه و هم خودش چک می‌کنه که اگه ورودی URL باشه، مستقیم همون رو برگردونه. پس دیگه نیاز نیست نگران باشی چی بهش می‌دی:

from django.shortcuts import resolve_url

def my_view(request):
    url = resolve_url('some-view-name-or-url')
    # ادامه کارا


3⃣  HttpResponsePermanentRedirect 🚦
شاید با HttpResponseRedirect آشنا باشی، ولی این یکی یه Redirect دائمی (کد 301) برمی‌گردونه. این وقتی خوبه که می‌خوای URL جدید رو دائمی کنی و به موتورهای جستجو بگی که این مسیر دیگه همیشه اینجاست:

from django.http import HttpResponsePermanentRedirect

def my_view(request):
    return HttpResponsePermanentRedirect('/new-url/')


4⃣ Http404 🚫
اگه دوست داری یه ارور 404 رو مستقیم دستی بندازی، Http404 بهترین گزینه‌ست. این طوری می‌تونی خودت خیلی شیک کنترل کنی که کجاها ارور 404 داده بشه:

from django.shortcuts import Http404

def my_view(request):
    if not some_condition:
        raise Http404("این صفحه وجود نداره!")
    # ادامه کارا


5⃣ get_current_site 🌍
اگه تو پروژه‌های پیچیده‌تر می‌خوای بدونی که کاربر الان تو کدوم سایت یا دامنه قرار داره (مثلا تو پروژه‌هایی که از multi-site استفاده می‌کنن)، get_current_site خیلی کاربردیه:

from django.contrib.sites.shortcuts import get_current_site

def my_view(request):
    current_site = get_current_site(request)
    # حالا می‌تونی با current_site هر کاری کنی


این شورتکات‌ها می‌تونن واقعاً تو پروژه‌های پیچیده‌تر جنگویی به کارت بیان و کارت رو راحت تر کنن.

امیدوارم این لیست براتون مفید باشه ✌️

#Django #Python #کدنویسی #شورتکات #توسعه_وب #برنامه_نویسی


@ninja_learn_ir
15👍2🔥1
💎 عملیات‌های IO Bound و CPU Bound در جنگو 💎

امروز می‌خوایم درباره عملیات‌های IO Bound و CPU Bound صحبت کنیم و اینکه این دو نوع عملیات چطور روی عملکرد اپلیکیشن‌های جنگو تاثیر میذاره.

حالا IO Bound چیه؟ 🤔
عملیات‌های IO Bound به کارهایی اشاره دارن که بیشتر زمانشون صرف ورود و خروج داده‌ها میشه. مثلاً وقتی داریم با دیتابیس، فایل‌ها یا شبکه کار می‌کنیم. این نوع عملیات معمولاً منتظر میمونن تا داده‌ها از دیسک یا شبکه بیاد.

مثال‌هایی از IO Bound:
- خوندن و نوشتن در دیتابیس
- ارسال یا دریافت داده‌ها از API
- بارگذاری فایل‌ها

حالا CPU Bound چیه؟ 🧠
عملیات‌های CPU Bound به کارهایی اشاره دارن که بیشتر زمانشون صرف محاسبات سنگین میشه. یعنی برای پردازش و محاسبات نیاز به قدرت پردازشی بالا دارن. در اینجا عملکرد CPU نقش کلیدی رو ایفا می‌کنه.

مثال‌هایی از CPU Bound:
- پردازش تصاویر و ویدئوها
- انجام محاسبات ریاضی پیچیده
- اجرای الگوریتم‌های پیچیده

تفاوت‌های اصلی بین IO Bound و CPU Bound 🔍
1⃣ مدت زمان انتظار:
-عملیات های IO Bound معمولاً منتظر ورود و خروج داده‌ها هستن و زمان زیادی رو صرف این کار می‌کنن.

- عملیات های CPU Bound بیشتر زمانشون رو برای پردازش و محاسبات صرف می‌کنن.

2⃣ تکنیک‌های بهینه‌سازی:
- برای عملیات‌های IO Bound می‌تونی از Async و Threading استفاده کنی تا زمان انتظار رو کاهش بدی.

- برای CPU Bound باید به سراغ Multiprocessing بری تا بتونی از چند هسته CPU به طور همزمان استفاده کنی.

چطور در جنگو با اینها کار کنیم؟ 🚀
- برای IO Bound:
می‌تونی از async و await استفاده کنی تا درخواست‌ها به صورت غیر همزمان انجام بشن و سرعت اپلیکیشن بالا بره.

- برای CPU Bound:
می‌تونی از ماژول concurrent.futures و ProcessPoolExecutor استفاده کنی تا کارهای سنگین رو در چند پروسه اجرا کنی.

جمع‌بندی 🎯
درک تفاوت‌های بین IO Bound و CPU Bound می‌تونه بهت کمک کنه تا اپلیکیشن‌های جنگو رو بهتر بهینه‌سازی کنی. اگه عملیات‌های IO Bound داری، از async استفاده کن و اگر CPU Bound هستن، برو سراغ multiprocessing.

امید وارم مفید بوده باشه :)

#iobound #cpubound #python #django


@ninja_learn_ir
👍18👎1
Forwarded from Django Expert (Hêmn Hosseinpana)
پیکربندی لاگ زدن در جنگو - django logging

در این سری از ویدیوها که به ترفندها و نکته های جنگو میپردازیم، به سراغ لاگ زدن در جنگو رفتیم و کانفیگ ها و شیوه پیکربندی آن را از طریق بررسی و خواندن کد داخلی جنگو، شرح دادیم. در این ویدیو به جای کد نوشتن، بیشتر کد خوندیم که بفهمیم جنگو خودش برای لاگ زدن چگونه کار میکند و ما چگونه میتونیم از آن استفاده کنیم. همچنین امکان django logging را در سرویس های کلودی مانند sentry و APM هم مطرح کردیم. این مفاهیم رو در پروژه مینی ترلو به شکل عملی استفاده میکنیم.


video link: https://youtu.be/LGatKmpL7k8

playlist: https://www.youtube.com/playlist?list=PLJ9zDGwhhsBwdrfdaoOqbYev3_ocuBOfv

#django #logging #microfrontend_ir
〰️〰️〰️〰️〰️
©️ @DjangoEx
👍11👏2
خب خب خب Django Channels چیه؟ و چرا من ازش خوشم نمیاد

قبل از اینکه با هم بریم سراغ Django Channels، یه کم درباره WebSocket بگیم که اصلاً بدونیم داریم درباره چی حرف می‌زنیم. خب، WebSocket یه پروتکل که بهت اجازه میده ارتباط دوطرفه و دائمی بین کلاینت و سرور داشته باشی. یعنی چی؟ یعنی مثلاً تو یه اپلیکیشن چت، به جای اینکه هر چند ثانیه یه بار درخواست بفرستی "چیزی جدید اومده؟"، سرور خودش هر وقت یه پیام جدید داشت، بلافاصله می‌فرسته سمتت 🚀.

حالا Django Channels چی میگه؟ 🤔
ـDjango Channels یه ابزار تو اکوسیستم Djangoئه که میاد پشتیبانی از WebSocket، پروتکل‌های real-time و کارای async رو به پروژه‌هات اضافه می‌کنه. به زبان ساده، اگه Django عادی رو یه "خیابون یک‌طرفه" فرض کنیم، Channels میاد این خیابون رو دوطرفه می‌کنه. این یعنی می‌تونی کارایی مثل:

چت real-time 💬


نوتیفیکیشن‌های فوری 🔔


استریم داده (مثل قیمت‌های ارز دیجیتال) 📈


و...

رو خیلی راحت‌تر با Django انجام بدی.

خب پس مشکلش چیه؟ چرا من ازش خوشم نمیاد؟ 🤷‍♂️

از دور که نگاه می‌کنی، Channels خیلی جذاب به نظر میاد، ولی وقتی می‌خوای باهاش کارکنی، مشکلات خودش رو نشون میده:

1⃣ پیچیدگی توی تنظیمات 😵‍💫
ـDjango همیشه به خاطر سادگی معروف بوده، ولی Channels میاد این سادگی رو خراب می‌کنه خیلی خراب میکنه. باید ASGI رو راه بندازی، Redis نصب کنی، routing یاد بگیری، و کلی تنظیمات دیگه انجام بدی. یه پروژه ساده که با Django راحت بود، یهو برات میشه یه جنگل از تنظیمات.

نکته: از Django 4.0 به بعد، پشتیبانی از ASGI مستقیم داخل هسته Django اومده، پس برای پروژه‌های ساده شاید نیاز نباشه کل پروژه رو وابسته به Channels کنی.

2⃣ وابستگی به Redis 🤦‍♂️
یکی از مشکلات بزرگ Channels اینه که برای مدیریت eventها و ارتباط‌ها حتماً نیاز به Redis داره. خب چرا؟ دلیلش اینه که Redis به‌عنوان message broker استفاده میشه تا پیام‌ها بین کلاینت‌ها و سرور مدیریت بشه. ولی اگه پروژه کوچیک باشه، این وابستگی می‌تونه دردسرساز بشه.

جایگزین: می‌تونی از RabbitMQ یا حتی راه‌حل‌های ساده‌تر مثل In-Memory Layers برای پروژه‌های سبک استفاده کنی.


3⃣ محدودیت توی scale کردن 😩
اگه پروژه کوچیک باشه، Channels بد نیست. ولی وقتی تعداد کاربران زیاد میشه و حجم درخواست‌ها بالا میره، Channels سریع از نفس می‌افته. این محدودیت بیشتر به خاطر پیچیدگی WebSocket و محدودیت‌های سرورهای تک رشته ای هست تا خود Channels. برای پروژه‌های بزرگ و real-time محور، ابزارای دیگه‌ای مثل Socket.IO یا FastAPI خیلی بهتر عمل می‌کنن.

4⃣ مشکلات performance 🚨
حتی اگه پروژه خیلی هم بزرگ نباشه، Channels برای real-time پروژه‌های سنگین خوب عمل نمی‌کنه. کارای پیچیده async و ارتباطات real-time می‌تونن سرور رو داغون کنن. البته با تنظیم درست workerها و Redis channel layers می‌تونی بخشی از این مشکلات رو کم کنی، ولی باز هم کار اضافه‌ست.

5⃣ کمبود مستندات و منابع آموزشی درست و حسابی 📚
یکی دیگه از مشکلات اینه که منابع آموزشی کامل و به‌روزی برای Channels خیلی کمه. هر وقت گیر کنی، یا باید بری توی GitHub دنبال issueها، یا دست به دامن دیگران بشی. این باعث میشه زمان زیادی صرف حل مشکلات کنی.

خب حالا راه‌حل چیه؟ 💡
اگه بخوای real-time کار کنی، اینا می‌تونن گزینه‌های بهتری باشن:

ـFastAPI: اگه دنبال سرعت، سادگی و پرفورمنس خوب هستی، FastAPI انتخاب فوق‌العاده‌ایه. با WebSocket خیلی راحت کار می‌کنه و خبری از دردسرای Channels نیست 🚀.

ـSocket.IO: این یکی برای پروژه‌های real-time شاهکاره. خیلی ابزارای متنوع داره و با Node.js هم عالی مچ میشه.


جمع‌بندی 🎯
ـDjango Channels می‌تونه برای پروژه‌های کوچیک و ساده مناسب باشه، ولی اگه بحث scale، پرفورمنس یا راحتی کار مطرح باشه، اصلاً گزینه خوبی نیست. من از پیچیدگی‌ها و محدودیت‌هاش خسته شدم و به جای اون سراغ ابزارای دیگه رفتم.
نظر تو چیه؟ Django Channels تا حالا اذیتت کرده یا ازش خوشت میاد؟ بگو ببینم چی تو ذهنت می‌گذره🧐


#programming #web #django



🔆 CHANNEL | GROUP
👍22😁3
یک مبحثی که خیلی وقت‌ها آدم‌های رو داخل #جنگو گیج میکنه موضوع Aggregation هستش. برای مثال کوئری پایین:


>>> from django.db.models import Avg, Max, Min
>>> Book.objects.aggregate(Avg("price"), Max("price"), Min("price"))
# {'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

خب این کوئری مشخصه چه کاری داره انجام میده، همه میتونن بفهمنش مخصوصا وقتی خروجی کوئری رو میبینن، اما اگر ازشون بپرسید خب Aggregation چی هستش هیچ ایده ای ندارن! و این ماجرا از ضعف در دانش SQL سر چشمه میگیره. چون خیلی از آدم‌هایی که دارن #django کار میکنن مستقیم سراغ جنگو اومدن و نرفتن چیزهای دیگه رو مطالعه کنن و یاد بگیرن.

اسم Aggregation داخل ORM جنگو مستقیما از SQL میاد. در SQL یک سری فانکشن وجود داره که بهشون Aggregation functions میگن و کارشون خلاصه سازی اطلاعات:
MIN() - returns the smallest value within the selected column
MAX() - returns the largest value within the selected column
COUNT() - returns the number of rows in a set
SUM() - returns the total sum of a numerical column
AVG() - returns the average value of a numerical column

و خب شما میتونید داخل کوئری‌های SQL ازشون استفاده کنید و دیتا خروجی رو خلاصه سازی کنید و یا یک آمار ازش دربیارید. مثلا میانگین قیمت کتاب‌های تو سال اخیر و ...
یک کوئری مثال برای Aggregation میتونه این باشه:

SELECT AVG(Price) as price_avg FROM Books WHERE puddate='2023-01-01';


خب از اونجایی که ORM جنگو در نهایت قرار کار همین SQL نوشتن برای شما انجام بده و کوئری‌ شمارو به SQL تبدیل کنه شما دقیقا همین کوئری میتونید داخل جنگو به این صورت بنویسید:


>>> from django.db.models import Avg
>>> from datetime import datetime
>>> Books.objects.filter(pubdate=datetime(2023, 1, 1)).aggregate(price_avg=Avg("price"))

میتونید لیست فانکشن‌های Aggregation خود SQL داخل این لینک ببینید و ساپورت جنگو هم میتونید داخل این لینک ببینید.

در نهایت از دانش SQL غافل نباشید و حتما یادش بیگیرید. هرچی بیشتر SQL بدونید زندگی راحت‌تری خواهید داشت.

@TorhamDevCH
👍181