Transaction in #django
اگه نمیدونید به طور کلی ترنزاکشن چیه میتونید به این لینک سر بزنید. ولی خیلی خلاصه بخوام بگم به انجام چندتا کوئری مختلف ولی در یک unit ترنزاکشن میگن، یعنی شما ۳ تا کوئری انجام میدی، اگه حتی یکدونه از اون ۳ تا ارور بخوره بقیه ۲ تا هم هر تغییری داده باشن اون برمیگردونن یا به اصطلاح Rollback میکنه.
حالا #جنگو این قابلیت به شما میده که از این فیچر دیتابیسها داخل کدتون استفاده کنید برای این کار میتونید از transaction.atomic استفاده کنید، این فانکشن هم قابلیت استفاده به عنوان دکوریتور رو داره هم کانتکس منیجر
به عنوان دکوریتور:
یا به عنوان کانتکس منیجر:
تو هر دو کد اگر اتفاقی داخل یکی از فانکشن ها بیوفته یعنی اگر ارور raise بشه کوئریها و تغییراتی که رو دیتابیس اعمال شده همه rollback میشه و برمیگرده، شما میتونید چندتا ترنزاکشن به صورت nested هم انجام بدید برای مثال:
تو اینجا باید یک نکته رو در نظر داشته باشید که جنگو هربار که یک nested میزنید از بیرون به داخل میره و هربار که وارد یک مرحله عمیق تر میشه یک savepoint میسازه از نستد بالایی یعنی اول ترنزاکشن آپدیت انجام میده بعد savepoint میسازه که بفهمه چیکار کرده بعد میره دومی، میتونید ساخت savepoint رو غیر فعال کنید با پاس دادن savepoint=false به فانکشن اتومیک که توصیه جنگو اینه که اینکار نکنید مگر اینکه واقعا مشکل پرفورمنس بخورید.
در پست بعدی درباره Transaction.on_commit صحبت میکنم :)
@TorhamDevCH
اگه نمیدونید به طور کلی ترنزاکشن چیه میتونید به این لینک سر بزنید. ولی خیلی خلاصه بخوام بگم به انجام چندتا کوئری مختلف ولی در یک 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
❤10
Transaction.on_commit in #django
قبل اینکه بریم سراغ موضوع اصلی بیایید چندتا نکته دیگه رو بفهمیم. داخل #جنگو شما لازم نیست تغییراتتون رو کامیت کنید مثلا اگر چیزی داخل دیتابیس آپدیت میکنید دیگه نمیایید خط بعد بنویسید Model.objects.commit)( به جاش از .save استفاده میکنید. ولی هرکسی که تاحالا با یک API لول پایینتر از ORM جنگو کار کرده میدونه که برای ثبت تغییرات باید commit بزنی. همینطوری که داخل بقیه کتابخونه و ... دیگه دیدی و میدونید شما میتونید آپشن آتوکامیت رو فعال کنید و دیگه لازم نباشه کامیت بزنید، داخل جنگو این آپشن بهصورت پیشفرض فعال و میتونید داخل تنظیمات بخش دیتابیس اون غیر فعال کنید اما همه اینها رو گفتم که بگم جنگو زمانی که شما یک Transaction باز میکنید خودش این آتوکامیت رو در اون لحظه غیر فعال میکنه! دلیل منطقیش هم اینه که شما شاید ارور بخورید و بخوایید transaction رو rollback کنید در نتيجه اتوکامیت خاموش میشه. حالا چی میشه اگه شما بخوایید اگر یک transaction موفق بود(کامیت شد) بعدش یک کار رو انجام بدید؟ مثلا اگه موفق بود یک ایمیل بفرسته، خب آتوکامیت که خاموشه در نتیجه تو خطی که کد ران میشه کامیت اتفاق نمیوفته حالا جنگو اومده یک transaction.on_commit در اختیارتون گذاشته که یک callable میگیره و زمانی که اون transaction موفق بود و کامیتش کرد میاد این فانکشن شما رو اجرا میکنه.
* اگر هیچ ترنزاکشنی باز نکرده باشید و از فانکشن استفاده کنید فانشکن درجا اجرا میشه.*
نحوه استفاده:
حالا قطعا اون وسط یک کوئری هم ران میکنید :)
@TorhamDevCH
قبل اینکه بریم سراغ موضوع اصلی بیایید چندتا نکته دیگه رو بفهمیم. داخل #جنگو شما لازم نیست تغییراتتون رو کامیت کنید مثلا اگر چیزی داخل دیتابیس آپدیت میکنید دیگه نمیایید خط بعد بنویسید Model.objects.commit)( به جاش از .save استفاده میکنید. ولی هرکسی که تاحالا با یک API لول پایینتر از ORM جنگو کار کرده میدونه که برای ثبت تغییرات باید commit بزنی. همینطوری که داخل بقیه کتابخونه و ... دیگه دیدی و میدونید شما میتونید آپشن آتوکامیت رو فعال کنید و دیگه لازم نباشه کامیت بزنید، داخل جنگو این آپشن بهصورت پیشفرض فعال و میتونید داخل تنظیمات بخش دیتابیس اون غیر فعال کنید اما همه اینها رو گفتم که بگم جنگو زمانی که شما یک Transaction باز میکنید خودش این آتوکامیت رو در اون لحظه غیر فعال میکنه! دلیل منطقیش هم اینه که شما شاید ارور بخورید و بخوایید transaction رو rollback کنید در نتيجه اتوکامیت خاموش میشه. حالا چی میشه اگه شما بخوایید اگر یک transaction موفق بود(کامیت شد) بعدش یک کار رو انجام بدید؟ مثلا اگه موفق بود یک ایمیل بفرسته، خب آتوکامیت که خاموشه در نتیجه تو خطی که کد ران میشه کامیت اتفاق نمیوفته حالا جنگو اومده یک transaction.on_commit در اختیارتون گذاشته که یک callable میگیره و زمانی که اون transaction موفق بود و کامیتش کرد میاد این فانکشن شما رو اجرا میکنه.
* اگر هیچ ترنزاکشنی باز نکرده باشید و از فانکشن استفاده کنید فانشکن درجا اجرا میشه.*
نحوه استفاده:
def something():
with transaction.atomic():
transaction.on_commit(send_mail)
حالا قطعا اون وسط یک کوئری هم ران میکنید :)
@TorhamDevCH
👍10
مشکل 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
بیایید اول بفهمیم مشکل چی هست اصلا که میخواییم حلش کنیم. از زمانی که انسانها موفق شدم چند پروسس رو همزمان اجرا کنن این مشکل به وجود اومد برای مثال این روزها دیگه شما فقط یک ترد پروژه جنگوتون اجرا نمیکنید بلکه با ابزارهای مثل گونیکورن و یونیکورن و ... چندتا ترد و پروسس ازش اجرا میکنید تا همچی سریعتر اتفاق بیوفته. اما این مشکل که اتفاق میوفته چیه؟
فرض کنید شما یک سیستم بانکی دارید و داخل این بانک یک حساب مشترک دارید بین user1 و user2. هردو این یوزرها به این حساب دسترسی دارن یوزر اول پرداخت کننده است و یوزر دوم برداشت کننده حالا فکر کنید بالانس(موجودی) حساب ۱۰۰۰ دلاره.
در همین لحظه که ما هستیم user1 میخواد ۱۰۰ دلار به حساب واریز کنه و دقیقا همزمان باهاش user2 میخواد ۱۰۰ دلار برداشت کنه. سوال اینه که در آخر این برداشت و واریز موجود یا همون بالانس حساب چقدر خواهد بود؟ جواب منطقی ما اینه که بالانس همون هزار دلار خواهد بود چون ۱۰۰ دلار اومد و ۱۰۰ دلار هم رفت و آره! اگه برنامه ما فقط و فقط یک پروسس باشه این ۲ درخواست به ترتیب اجرا خواهد شد و بالانس همون هزار دلار میشه اما اگه برنامه بیشتر از یک پروسس باشه چه اتفاقی میوفته؟
خوب بیایید فرض کنیم این بار درخواست اول به پروسس شماره ۱ و درخواست دوم به پروسس شماره ۲ میره و این دو همزمان از دیتابیس بالانس حساب میخونن تا درنهایت بهش جمع و منها بزنن دیگه. مراحل این خواهد شد.
۱. یوزر اول درخواست میزنه و موجودی ۱۰۰۰ دلار رو دریافت میکنه
۲. یورر اول موجودی آپدیت میکنه به ۱۱۰۰ دلار .
۳. یوزر دوم دقیقا تو همون لحظه درخواست میزنه و قبل اپدیت شدن موجودی اون میگیره و نتیجه موجودی برای ۱۰۰۰ دلاره
۴. ازش ۱۰۰ تا کم میکنه و ۹۰۰ رو به عنوان بالانس ذخیره میکنه.
و درنهایت موجودی شد ۹۰۰ دلار! این ماجرا میتونست برعکس هم اتفاق بیوفته و موجودی بشه ۱۱۰۰ دلار که همش نتیجه یک چند صدم ثانیه اختلاف بین ریکوئست یک و دو بود. نکته اصلی این بود که اینا قبل اینکه اون یکی موجودی آپدیت موجودی رو میخوندن و داخل مموری ذخیره میکردند در نتیجه تو اپدیت هم اشتباه اپدیت میکردند.
حالا که فهمیدید ماجرا از چه قرار شما میتونید داخل #django با استفاده از select_for_update و ترنزاکشن که پستها قبل توضیح دادم این ماجرا حل کنید. ( در حقیقت جنگو ماجرا رو حل نمیکنه بلکه با استفاده از متدها شما به جنگو میگید کوئری بسازه که نتیجه اش این بشه که دیتابیس براتون یک لاک رو row که میخوایید آپدیت کنید بگیره)
حالا روش استفاده و توضیحات بیشتر میتونید تو مقاله پایین بخونید. این پست خلاصهای کوتاه از مقاله زیر بود ( خود مقاله هم کوتاه)
https://www.sankalpjonna.com/learn-django/managing-concurrency-in-django-using-select-for-update
@TorhamDevCH
Sankalpjonna
Managing concurrency in Django using select_for_update
A tutorial on how one can use select_for_update to lock a Django queryset until the transaction it is in is committed in order to handle concurrency.
❤8👍5❤🔥1
Field level validation in #DRF
خوب خود فریمورک Django Rest یا مخفف DRF چندین نوع ولیدتور در اختیار شما میزاره که باهاش میتونی دیتا ورودی رو ولیدت(اعتبارسنجی؟) کنید یکی از اونا field level هستش که به شما اجازه میده هرکاری با مقدار یک فیلد انجام بدید و حتی تغییرش بدید در هنگام اعتبارسنجی(همون ولیدت کردن XD )
برای استفاده از این نوع ولیدیشن/اعتبارسنجی باید برای هر فیلدی که میخوایید روش اعتبارسنجی انجام بدید یک فانکشن با این prefix به کلاس سریالایزرتون اضافه کنید :
validate_<field_name>
مثلا اگه یک فیلد به اسم amount دارید
validate_amount
مثال کامل:
این مثال ساده ای بود از این ولیدتور/اعتبارسنج
نکاتی که خوبه بدونیم:
مقدار value نسبت به نوع فیلد فرق خواهد کرد.
در نهایت شما میتونید یک مقدار دیگه هم برگردونید، ولی حتما باید یک مقدار برگرده مگرنه فیلد None خواهد شد.
در خطی که ارور raise شده شما میتونید به کلاس ValidationError یک استاتوس کد هم با ورودی code بدید که استاتوس کد httpعه اون ارور خواهد بود، که تو مثال بالا میتونست 400 یا همون bad request باشه.
فریمورک django rest ولیدیشنهای مختلفی داره که در پستهای آینده بهش میپردازم.
@TorhamDevCH
خوب خود فریمورک Django Rest یا مخفف DRF چندین نوع ولیدتور در اختیار شما میزاره که باهاش میتونی دیتا ورودی رو ولیدت(اعتبارسنجی؟) کنید یکی از اونا field level هستش که به شما اجازه میده هرکاری با مقدار یک فیلد انجام بدید و حتی تغییرش بدید در هنگام اعتبارسنجی(همون ولیدت کردن XD )
برای استفاده از این نوع ولیدیشن/اعتبارسنجی باید برای هر فیلدی که میخوایید روش اعتبارسنجی انجام بدید یک فانکشن با این prefix به کلاس سریالایزرتون اضافه کنید :
validate_<field_name>
مثلا اگه یک فیلد به اسم amount دارید
validate_amount
مثال کامل:
from rest_framework import serializers
class DepositSerializer(serializers.Serializer):
amount = serializers.IntegerField()
def validate_amount(self, value):
if value <= 0:
raise serializers.ValidationError("pls enter a number bigger than zero.")
return value
این مثال ساده ای بود از این ولیدتور/اعتبارسنج
نکاتی که خوبه بدونیم:
مقدار value نسبت به نوع فیلد فرق خواهد کرد.
در نهایت شما میتونید یک مقدار دیگه هم برگردونید، ولی حتما باید یک مقدار برگرده مگرنه فیلد None خواهد شد.
در خطی که ارور raise شده شما میتونید به کلاس ValidationError یک استاتوس کد هم با ورودی code بدید که استاتوس کد httpعه اون ارور خواهد بود، که تو مثال بالا میتونست 400 یا همون bad request باشه.
فریمورک django rest ولیدیشنهای مختلفی داره که در پستهای آینده بهش میپردازم.
@TorhamDevCH
👍6🤝6⚡1
TorhamDev | تورهام 😳
pass: can i stay here? 👉👈
یک نکته جالب:
تو عکس یک تودو میبینید که نوشته منطق withdraw یا همون برداشت رو اینجا توسعه بده.
نکته ای که میخوام بگم اینه که ما باید سعی کنیم بیزینس لاجیک رو تو یکجا نگه داریم تا جای ممکن حالا این تصمیم شماست که کجا قرارش بدید. توصیه جنگو اینه که سعی کنید اینجور چیزا نزدیک به مدل نگه دارید چون در نهایت بیشتر لاجیکها به crud ختم میشه در نتیجه شاید بهتر باشه از این ویو صرفا جهت routing استفاده کنیم و داخلش یک فانکشن ران کنیم که برداشت برامون انجام بده مثلا همچین چیزی:
خب این یعنی من یک فانکشن رو مدل Wallet دارم که این کار قرار برام انجام بده.
یا به عبارتی:
اینطوری اگه کسی متدهای مدل بخونه تا حد زیادی لاجیکهارو میفهمه و اینکه میدونه اگه دنبال یک لاجیک میگرده کجا بره!
@TorhamDevCH
تو عکس یک تودو میبینید که نوشته منطق withdraw یا همون برداشت رو اینجا توسعه بده.
نکته ای که میخوام بگم اینه که ما باید سعی کنیم بیزینس لاجیک رو تو یکجا نگه داریم تا جای ممکن حالا این تصمیم شماست که کجا قرارش بدید. توصیه جنگو اینه که سعی کنید اینجور چیزا نزدیک به مدل نگه دارید چون در نهایت بیشتر لاجیکها به crud ختم میشه در نتیجه شاید بهتر باشه از این ویو صرفا جهت routing استفاده کنیم و داخلش یک فانکشن ران کنیم که برداشت برامون انجام بده مثلا همچین چیزی:
Wallet.objects.get(pk=id).do_withdraw(amount=100)
خب این یعنی من یک فانکشن رو مدل Wallet دارم که این کار قرار برام انجام بده.
یا به عبارتی:
@transaction.atomic()
def deposit(self, *, amount: int):
obj = self.get_queryset().select_for_update().get()
obj.balance = models.F("balance") + amount
obj.save()
Transaction.objects.create(
amount=amount,
wallet=self,
tr_type=TransactionsType.DIPOSIT,
status=TransactionsStatus.SUCCESS,
)
@transaction.atomic()
def withdraw(self, *, amount: int): ...
اینطوری اگه کسی متدهای مدل بخونه تا حد زیادی لاجیکهارو میفهمه و اینکه میدونه اگه دنبال یک لاجیک میگرده کجا بره!
@TorhamDevCH
👍9
get_object_404 in #django
فریمورک #جنگو یکسری shortcuts یا همون میانبر داره که کد زدن شما رو کمتر میکنه و خیلی کاربردی ان. میتونید تمام اونها از این مسیر ایمپورت کنید:
امروز میخوام درباره یکی از میانبر ها بگم به اسم get_object_404 این شرتکات کار نوشتن کد شما رو کمتر میکنه. بیشتر مواقع وقتی شما میخوایید یک آبجکت رو در دیتابیس کوئری بزنید باید این کار رو کنید: اگر آبجکت بود اون رو برگردون اگر نبود ارور ۴۰۴ بده.
حالا شما باید این کار رو هرجا که میخوایید یک چیزی از دیتابیس بگیرید انجام بدید. خلاصه هربار باید همچین کدی بزنید:
حالا جنگو اومده این کار براتون کرده یک فانکشن. فقط صداش میزنید و مدل و کوئری که میخوایید بزنید رو بهش میدید و اگر وجود داشت آبجکت بهتون میده اگه نداشت خودش ارور ۴۰۴ میده.
این شرتکات یک نمونه مشابه داره به اسم get_list_or_404 که این یکی به شما لیستی از ابجکتها رو برمیگردونه. اولی فقط و فقط یک آبجکت برمیگردوند.
@TorhamDevCH
فریمورک #جنگو یکسری shortcuts یا همون میانبر داره که کد زدن شما رو کمتر میکنه و خیلی کاربردی ان. میتونید تمام اونها از این مسیر ایمپورت کنید:
from django.shortcuts import <NAME>
امروز میخوام درباره یکی از میانبر ها بگم به اسم get_object_404 این شرتکات کار نوشتن کد شما رو کمتر میکنه. بیشتر مواقع وقتی شما میخوایید یک آبجکت رو در دیتابیس کوئری بزنید باید این کار رو کنید: اگر آبجکت بود اون رو برگردون اگر نبود ارور ۴۰۴ بده.
حالا شما باید این کار رو هرجا که میخوایید یک چیزی از دیتابیس بگیرید انجام بدید. خلاصه هربار باید همچین کدی بزنید:
def get_object(self):
try:
return Record.objects.get(id=self.request.query_params['id'])
except Record.DoesNotExist:
raise Http404()
حالا جنگو اومده این کار براتون کرده یک فانکشن. فقط صداش میزنید و مدل و کوئری که میخوایید بزنید رو بهش میدید و اگر وجود داشت آبجکت بهتون میده اگه نداشت خودش ارور ۴۰۴ میده.
def get_object(self):
return get_object_or_404(Record, id=self.request.query_params['id'])
این شرتکات یک نمونه مشابه داره به اسم get_list_or_404 که این یکی به شما لیستی از ابجکتها رو برمیگردونه. اولی فقط و فقط یک آبجکت برمیگردوند.
@TorhamDevCH
👍7🍾2
تا حالا براتون سوال شده جنگو چطوری وقتی یک رابطه one2many یا همون فارنکی میزنید چطوری وقتی فیلد صدا میکنید ابجکت فارنکی بهتون میده؟ یا چطوری کوئری look up هایی که میزنید برای مثال داخل filter رو هندل میکنه؟
مثلا رابطه بین مدل a و b دارید بعد همچنین چیزی مینویسید
a.fk.name
و جنگو مقدار name رو میده یا وقتی فیلتر میزنید
a.objects.filter(fk__name="test")
جنگو اینها رو با استفاده از denoscriptor در پایتون انجام میده که قابلیتهای خفنی به کلاستون میده، برای مثال وقتی یک attribute از یک کلاس صدا بزنید درجا یک کد ران کنید و خیلی چیزا دیگه که الان طولانی میشه توضیح بدم، در نتیجه پاشید برید داکیومنتشو بخونید :)
https://docs.python.org/3/howto/denoscriptor.html
@TorhamDevCH
مثلا رابطه بین مدل a و b دارید بعد همچنین چیزی مینویسید
a.fk.name
و جنگو مقدار name رو میده یا وقتی فیلتر میزنید
a.objects.filter(fk__name="test")
جنگو اینها رو با استفاده از denoscriptor در پایتون انجام میده که قابلیتهای خفنی به کلاستون میده، برای مثال وقتی یک attribute از یک کلاس صدا بزنید درجا یک کد ران کنید و خیلی چیزا دیگه که الان طولانی میشه توضیح بدم، در نتیجه پاشید برید داکیومنتشو بخونید :)
https://docs.python.org/3/howto/denoscriptor.html
@TorhamDevCH
Python documentation
Denoscriptor Guide
Author, Raymond Hettinger,, Contact,<python at rcn dot com>,. Contents: Denoscriptor Guide- Primer- Simple example: A denoscriptor that returns a constant, Dynamic lookups, Managed attributes, Cu...
👍4
HTTP1 vs HTTP2 vs HTTP3 - A Deep Dive.pdf
3.9 MB
تفاوت بین http1 و http2 و http3 و مراحل رشدشون
داکیومنت فوقالعادهای به نظرم حتما بخونید، شاید شما هم نمیدانستید http0.9 هم داشتیم :)
من قبلا دوره حسین ناصر رو دیده بودم و خب تو اون دوره پروتوکل ها و http و نسخه های مختلف رو توضیح میده، این داکیومنت واقعا مکمل عالیه برای کامل کردن اون اطلاعات دوره.
- منبع
دوره حسین ناصر هم اسمش Fundamental of back-end engineering هستش، دوره خیلی خوبیه.
@TorhamDevCH
داکیومنت فوقالعادهای به نظرم حتما بخونید، شاید شما هم نمیدانستید http0.9 هم داشتیم :)
من قبلا دوره حسین ناصر رو دیده بودم و خب تو اون دوره پروتوکل ها و http و نسخه های مختلف رو توضیح میده، این داکیومنت واقعا مکمل عالیه برای کامل کردن اون اطلاعات دوره.
- منبع
دوره حسین ناصر هم اسمش Fundamental of back-end engineering هستش، دوره خیلی خوبیه.
@TorhamDevCH
👍14💋5❤1👎1
امروز یک پست جالب درباره آپتومایز کردن کوئریهای جنگو مینویسم ✨✌️
🔥11❤🔥2👍1👌1
این چند روز اخیر داشتم رو این پروژه کار میکردم. خیلی چیزا جدید یادگرفتم. هنوز هم روش کار میکنم، هر روز که چیزا جدید میخونم روش پیاده میکنم، تا الان باب میل خودم هست :) حالا فرستادمش برای اهل فنها تا مشکل بگیرن ازش بدم بیاد XD
https://github.com/TorhamDev/simple-wallet-improved
داخلش از جنگو - جنگو رست - رببیت - پستگرس استفاده کردم.
@TorhamDevCH
https://github.com/TorhamDev/simple-wallet-improved
داخلش از جنگو - جنگو رست - رببیت - پستگرس استفاده کردم.
@TorhamDevCH
GitHub
GitHub - TorhamDev/simple-wallet-improved: improved version of simple wallet
improved version of simple wallet. Contribute to TorhamDev/simple-wallet-improved development by creating an account on GitHub.
بیایید به بهانه آپتومایز کردن کوئریهای #جنگو چندتا چیز جدید درباره ORM جنگو یاد بگیریم
شما همیشه وقتی چیزی رو نیاز دارید داخل جنگو همچین کوئری میزنید:
خب این قرار آبجکتهایی که ایدی ۱ تا ۴ دارن بهمون بده. حالا شما میخوایید با اینا یکسری پردازش انجام بدید مثلا بیایید اسم همه رکوردها رو نشون کار برید یا به عبارتی:
و کار شما اینجا تموم میشه و خوشحال میشید. اماااااااا شما یک عالمه پردازش بی جا انجام دادید و یک عالمه منابع الکی خرج کرید. بیایید ببینیم کوئری بالا وقتی sql میشه چه شکلی میشه:
این چیزی که داخل دیتابیس اجرا میشه. متوجه اش شدید؟
اگه نشدید باید بگم شما فقط به فیلد name نیاز داشتید اما تمام فیلدهای اون اون رکوردها رو گرفتید! هیچ وقت هم ازشون استفاده نکردید.
برای حل این مشکل جنگو دوتا راه حل داره:
1. values
2. values_list
با استفاده از این دو میتونید فقط فیلدهایی که میخوایید رو بگیرید. برای مثال:
و حالا کوئری که میسازید همچین چیزی خواهد شد:
و همینطور که میبینید حالا فقط اون فیلدی رو گرفتید که لازمش دارید.
تفاوت بین values و valuse_list تنها در دیتا استراکچر خروجی که به شما میده و داخل کوئری نهایی هردو مثل هم عمل میکنن. برای درک بیشتر:
بله یکی دیکشنری و دومی تاپل :) همچنین اگه فقط فقط یک فیلد میخوایید مثلا name میتونید از flat=True هم استفاده کنید برای بهتر شدن دیتا خروجی:
@TorhamDevCH
شما همیشه وقتی چیزی رو نیاز دارید داخل جنگو همچین کوئری میزنید:
Record.objects.filter(id__in=[1,2,3,4])
خب این قرار آبجکتهایی که ایدی ۱ تا ۴ دارن بهمون بده. حالا شما میخوایید با اینا یکسری پردازش انجام بدید مثلا بیایید اسم همه رکوردها رو نشون کار برید یا به عبارتی:
for r in records:
print(record.name)
و کار شما اینجا تموم میشه و خوشحال میشید. اماااااااا شما یک عالمه پردازش بی جا انجام دادید و یک عالمه منابع الکی خرج کرید. بیایید ببینیم کوئری بالا وقتی sql میشه چه شکلی میشه:
SELECT id,
name,
created_at,
is_deleted
FROM records
WHERE id IN (1, 2, 3, 4);
این چیزی که داخل دیتابیس اجرا میشه. متوجه اش شدید؟
اگه نشدید باید بگم شما فقط به فیلد name نیاز داشتید اما تمام فیلدهای اون اون رکوردها رو گرفتید! هیچ وقت هم ازشون استفاده نکردید.
برای حل این مشکل جنگو دوتا راه حل داره:
1. values
2. values_list
با استفاده از این دو میتونید فقط فیلدهایی که میخوایید رو بگیرید. برای مثال:
Record.objects.filter(id__in=[1,2,3,4]).values('name')
و حالا کوئری که میسازید همچین چیزی خواهد شد:
SELECT name
FROM records
WHERE id IN (1, 2, 3, 4);
و همینطور که میبینید حالا فقط اون فیلدی رو گرفتید که لازمش دارید.
تفاوت بین values و valuse_list تنها در دیتا استراکچر خروجی که به شما میده و داخل کوئری نهایی هردو مثل هم عمل میکنن. برای درک بیشتر:
>>> Record.objects.filter(is_deleted=False).values('id', 'name')
<QuerySet [{'id': 1, 'name': 'First record'}, {'id': 2, 'name': 'Second Record'}, {'id': 3, 'name': 'Third Record'}]>
>>> Record.objects.filter(is_deleted=False).values_list('id', 'name')
<QuerySet [(1, 'First record'), (2, 'Second Record'), (3, 'Third Record')]>
بله یکی دیکشنری و دومی تاپل :) همچنین اگه فقط فقط یک فیلد میخوایید مثلا name میتونید از flat=True هم استفاده کنید برای بهتر شدن دیتا خروجی:
>>> Record.objects.filter(is_deleted=False).values_list('name',flat=True)
<QuerySet ['First record', 'Second Record', 'Third Record']>
@TorhamDevCH
🙏7👍6👌2✍1
TorhamDev | تورهام 😳
بیایید به بهانه آپتومایز کردن کوئریهای #جنگو چندتا چیز جدید درباره ORM جنگو یاد بگیریم شما همیشه وقتی چیزی رو نیاز دارید داخل جنگو همچین کوئری میزنید: Record.objects.filter(id__in=[1,2,3,4]) خب این قرار آبجکتهایی که ایدی ۱ تا ۴ دارن بهمون بده. حالا شما…
این پست درباره آپتومایز کردن read بود، پست فردا درباره اپتومایز کردن update میگم. اونم خیلی جالبه
👍5🆒1
بچهها میتونید تسکهایی که شرکتها بهتون دادن برای مصاحبه رو برام بفرستید لطفا؟ 👈👉🥹
تولدم مبارک 🎉
هدیه نمیگیرم ولی اگه کسی خواست بده میتونه تتر بزنه 😂❤️
My Public Address to Receive TRX TDzW3Q8DkenuYdoQFqsRE7mZZYuFhzZRAH
Pay me via Trust Wallet: https://link.trustwallet.com/send?coin=195&address=TDzW3Q8DkenuYdoQFqsRE7mZZYuFhzZRAH
هدیه نمیگیرم ولی اگه کسی خواست بده میتونه تتر بزنه 😂❤️
My Public Address to Receive TRX TDzW3Q8DkenuYdoQFqsRE7mZZYuFhzZRAH
Pay me via Trust Wallet: https://link.trustwallet.com/send?coin=195&address=TDzW3Q8DkenuYdoQFqsRE7mZZYuFhzZRAH
❤22👎4👍2🍾2🗿2😁1💋1
چطور کوئری آپدیت بهتری داخل #جنگو بزنیم؟
روشهای زیادی برای آپدیت کردن یک آجکت یا چندتا آبجکت داخل جنگو وجود داره، ساده ترین حالتی که افراد استفاده میکنن همچین چیزی.
مدل فرضی:
برای مثال اگه کسی بخواد یک آبجکت از این مدل رو آپدیت کنه همچین کار میکنه:
که این اوکیه، بد نیست و آپدیت براتون انجام میده اما یک نکته رو بهش توجه نمیکنید!
زمانی که شما یک آبجکت این شکلی آپدیت میکنید در اصل دارید تمام فیلدها رو آپدیت میکنید :) ولی خب مقدار فیلدهای قبلی همون قبلی ها آپدیت میشه، برای اینکه از این کار جلو گیری کنید باید explicit ( نمیدونم، فکر کنم دقیق تر معنی بده) باشید یعنی، دقیقا بگید کدوم فیلد/فیلدها میخوایید آپدیت کنید. این کار میکنید با استفاده از پارامتر update_fields انجام بدید.
البته باید مراقب باشید که حتما فیلدهایی که میخوایید آپدیت کنید رو داخلش بزارید مگرنه آپدیت نمیشن.
پست بعدی آپدیت کردن چند آبجکت...
@TorhamDevCH
روشهای زیادی برای آپدیت کردن یک آجکت یا چندتا آبجکت داخل جنگو وجود داره، ساده ترین حالتی که افراد استفاده میکنن همچین چیزی.
مدل فرضی:
class Records(models.Model):
name = models.Charfield()
balance = models.InetegerField()
country =models.CharField()
برای مثال اگه کسی بخواد یک آبجکت از این مدل رو آپدیت کنه همچین کار میکنه:
r = Records.objects.get(pk=1)
r.name = "new name"
r.save()
که این اوکیه، بد نیست و آپدیت براتون انجام میده اما یک نکته رو بهش توجه نمیکنید!
زمانی که شما یک آبجکت این شکلی آپدیت میکنید در اصل دارید تمام فیلدها رو آپدیت میکنید :) ولی خب مقدار فیلدهای قبلی همون قبلی ها آپدیت میشه، برای اینکه از این کار جلو گیری کنید باید explicit ( نمیدونم، فکر کنم دقیق تر معنی بده) باشید یعنی، دقیقا بگید کدوم فیلد/فیلدها میخوایید آپدیت کنید. این کار میکنید با استفاده از پارامتر update_fields انجام بدید.
r = Records.objects.get(pk=1)
r.name = "new name"
r.save(update_fields=["name"])
البته باید مراقب باشید که حتما فیلدهایی که میخوایید آپدیت کنید رو داخلش بزارید مگرنه آپدیت نمیشن.
پست بعدی آپدیت کردن چند آبجکت...
@TorhamDevCH
👍18❤9
آپدیت کردن چند آبجکت به صورت همزمان در #جنگو
فریمورک #django قابلیت آپدیت کردن دیتاها رو به روش ها مختلف داره که خیلی ها یا ازش بی خبر ان یا استفاده نمیکنن. بیایید ببینیم هر کدوم رو کجا استفاده کنی بهتره :)
مدل فرضی:
خب فرض کنید ما یک هدیه به مناسب عید نو روز میخواییم به کاربرا بدیم، مثلا میخاییم نفری ۲ هزار تومن هدیه بدیم D:
حالا چند روش وجود داره.
روش اول ( نوب):
خیلی ساده و البته درب و داغون در خیلی جهات. مشکل اول اینه که ما رو همه کاربرا حلقه میزنیم و هر بار آپدیت رو روی کاربرا صدا میزنیم یعنی برای هر یوزر یک درخواست اپدیت به دیتابیس میره که اگه ۱ میلیون یوزر داشته باشیم ۱ میلیون درخواست میره :).
( تو اینه پست به اینکه باید از F استفاده کنید یا کانکارنسی و اینا هندل کنید اشاره نمیکنم، پستها قبلی بخونید)
حالا روش بهتر چیه؟
روش بهتر:
همینقدر ساده :)
سناریو دوم: با بکاند یک بازی خفن رو داریم توسعه میدیم، داخل این بازی هر هفته یک ایونت اتفاق میوفته که افرادی که اون رو تموم کنن در آخر هفته یک تایتل به کنار اسمشون اضافه میشه و همچنین اگه امتیاز بالاتر از ۱۰ کسب کرده باشن به بالانس پول داخل گیمشون هم ۱۰۰ تا گلد اضافه میشه.
حالا بیایید فقط کوئری آپدیت این بهش ببینیم، فرض کنید این کوئری آخر هفته اجرا میشه. ( این فیلدا تو مدل فرض نداریم دیگه خودتون فرض کنید هست 😂❤️)
خب همینطور که خیلی معلومه مشکلات فراوان داخلش هست. بزرگترین مشکلش اینه که هر بار برای هر کاربر یک درخواست آپدیت میدیم که میشهه همون مشکل بالا، آما آیا این بار میشه از روش بالا استفاده کرد و اینو فیکسش کرد؟ نه
روش بالا زمانی کاربرد داره که فیلدها قراره یک مقداری ثابتی به همشون داده بشه، اینجا بعضی ها ۱۰۰ تا گلد میگیرن بعضی ها نه پس کار نمیکنه، اینجا ما میتونیم از فانکشن bulk_update جنگو استفاده کنیم.
همون حلقه بالا رو میزنید با این تفاوت که داخلش .save رو صدا نمیزنید و تمام آبجکتها رو داخل مموری آپدیت میکنید و بعد همچین حرکتی میزنید:
و تموم همرو با هم آپدیت میکنید با یک درخواست اینجا حتی میتونید یک قدم جلوتر برید و با اضافه کردن updated_fields به ورودی فانشکن و مشخص کردن اینکه دقیقا دوتا فیلد بالانس و نام فقط قرار آپدیت بشه بهترش کنید!
از این به بعد بهتر آپدیت کنید :)
@TorhamDevCH
فریمورک #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
❤🔥9👍2🐳2🆒2
یک چیزی که خیلی داخل بچههایی که جنگو یاد میگیرن میبینم اینه که خیلی تایمشون رو داخل یادگرفتن سیستم تمپلیت جنگو هدر میدن. اقا اگه قرار بکاند کار باشید و کارهای فول استکی انجام نمیدید(حتی اونجا هم بیشتر موارد لازم نیست) اصلا از این بخش بگذرید به کار شما نمیاد اصلا، بیشتر عمر بکاند تون قراره API توسعه بدید نه تمپلیت، به جاش روی خود API و ORM جنگو و DRF تمرکز کنید.
@TorhamDevCH
@TorhamDevCH
👍42❤2❤🔥1👎1
Forwarded from BenDev
Media is too big
VIEW IN TELEGRAM