TorhamDev | تورهام 😳
کوئریهای #جنگو lazy هستند اما این یعنی چی؟ کلا برای فهمیدن موضوع lazy فکر کنم این ویدیو شهریار شریعتی (Laziness in Python تنبلی در پایتون ) خوب باشه و بتونید بفهمیدش، یا اینکه نسخه انگلیسی از Computerphile. به این مثال توجه کنید: >>> q = Entry.objects…
برای اینکه بفهمید دقیقا چه زمانی کوئریها واقعا اجرا میشن میتونید این بخش از داکیومنت رو بخونید.
https://docs.djangoproject.com/en/5.0/ref/models/querysets/#when-querysets-are-evaluated
@TorhamDevCH
https://docs.djangoproject.com/en/5.0/ref/models/querysets/#when-querysets-are-evaluated
@TorhamDevCH
Django Project
QuerySet API reference | Django documentation
The web framework for perfectionists with deadlines.
👍2❤1
خب من یک چیز خیلی جالب و در این حال گیجکننده درباره ORM #جنگو فهمیدم.
تو پیام قبلی گفتم که کوئریهای #django دقیقا چه زمانی واقعا اجرا میشن. اما اینجا یک نکته دیگهای هم هست، جنگو نتیجه کوئریهارو کش میکنه.
و این خیلیییییییییییی مهمه، یعنی بعضی از جاها که فکر میکنید جنگو قرار دیتابیس هیت کنه هیت نمیکنه و از کش استفاده میکنه و بعضی جاها که فکر میکنید قرار کش استفاده کنه واقعا هیت میکنه. دونستن و فهمیدن این که چه زمانی کش استفاده میکنه چه زمانی نه حدود ۲ ساعت از من زمان گرفت :) ولی تو این پست توضیح میدم چیزی که فهمیدم رو.
اول این قانون تو ذهنتون داشته باشید: هر چیزی که باعث ایجاد یک QuerySet جدید بشه، باعث هیت به دیتابیس خواهد شد اگر اون کوئری اجرا بشه.
به مثال زیر دقت کنید:
خب تو این مثال فکر میکنید چندبار دیتابیس توسط جنگو هیت میشه؟ اگه ماجرا کش کردن ندونید ولی ماجرا اینکه چه زمانی واقعا اجرا میشه رو بدونید احتمالا با خودتون میگید ۳ بار داخل این کد جنگو دیتابیس رو هیت میکنه.
اما اگر من بگم فقط دو بار دیتابیس هیت میکنه چی؟
بزارید توضیح بدم. تو خط اول ما صرفا کوئری رو ساختیم و هیچ هیتی به دیتابیس نزدیم. تو خط دوم ما کوئری پرینت کردیم و اینجا اولین هیت به دیتابیس خورده میشه، ولی یک نکته اینجاست وقتی شما یک کوئری رو پرینت میکنید جنگو نمیاد کل کوئری رو اجرا کنه چون منطقی نیست!، مثلا اگر کوئری شما هزارتا خروجی داشته باشه شما اون هزارتا رو که داخل پرینت نمیخایید، در نتیجه جنگو فقط یک بخش از کوئری رو ران میکنه یا به عبارت دیگه از LIMIT استفاده میکنه!. تو این خط هیچ کش کردنی اتفاق نمیوفته(جلوتر میگم چرا)
خط بعدی ما از if استفاده کردیم و اینجا یک هیت دیگه به دیتابیس میخوره اما اینبار کل کوئری اجرا میشه و اینجاست که جنگو ریزالت کوئری میگیره و داخل کش ذخیره میکنه. تو خط بعدی که اومدیم حلقه زدیم روی کوئری جنگو دیگه نمیاد به دیتابیس درخواست بزنه و از کش استفاده میکنه!
در نتیجه اینجا فقط ۲ بار دیتابیس هیت میخوره.
ادامه داخل پیام بعد...
@TorhamDevCH
تو پیام قبلی گفتم که کوئریهای #django دقیقا چه زمانی واقعا اجرا میشن. اما اینجا یک نکته دیگهای هم هست، جنگو نتیجه کوئریهارو کش میکنه.
و این خیلیییییییییییی مهمه، یعنی بعضی از جاها که فکر میکنید جنگو قرار دیتابیس هیت کنه هیت نمیکنه و از کش استفاده میکنه و بعضی جاها که فکر میکنید قرار کش استفاده کنه واقعا هیت میکنه. دونستن و فهمیدن این که چه زمانی کش استفاده میکنه چه زمانی نه حدود ۲ ساعت از من زمان گرفت :) ولی تو این پست توضیح میدم چیزی که فهمیدم رو.
اول این قانون تو ذهنتون داشته باشید: هر چیزی که باعث ایجاد یک QuerySet جدید بشه، باعث هیت به دیتابیس خواهد شد اگر اون کوئری اجرا بشه.
به مثال زیر دقت کنید:
users = User.objects.all()
print(users)
if users:
for u in users:
print(u)
خب تو این مثال فکر میکنید چندبار دیتابیس توسط جنگو هیت میشه؟ اگه ماجرا کش کردن ندونید ولی ماجرا اینکه چه زمانی واقعا اجرا میشه رو بدونید احتمالا با خودتون میگید ۳ بار داخل این کد جنگو دیتابیس رو هیت میکنه.
اما اگر من بگم فقط دو بار دیتابیس هیت میکنه چی؟
بزارید توضیح بدم. تو خط اول ما صرفا کوئری رو ساختیم و هیچ هیتی به دیتابیس نزدیم. تو خط دوم ما کوئری پرینت کردیم و اینجا اولین هیت به دیتابیس خورده میشه، ولی یک نکته اینجاست وقتی شما یک کوئری رو پرینت میکنید جنگو نمیاد کل کوئری رو اجرا کنه چون منطقی نیست!، مثلا اگر کوئری شما هزارتا خروجی داشته باشه شما اون هزارتا رو که داخل پرینت نمیخایید، در نتیجه جنگو فقط یک بخش از کوئری رو ران میکنه یا به عبارت دیگه از LIMIT استفاده میکنه!. تو این خط هیچ کش کردنی اتفاق نمیوفته(جلوتر میگم چرا)
خط بعدی ما از if استفاده کردیم و اینجا یک هیت دیگه به دیتابیس میخوره اما اینبار کل کوئری اجرا میشه و اینجاست که جنگو ریزالت کوئری میگیره و داخل کش ذخیره میکنه. تو خط بعدی که اومدیم حلقه زدیم روی کوئری جنگو دیگه نمیاد به دیتابیس درخواست بزنه و از کش استفاده میکنه!
در نتیجه اینجا فقط ۲ بار دیتابیس هیت میخوره.
ادامه داخل پیام بعد...
@TorhamDevCH
👍11❤🔥3🆒2❤1
TorhamDev | تورهام 😳
خب من یک چیز خیلی جالب و در این حال گیجکننده درباره ORM #جنگو فهمیدم. تو پیام قبلی گفتم که کوئریهای #django دقیقا چه زمانی واقعا اجرا میشن. اما اینجا یک نکته دیگهای هم هست، جنگو نتیجه کوئریهارو کش میکنه. و این خیلیییییییییییی مهمه، یعنی بعضی از جاها…
خب از ماجرا و گفتههای خود داکیومنت جنگو میتونیم نتیجه بگیریم که جنگو زمانی یک ریزالت یک کوئری رو کش میکنه که کوئری کاملا اجرا بشه. یعنی داخل پرینت که بخشیش فقط اجرا شد کش اتفاق نمیوفته ولی داخل if کش اتفاق میوفته.
حالا شاید براتون سوال بشه کش رو چطوری میشه دید؟ میتونید با استفاده از _result_cache ببینیدش، اینطوری:
خروجی این خواهد بود: ( اگر فقط یک یوزر داشته باشیم)
همینطور که میبینید بعد از اجرا کردن پرینت رو کوئری کش هنوز None ( اینجا یکم شاید فهمیدنش سخت بشه با یکدونه یوزر ولی فکر کن هزارتا یوزر داریم پرینت فقط ۱۰ تاشو نشون خواهد داد)
پس بعد پرینت کش اتفاق نیوفتاد. ولی بعد از اینکه if اجرا شد کش پر شد و از اون به بعد جنگو از کش استفاده میکنه. البتهههههههه یک سری نکته هست که برمیگرده به قانونی که تو پست قبل گفتم. برای مثال:
تو این کد اینبار ۳ بار کوئری به دیتابیس میخوره. چرا؟ چون وقتی از .values استفاده میکنید یک QurySet جدید میسازید و اون کوئریست کش جدا خودش رو داره و از اونجایی که کشش خالیه در اون لحظه جنگو یک درخواست دیگه به دیتابیس خواهد زد :)
پایان\
@TorhamDevCH
حالا شاید براتون سوال بشه کش رو چطوری میشه دید؟ میتونید با استفاده از _result_cache ببینیدش، اینطوری:
users = User.objects.all()
print(users)
print("Cache: ", users._result_cache)
if users:
print("Cache: ", users._result_cache)
for u in users:
print(u)
خروجی این خواهد بود: ( اگر فقط یک یوزر داشته باشیم)
<QuerySet [<User: torham>]>
Cache: None
Cache: [<User: torham>]
torham
همینطور که میبینید بعد از اجرا کردن پرینت رو کوئری کش هنوز None ( اینجا یکم شاید فهمیدنش سخت بشه با یکدونه یوزر ولی فکر کن هزارتا یوزر داریم پرینت فقط ۱۰ تاشو نشون خواهد داد)
پس بعد پرینت کش اتفاق نیوفتاد. ولی بعد از اینکه if اجرا شد کش پر شد و از اون به بعد جنگو از کش استفاده میکنه. البتهههههههه یک سری نکته هست که برمیگرده به قانونی که تو پست قبل گفتم. برای مثال:
users = User.objects.all()
print(users)
if users:
for u in users.values("username"):
print(u)
تو این کد اینبار ۳ بار کوئری به دیتابیس میخوره. چرا؟ چون وقتی از .values استفاده میکنید یک QurySet جدید میسازید و اون کوئریست کش جدا خودش رو داره و از اونجایی که کشش خالیه در اون لحظه جنگو یک درخواست دیگه به دیتابیس خواهد زد :)
پایان\
@TorhamDevCH
👍7🆒4❤1
TorhamDev | تورهام 😳
خب من یک چیز خیلی جالب و در این حال گیجکننده درباره ORM #جنگو فهمیدم. تو پیام قبلی گفتم که کوئریهای #django دقیقا چه زمانی واقعا اجرا میشن. اما اینجا یک نکته دیگهای هم هست، جنگو نتیجه کوئریهارو کش میکنه. و این خیلیییییییییییی مهمه، یعنی بعضی از جاها…
من سر این ماجرا تو مصاحبه رد شدم :)
💔14👀3❤1
یک مبحثی که خیلی وقتها آدمهای رو داخل #جنگو گیج میکنه موضوع Aggregation هستش. برای مثال کوئری پایین:
خب این کوئری مشخصه چه کاری داره انجام میده، همه میتونن بفهمنش مخصوصا وقتی خروجی کوئری رو میبینن، اما اگر ازشون بپرسید خب 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 میتونه این باشه:
خب از اونجایی که ORM جنگو در نهایت قرار کار همین SQL نوشتن برای شما انجام بده و کوئری شمارو به SQL تبدیل کنه شما دقیقا همین کوئری میتونید داخل جنگو به این صورت بنویسید:
میتونید لیست فانکشنهای Aggregation خود SQL داخل این لینک ببینید و ساپورت جنگو هم میتونید داخل این لینک ببینید.
در نهایت از دانش SQL غافل نباشید و حتما یادش بیگیرید. هرچی بیشتر SQL بدونید زندگی راحتتری خواهید داشت.
@TorhamDevCH
>>> 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
W3Schools
W3Schools offers free online tutorials, references and exercises in all the major languages of the web. Covering popular subjects like HTML, CSS, JavaScript, Python, SQL, Java, and many, many more.
👍7✍3❤1🌚1
If you think you're perfect already, then you never will be.
—Cristiano Ronaldo
⚡11👍1
توصیه خود #جنگو اینه که آقا اگه به کوئریت شک داری احساس میکنی مقدار اشتباه داره میده یا اینی که نوشتی نتیجهاش اون SQL که میخواستی نشده، بشین ببین واقعا چه کوئری SQL ای ساخته که اینکه چطور این کار کنید تو یکی از پستها بالا گفته بودم.
@TorhamDevCH
@TorhamDevCH
👍2
Forwarded from TorhamDev | تورهام 😳
شاید برای شما هم سوال بود که چطور SQL کوئریهایی که با ORM جنگو میزنیم رو ببینیم؟
https://b0uh.github.io/django-show-me-the-sql.html
@TorhamDevCH
https://b0uh.github.io/django-show-me-the-sql.html
@TorhamDevCH
b0uh.github.io
Django: show me the SQL - Thomas Loiret - Random thoughts
8 different ways to see the SQL generated by Django
❤🔥2
چطوری داخل #جنگو manager رو rename کنیم؟
همه آپهای جنگو حداقل یک منیجر دارن که به صورت پیشفرض اون منیجیر objects خونده میشه و شما همچین استفاده ای ازش میکنید:
Users.objects.all()
اما شاید بخوایید از کلمه objects به عنوان فیلد استفاده کنید یا به هر دلیلی میخایید این اسم رو تغییر بدید. برای اینکار شما به راحتی میتونید یک فیلد داخل مدل جنگو بسازید و اون رو برابر با models.Manager() کنید با اینکار جنگو میاد objects رو حذف و از این فیلد استفاده میکنه و اگر بعد این کار شما سعی کنید از objects استفاده کنید ارور AttributeError میخورید.
مثال:
from django.db import models
class Person(models.Model):
# ...
people = models.Manager()
رو این مدل اگه objects رو صدا کنید ارور میخورید و برای کوئری زدن باید از people استفاده کنید.
Person.objects.all() ❌
Person.people.all() ✅
@TorhamDevCH
همه آپهای جنگو حداقل یک منیجر دارن که به صورت پیشفرض اون منیجیر objects خونده میشه و شما همچین استفاده ای ازش میکنید:
Users.objects.all()
اما شاید بخوایید از کلمه objects به عنوان فیلد استفاده کنید یا به هر دلیلی میخایید این اسم رو تغییر بدید. برای اینکار شما به راحتی میتونید یک فیلد داخل مدل جنگو بسازید و اون رو برابر با models.Manager() کنید با اینکار جنگو میاد objects رو حذف و از این فیلد استفاده میکنه و اگر بعد این کار شما سعی کنید از objects استفاده کنید ارور AttributeError میخورید.
مثال:
from django.db import models
class Person(models.Model):
# ...
people = models.Manager()
رو این مدل اگه objects رو صدا کنید ارور میخورید و برای کوئری زدن باید از people استفاده کنید.
Person.objects.all() ❌
Person.people.all() ✅
@TorhamDevCH
👍8
وقتی که بعد از پنج ساعت توضیح طرف، نفهمیدم مشکلش چیه، ولی بلاخره کدش رو فرستاد🗿🤝
با من با کد صحبت کن من دیگر زبان انسانها را فراموش کرده ام
@TorhamDevCH
@TorhamDevCH
🤓8🤪2👍1👌1
کوئری raw زدن در #جنگو
درسته #django یک ORM خوب داره و تقریبا تمام چیزهایی که لازم دارید رو ساپورت میکنه اما شما میتونید داخل جنگو مستقیم کوئری SQL ران کنید!
ران کردن این کوئریها به صورت raw میتونه تو دو لایه در جنگو انجام بشه. یک در لایه مدل خودتون و دومی تو لایه پایین تر مستقیم با کانکشن دیتابیس.
تو حال اول جنگو سعی میکنه که خروجی SQL را براتون Map کنه و خروجی دوباره مدل براتون برگردونه حتی وقتی دارید raw میزنید مثال:
مدل فرضی:
کوئری مثال:
این کوئری دقیقا معادل objects.all() و جنگو خروجی رو بر اساس اسم فیلدها مپ میکنه به مدل. این مهمه ها! بر اساس اسم فیلد! یعنی شما میتونید کوئری رو حتی رو یک تیبل دیگه بزنید و تا زمانی که اسم فیلدا خروجیتون با مدل یکی باشه جنگو اونهارو مپ میکنه. مثال:
بله میتونیم از AS استفاده کنیم و اسم فیلدا مشابه مدلمون بزاریم. خود جنگو هم یک فیچر داره که براتون همین AS رو میزنه!
میتونید از پارامتر translations استفاده کنید برای اینکار.
میتونید برخی از فیلدها رو انتخاب نکنید!
برای مثال:
برای مثال داخل این raw کوئری ما فیلد last_name رو انتخاب نکردیم. حالا چه اتفاقی افتاده؟ همچنان اگه شما فیلد last_name صدا بزنید مشکلی پیش نمیاد و دریافتش میکنید ولیییییی جنگو از اونجایی که اون فیلد داخل کوئری وارد نکرده بودید و خروجیش رو نداشته خودش میاد همون لحضه دوباره یک درخواست به دیتابیس میزنه و اون دریافت میکنه!
لایه خود کانکشن
اگه این مپینگ رو نمیخایید و کلا میخوایید یک کوئری مستقیم بزنید مثل زمانی که از یک کتابخونه معمولی تو پایتون برای دیتابیس استفاده میکنید میتونید از connection در جنگو استفاده کنید! مثال:
خروجی یک لیست از نتایج خواهد بود. البته میتونید با یک حرکت ساده این لیست مپ کنید خودتون و در نهایت یک دیکشنری داشته باشید.
و آخرین نکته اینکه وقتی دارید از raw استفاده میکنید دیگه اسلایس کردن رو لول کوئری لیمیت نمیزاره و بهتره از LIMIT استفاده کنید داخل خود کوئری SQL
@TorhamDevCH
درسته #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.denoscription]
return [dict(zip(columns, row)) for row in cursor.fetchall()]
و آخرین نکته اینکه وقتی دارید از raw استفاده میکنید دیگه اسلایس کردن رو لول کوئری لیمیت نمیزاره و بهتره از LIMIT استفاده کنید داخل خود کوئری SQL
@TorhamDevCH
👍9❤2🔥1
👍7❤🔥1
Transaction per-request in #django
#جنگو قابلیت انجام ترنزکشن با دیتابیس به شما میده در چندید حالت مختلف یکی از حالتها ترنزاکشن بر هر ریکوئستِ، یعنی چی؟ یعنی جنگو برای هر ریکوئستی که شما میگیرید یک ترنزاکشن باز میکنه یا به عباری برای هر ویو فانکشن شما یک atomic() ران میکنه! این قابلیت به شکل پیشفرض غیر فعاله ولی میتونید با اضافه ATOMIC_REQUESTS داخل کانفیگ دیتابیسی که میخوایید این حرکت باهاش بزنید این قابلیت فعال کنید.
این کار هر ریکوئست شمارو داخل یک ترنزاکشن warp میکنه و اگر ویو شما هر اروری بده ( ارور raise کنه ) ترنزاکشن rollback میخوره و تغییرات اعمال نمیشه.
@TorhamDevCH
#جنگو قابلیت انجام ترنزکشن با دیتابیس به شما میده در چندید حالت مختلف یکی از حالتها ترنزاکشن بر هر ریکوئستِ، یعنی چی؟ یعنی جنگو برای هر ریکوئستی که شما میگیرید یک ترنزاکشن باز میکنه یا به عباری برای هر ویو فانکشن شما یک atomic() ران میکنه! این قابلیت به شکل پیشفرض غیر فعاله ولی میتونید با اضافه ATOMIC_REQUESTS داخل کانفیگ دیتابیسی که میخوایید این حرکت باهاش بزنید این قابلیت فعال کنید.
این کار هر ریکوئست شمارو داخل یک ترنزاکشن warp میکنه و اگر ویو شما هر اروری بده ( ارور raise کنه ) ترنزاکشن rollback میخوره و تغییرات اعمال نمیشه.
@TorhamDevCH
Django Project
Settings | Django documentation
The web framework for perfectionists with deadlines.
👍4
TorhamDev | تورهام 😳
Transaction per-request in #django #جنگو قابلیت انجام ترنزکشن با دیتابیس به شما میده در چندید حالت مختلف یکی از حالتها ترنزاکشن بر هر ریکوئستِ، یعنی چی؟ یعنی جنگو برای هر ریکوئستی که شما میگیرید یک ترنزاکشن باز میکنه یا به عباری برای هر ویو فانکشن شما…
چیزای زیادی درباره transaction هست که میگم بعدا فعلا خستهام، فردا دربارشون مینویسم✌️ فعلا اینو داشته باشید :)
❤4👍1
Forwarded from Sadra Codes
نسخه جدید Hey رو رلیز کردم! 🙌
+ خیلی سریعتره نسبت به ورژن قبلی.
+ راهاندازیش خیلی سادهتر و سریعتر شده چون از داشبورد mindsdb استفاده نمیکنه و سوییچ کرده به mdb.ai.
+ تقریبا همهجاش قابل شخصیسازی هست. از پراومت تا انیمیشن لودینگ!
نحوه نصب و ریپازیتوری: github.com/lnxpy/hey
و اینکه از اون اپهاست که خودم شخصا استفاده میکنم و پایداره. :))
+ خیلی سریعتره نسبت به ورژن قبلی.
+ راهاندازیش خیلی سادهتر و سریعتر شده چون از داشبورد mindsdb استفاده نمیکنه و سوییچ کرده به mdb.ai.
+ تقریبا همهجاش قابل شخصیسازی هست. از پراومت تا انیمیشن لودینگ!
نحوه نصب و ریپازیتوری: github.com/lnxpy/hey
و اینکه از اون اپهاست که خودم شخصا استفاده میکنم و پایداره. :))
👍2
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