TorhamDev | تورهام 😳 – Telegram
TorhamDev | تورهام 😳
2.84K subscribers
420 photos
49 videos
21 files
591 links
code, linux and anything related.

• YouTube
youtube.com/@techwithtori

• CodeNaline Podcast
@CodeNaline

• Group
@GothamProgrammers

• Github
github.com/TorhamDev

• Email
torhamdev@gmail.com
Download Telegram
چطوری داخل #جنگو 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
👍8
وقتی که بعد از پنج ساعت توضیح طرف، نفهمیدم مشکلش چیه، ولی بلاخره کدش رو فرستاد🗿🤝

با من با کد صحبت کن من دیگر زبان انسان‌ها را فراموش کرده ام

@TorhamDevCH
🤓8🤪2👍1👌1
کوئری 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.denoscription]
return [dict(zip(columns, row)) for row in cursor.fetchall()]


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

@TorhamDevCH
👍92🔥1
دوسِتان ایا پست‌های جنگو برایتان مفید است؟
Anonymous Poll
79%
بله
21%
خیر
👍7❤‍🔥1
Transaction per-request in #django


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

@TorhamDevCH
👍4
Forwarded from Sadra Codes
نسخه جدید Hey رو رلیز کردم! 🙌

+ خیلی سریعتره نسبت به ورژن قبلی.

+ راه‌اندازیش خیلی ساده‌تر و سریعتر شده چون از داشبورد mindsdb استفاده نمی‌کنه و سوییچ کرده به mdb.ai.

+ تقریبا همه‌جاش قابل شخصی‌سازی هست. از پراومت تا انیمیشن لودینگ!

نحوه نصب و ریپازیتوری: github.com/lnxpy/hey

و اینکه از اون اپ‌هاست که خودم شخصا استفاده می‌کنم و پایداره. :))
👍2
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
10
Transaction.on_commit in #django

قبل اینکه بریم سراغ موضوع اصلی بیایید چندتا نکته دیگه رو بفهمیم. داخل #جنگو شما لازم نیست تغییراتتون رو کامیت کنید مثلا اگر چیزی داخل دیتابیس آپدیت میکنید دیگه نمیایید خط بعد بنویسید 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
8👍5❤‍🔥1
Field level validation in #DRF

خوب خود فریمورک 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🤝61
pass: can i stay here? 👉👈
🤣8
TorhamDev | تورهام 😳
pass: can i stay here? 👉👈
یک نکته جالب:

تو عکس یک تودو میبینید که نوشته منطق 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 یا همون میان‌بر داره که کد زدن شما رو کمتر میکنه و خیلی کاربردی ان. میتونید تمام اونها از این مسیر ایمپورت کنید:

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
👍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
👍14💋51👎1
امروز یک پست جالب درباره آپتومایز کردن کوئری‌های جنگو مینویسم ✌️
🔥11❤‍🔥2👍1👌1
این چند روز اخیر داشتم رو این پروژه کار میکردم. خیلی چیزا جدید یادگرفتم. هنوز هم روش کار میکنم، هر روز که چیزا جدید میخونم روش پیاده میکنم، تا الان باب میل خودم هست :) حالا فرستادمش برای اهل فن‌ها تا مشکل بگیرن ازش بدم بیاد XD

https://github.com/TorhamDev/simple-wallet-improved


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



@TorhamDevCH
بیایید به بهانه آپتومایز کردن کوئری‌های #جنگو چندتا چیز جدید درباره ORM جنگو یاد بگیریم

شما همیشه وقتی چیزی رو نیاز دارید داخل جنگو همچین کوئری میزنید:

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👌21