Ninja Learn | نینجا لرن
1.26K subscribers
95 photos
36 videos
11 files
306 links
یادگیری برنامه نویسی به سبک نینجا 🥷
اینجا چیزایی یاد میگیری که فقط نینجاهای وب‌ بلدن 🤫

📄 Send me post: https://t.iss.one/NoronChat_bot?start=sec-fdggghgebe

👥 ɢʀᴏᴜᴘ: https://t.iss.one/+td1EcO_YfSphNTlk
Download Telegram
📕 کتاب REST API Design Rulebook

📌 فصل دوم: Identifier Design with URIs

📍پارت: سوم

#book

💎 Document 💎
یک سند مثل یه مفهوم singular هست که شبیه یه instance از یه آبجکت یا رکورد توی دیتابیسه.
معمولاً یه سند شامل چند تا فیلد با مقادیر مشخص و یه سری لینک به منابع دیگه‌ست.

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

این URI‌ها هر کدوم یه منبع سند رو نشون میدن:
https://api.soccer.restapi.org/leagues/seattle

https://api.soccer.restapi.org/leagues/seattle/teams/trebuchet

https://api.soccer.restapi.org/leagues/seattle/teams/trebuchet/players/mike


یه سند ممکنه منابع فرعی داشته باشه که زیرمجموعه‌های خاصش رو نشون میدن.
چون می‌تونه چندین نوع منبع مختلف رو زیر یه والد قرار بده، سند گزینه مناسبی برای منبع اصلی یه REST API (که بهش "docroot" هم می‌گن) محسوب می‌شه.

اینم یه مثال از URI که docroot رو نشون میده و همون اندپوینت API فوتباله:
https://api.soccer.restapi.org


💎 Collection 💎
یک مجموعه (Collection) مثل یه دایرکتوریه که سرور اون رو مدیریت می‌کنه و شامل یه سری منابعه.
کلاینت‌ها می‌تونن پیشنهاد بدن که منابع جدید به مجموعه اضافه بشن، ولی در نهایت این خود مجموعه‌ست که تصمیم می‌گیره یه منبع جدید بسازه یا نه. مجموعه خودش انتخاب می‌کنه چی رو توی خودش نگه داره و همچنین URI (آدرس) هر منبعی که داخلش هست رو هم خودش تعیین می‌کنه.

هر کدوم از URI‌های زیر یه مجموعه (Collection) رو نشون می‌ده:
https://api.soccer.restapi.org/leagues

https://api.soccer.restapi.org/leagues/seattle/teams

https://api.soccer.restapi.org/leagues/seattle/teams/trebuchet/players


💎 Store 💎
یک Store منبعیه که توسط کلاینت مدیریت می‌شه. Store به کلاینت API این امکان رو می‌ده که منابع رو داخلش بذاره، دوباره اون‌ها رو بیرون بیاره، و تصمیم بگیره کی اون‌ها رو حذف کنه. خود Store منابع جدید نمی‌سازه، بنابراین هیچ وقت URI جدید ایجاد نمی‌کنه. در عوض، هر منبعی که داخل مخزن گذاشته می‌شه، URI داره که کلاینت موقع اضافه کردنش انتخاب کرده.

تعامل زیر یه مثال از کاربر (با شناسه 1234) در یه برنامه کلاینت رو نشون می‌ده که از یه API فرضی فوتبال استفاده می‌کنه تا یه سند به اسم «آلونزو» رو توی مخزن علاقه‌مندی‌های خودش قرار بده:

PUT /users/1234/favorites/alonso


💎 Controller 💎
کنترلر یه مفهوم رویه‌ای (procedural) رو مدل‌سازی می‌کنه. کنترلرها شبیه به توابع اجرایی هستن که ورودی و خروجی دارن و پارامترها و مقادیر برگشتی رو مدیریت می‌کنن. مثل فرم‌های HTML در وب اپلیکیشن‌های سنتی، یک REST API هم از منابع کنترلر استفاده می‌کنه تا کارهایی انجام بده که به‌طور منطقی به یکی از متد های استاندارد (Create، Retrieve، Update و Delete، که بهشون CRUD می‌گن) مربوط نمی‌شن.

معمولاً اسم‌های کنترلر به‌عنوان آخرین قسمت تو مسیر URI ظاهر می‌شن و بعد از اون‌ها هیچ منبع فرعی دیگه‌ای توی سلسله‌مراتب نیست. مثال زیر یه کنترلر رو نشون می‌ده که به کلاینت اجازه می‌ده یه هشدار رو دوباره برای کاربر بفرسته:

POST /alerts/245743/resend


@ninja_learn_ir
5👍2
ضمن خوش آمد گویی به دوستانی که تازه به جمعمون اضافه شدن 🌹، باید بگم که ما هر روز بخشی از خلاصه کتاب REST API Design Rulebook رو داخل کانال منتشر میکنیم و میتونید مطالعه کنید.

فایل PDF هم قبلا آپلود شده، فقط کافیه سرچ بزنید 🔎

از هشتگ #کتاب هم میتونید استفاده کنید برای دسترسی سریعتر #️⃣

استفاده کنید 😄❤️
7👍2
💎 ساخت کامندهای کاستوم توی جنگو 💎

امروز می‌خوام یه موضوع خفن از جنگو رو بهتون توضیح بدم، اینکه چجوری می‌تونیم کامندهای کاستوم توی جنگو بسازیم. 😎

احتمالاً تا حالا با کامندهای پیش‌فرض جنگو مثل migrate, makemigrations, یا runserver کار کردید، ولی خب بعضی وقتا پیش میاد که نیاز داریم یه سری کارهای خاص رو توی پروژه انجام بدیم که با این کامندها نمیشه. اینجاست که کامندهای کاستوم وارد میشن! 🤓

مراحل ساخت کامند کاستوم توی جنگو

1⃣ ساخت پوشه management/commands

   اول باید توی یکی از اپلیکیشن‌هاتون یه پوشه به اسم management بسازی و داخلش یه پوشه دیگه به اسم commands بذاری. این پوشه جاییه که همه کامندهای کاستومت توش قرار می‌گیره. یادت باشه که توی هر دو پوشه باید فایل init.py رو هم بسازی که پایتون این پوشه‌ها رو بشناسه.

   مسیرش میشه چیزی شبیه این:
    |— your_app/
      |— management/
      |— __init__.py
         |—commands/
         |— __init__.py
  


2⃣ ساخت فایل کامند
   حالا نوبت اینه که کامند خودتو بسازی فرض کنیم می‌خوای یه کامند بسازی که اطلاعات کاربرا رو پرینت کنه. یه فایل به اسم print_users.py داخل پوشه commands بساز و این کد رو داخلش بذار:
from django.core.management.base import BaseCommand 
from your_app.models import User

   class Command(BaseCommand):
       help = 'چاپ کردن لیست کاربرا'

       def handle(self, *args, **kwargs):
           users = User.objects.all()
           for user in users:
                            self.stdout.write(self.style.SUCCESS(f'User: {user.username}'))
  

   اینجا:
   - از کلاس BaseCommand استفاده کردیم تا یه کامند جدید بسازیم.
   - متد handle جاییه که منطق اصلی کامند رو می‌نویسیم. هرچی تو این متد بنویسی موقع اجرای کامند اجرا میشه.
   - با self.stdout.write می‌تونیم پیام‌ها رو توی کنسول پرینت کنیم.

3⃣ اجرای کامند
   بعد از اینکه فایل رو ساختی، برای اجرای کامندت، می‌تونی این دستور رو توی ترمینال وارد کنی:
   python manage.py print_users
  

   اگه همه چیز درست باشه، لیست کاربرا رو توی کنسول می‌بینی. 🎉

امکانات بیشتر
می‌تونی کامندت رو حرفه‌ای‌تر هم بکنی:
- با اضافه کردن آرگومان (مثل python manage.py print_users --active برای کاربرای فعال)
- یا استفاده از ورودی‌های کاربر (مثل پرسیدن سوال توی ترمینال و دریافت جواب)

مثال اضافه کردن آرگومان:
def add_arguments(self, parser):
    parser.add_argument('--active', action='store_true', help='فقط کاربرای فعال')

حالا اگه --active رو اضافه کنی، فقط کاربرای فعال رو نشون میده.

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

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

#backend #django


@ninja_learn_ir
👍93🔥2🥰1
سلام دوستان 👋

امروز تو یکی از گروه‌ها یه چیزی دیدم که گفتم بهتره در موردش صحبت کنم. خیلی از دوستانی که اینجا هستن سنشون زیر ۱۸ ساله. خب دوستان، تو این سن استخدام شدن تو یه شرکت خیلی سخته (نمی‌گم غیرممکنه، ولی خیلی کمه جایی پیدا بشه که با این سن کم استخدام کنه). دلیلش هم واضحه؛ شما نه کارت پایان خدمت دارین، نه وقت کافی چون درگیر درس و مدرسه هستین و...

پیشنهاد من اینه که تو این سن به‌جای اینکه دنبال استخدام شدن باشین، بیشتر روی قوی کردن مهارت‌های فنی و نرم خودتون کار کنین. اگه بتونین تو این چند سال خوب رو خودتون کار کنین، مهارت‌هاتون رو بالا ببرین و شبکه‌سازی کنین، وقتی به سن ۲۰ سالگی می‌رسین، خیلی‌ها دنبال شما خواهند بود.

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

برای همتون ارزوی بهترین هارو میکنم ❤️
8👍1
📕 کتاب REST API Design Rulebook

📌 فصل دوم: Identifier Design with URIs

📍پارت: چهارم

#کتاب

💎 URI Path Design 💎
هر قسمت از مسیر URI که با اسلش (/) جدا می‌شه، یه فرصت طراحی حساب می‌شه. اگه به هر قسمت از مسیر یه ارزش معنی‌دار بدیم، می‌تونیم ساختار سلسله‌مراتبی منابع یه REST API رو بهتر نشون بدیم.

شکل 2-1 با استفاده از نشانه‌گذاری WRML نشون می‌ده که چطور طراحی مسیر URI با مدل منابعی که ارائه می‌ده ارتباط داره.

این بخش قوانینی رو ارائه می‌ده که مربوط به طراحی مسیرهای URI معنی‌دار هستن.

⭕️ برای نام‌گذاری اسناد باید از اسم مفرد استفاده بشه.
یه URI که نمایانگر یه سند هست، باید توی قسمت مسیر از یک اسم یا عبارت اسمی مفرد استفاده کنه.

مثال: URI برای یه سند مربوط به یک بازیکن به شکل مفرد خواهد بود:

https://api.soccer.restapi.org/leagues/seattle/teams/trebuchet/players/claudio


⭕️ برای نام‌گذاری کالکشن ها باید از اسم جمع استفاده بشه.
‏URI که یه مجموعه رو شناسایی می‌کنه، باید توی قسمت مسیر از اسم یا عبارت اسمی جمع استفاده کنه. نام کالکشن باید طوری انتخاب بشه که نشون بده چه چیزهایی به‌صورت یکسان داخلش قرار داره.

مثال: URI برای یه مجموعه از اسناد بازیکنان از فرم جمع اسم منابع استفاده می‌کنه:

https://api.soccer.restapi.org/leagues/seattle/teams/trebuchet/players


⭕️ برای نام‌گذاری Store ها هم باید از اسم جمع استفاده بشه. URI که یه Store رو شناسایی می‌کنه، باید توی قسمت مسیر از اسم یا عبارت اسمی جمع استفاده کنه.

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

https://api.music.restapi.org/artists/mikemassedotcom/playlists


⭕️ برای نام‌گذاری کنترلر باید از یک فعل یا عبارت فعلی استفاده بشه، درست مثل عملکرد یه تابع در برنامه‌نویسی. URI که یه منبع کنترلر رو شناسایی می‌کنه، باید به‌طوری نام‌گذاری بشه که نشون‌دهنده‌ی عملی باشه که قراره انجام بشه.

مثال‌ها:
https://api.college.restapi.org/students/morgan/register
https://api.example.restapi.org/lists/4324/dedupe
https://api.ognom.restapi.org/dbs/reindex
https://api.build.restapi.org/qa/nightly/runTestSuite


⭕️ بعضی از بخش‌های مسیر URI ثابت هستند؛ یعنی اسم‌های مشخصی دارن که توسط طراح REST API انتخاب می‌شن. اما بقیه بخش‌های مسیر متغیر هستن، یعنی به‌طور خودکار با یه شناسه پر می‌شن که به خاص بودن URI کمک می‌کنه. در نگارش الگوی URI، طراح‌ها می‌تونن هم بخش‌های ثابت و هم متغیر رو مشخص کنن. یک الگوی URI شامل متغیرهایی هست که قبل از استفاده باید جایگزین بشن.

مثال زیر سه متغیر داره: ‌`leagueId`، ‌`teamId`، و ‌`playerId`:

https://api.soccer.restapi.org/leagues/{leagueId}/teams/{teamId}/players/{playerId}


جایگزینی متغیرهای الگوی URI ممکنه توسط REST API یا کلاینت‌ها انجام بشه. هر جایگزینی می‌تونه از یه شناسه عددی یا الفبایی-عددی استفاده کنه، مثل مثال‌های زیر:

https://api.soccer.restapi.org/leagues/seattle/teams/trebuchet/players/21
https://api.soccer.restapi.org/games/3fd65a60-cb8b-11e0-9572-0800200c9a66


در اینجا، مقدار ‌`21` در قسمت متغیر ‌`playerId` قرار گرفته و مقدار UUID در قسمت متغیر ‌`gameId` جایگزین شده.


⭕️ نباید از نام‌های توابع CRUD در URI‌ها استفاده بشه. URI‌ها باید برای شناسایی منحصر‌به‌فرد منابع استفاده بشن و باید طبق قوانین بالا نام‌گذاری بشن. همون‌طور که در بخش «روش‌های درخواست» توضیح داده شده، روش‌های HTTP برای مشخص کردن عملکرد CRUD استفاده می‌شن.

مثلاً این طراحی API ارجحیت داره:
DELETE /users/1234


در مقابل، الگوهای نادرست (که نباید انجام بشن) به شکل زیر هستن:

GET /deleteUser?id=1234
GET /deleteUser/1234
DELETE /deleteUser/1234
POST /users/1234/delete


@ninja_learn_ir
2👍2
💎 پکیج joi در جاوااسکریپت 💎

امروز میخوام در مورد یه پکیج خفن به اسم joi توی جاوااسکریپت صحبت کنم. 😎

خب بذارید اول بگم joi چیه و به چه دردی میخوره. 💡

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

حالا اینجاست که joi وارد میشه! 😎
با این پکیج می‌تونی خیلی راحت و سریع همه چیز رو ولیدیت کنی و نیازی نیست کلی کد بنویسی. 🙌

چطوری نصب کنیم؟
خیلی ساده‌ست:
npm install joi

حالا استفاده‌اش چطوریه؟
یه مثال ساده میزنم:

const Joi = require('joi');

const schema = Joi.object({
username: Joi.string().alphanum().min(3).max(30).required(),
password: Joi.string().pattern(new RegExp('^[a-zA-Z0-9]{3,30}$')).required(),
email: Joi.string().email({ tlds: { allow: false } })
});

حالا میایم دیتا رو با این اسکیما ولیدیت می‌کنیم
const { error, value } = schema.validate({ 
username: 'user123',
password: 'mypassword',
email: '[email protected]'
});

if (error) {
console.log('خطا: ', error.details);
} else {
console.log('همه چی اوکیه: ', value);
}

همونطور که می‌بینید، با joi می‌تونید به راحتی اسکیماهای مختلف برای ولیدیت کردن دیتا تعریف کنید.
نیازی نیست نگران درست بودن دیتا باشید، چون خودش همه چیز رو چک می‌کنه. 😎💪

برای مطالعه بیشتر میتونید به این سایت مراجعه کنید 😁

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

#JOIValidation #JavaScriptTips #BackendDevelopment


@ninja_learn_ir
👏51👍1
💎 خب Uvicorn چیه و چرا مهمه؟ 💎

سلام دوستان! امروز می‌خوام در مورد Uvicorn صحبت کنم که شاید به گوشتون خورده باشه، مخصوصاً اگه با FastAPI یا Starlette کار کرده باشید. 😎

حالا Uvicorn چیه؟

خب Uvicorn یه ASGI سرور خیلی سریع و سبک هست که برای اجرای اپلیکیشن‌های پایتونی که از پروتکل ASGI استفاده می‌کنن، طراحی شده. برعکس WSGI که واسه فریمورک‌های سنتی مثل Django و Flask استفاده میشه، ASGI این قابلیت رو داره که اپلیکیشن‌های آسنکرون (async) رو هندل کنه. 🚀

به زبون ساده‌تر، اگه بخوای یه اپلیکیشن پایتونی سریع و مقیاس‌پذیر بسازی که هم درخواست‌های همزمان رو به‌خوبی هندل کنه و هم نیاز به عملکرد بالا داشته باشه، Uvicorn گزینه‌ی ایده‌آلیه. 💥

مزایای Uvicorn

سرعت بالا 🚀:
یکی از اصلی‌ترین دلایل محبوبیت Uvicorn سرعت بالاشه. به‌خاطر استفاده از کتابخونه‌های بهینه مثل uvloop و httptools.

پشتیبانی از async :
خب Uvicorn به طور کامل از async و await پشتیبانی می‌کنه، که این یعنی تو میتونی باهاش اپلیکیشن‌های خیلی مقیاس‌پذیر بسازی که درخواست‌های زیادی رو به‌طور همزمان هندل کنن.

سادگی در استفاده 🛠️:
استفاده از Uvicorn خیلی ساده‌ست و به راحتی میتونی اونو با FastAPI یا هر فریمورکی که از ASGI پشتیبانی میکنه استفاده کنی.

چجوری Uvicorn رو نصب و اجرا کنیم؟

نصب Uvicorn فوق‌العاده ساده‌ست. با یه دستور می‌تونی نصبش کنی:

pip install uvicorn

بعد از نصب، کافیه اپلیکیشنت رو اجرا کنی. فرض کن یه اپلیکیشن FastAPI داری:

uvicorn main:app --reload

اینجا main اسم فایل و app اسم اپلیکیشنی هست که توش تعریف شده. با سویچ --reload هم Uvicorn در حالت توسعه قرار می‌گیره و هر تغییری که بدی به‌صورت خودکار اعمال میشه.

حالا Uvicorn کجاها کاربرد داره؟

اگه با فریمورک‌هایی مثل FastAPI کار می‌کنی یا حتی می‌خوای از اپلیکیشن‌های سنگین و با تعداد درخواست بالا پشتیبانی کنی، حتماً باید از Uvicorn استفاده کنی. مثلاً برای APIهای real-time، وب‌سرویس‌ها و اپلیکیشن‌های چت Uvicorn می‌تونه فوق‌العاده مفید باشه.

جمع‌بندی 🎯

در کل، Uvicorn یه سرور سریع، سبک و مقیاس‌پذیره که اگه با اپلیکیشن‌های آسنکرون کار می‌کنی یا می‌خوای عملکرد بالایی توی هندل کردن درخواست‌ها داشته باشی، بهترین گزینه‌ست. 😎🔥

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

#django #uvcorn #web_server #asgi_server

@ninja_learn_ir
👍62
📕 کتاب REST API Design Rulebook

📌 فصل دوم: Identifier Design with URIs

📍پارت: پنجم

#کتاب

💎 URI Query Design 💎
این بخش درباره قوانین طراحی کوئری‌های URI صحبت می‌کنه. طبق استاندارد RFC 3986، کوئری URI (که اختیاری هست) بعد از مسیر (path) و قبل از تکه‌ی اختیاری (fragment) قرار می‌گیره:

URI = schema "://" authority "/" path [ "?" query ] [ "#" fragment ]


کوئری تو URI به شناسایی منحصربه‌فرد بودن یه منبع کمک می‌کنه. به این مثال توجه کن:

https://api.college.restapi.org/students/morgan/send-sms

https://api.college.restapi.org/students/morgan/send-sms?text=hello


اولی URI یه منبعی هست که پیامک می‌فرسته. دومی همون منبع رو نشون می‌ده ولی با این تفاوت که توش پیام "hello" فرستاده می‌شه.

قسمت کوئری URI شامل یه سری پارامتره که به عنوان یه نوع تغییر یا نسخه‌ای از منبع اصلی (که تو بخش مسیر URI تعریف شده) تفسیر می‌شه. پس این دو منبع دقیقاً یکی نیستن، ولی به هم خیلی نزدیکن.

قسمت کوئری تو URI می‌تونه به کلاینت‌ها امکانات بیشتری مثل جستجو یا فیلتر کردن بده. به همین دلیل، این بخش از URI ممکنه برای کلاینت‌های یه REST API شفاف باشه (یعنی زیاد براشون مهم نباشه).

در ضمن، کل URI یه منبع باید برای واسطه‌های شبکه مثل کش‌های HTTP غیرشفاف (opaque) باشه. کش‌ها نباید رفتار خودشون رو فقط بر اساس وجود یا عدم وجود کوئری توی URI تغییر بدن. یعنی پیام‌های پاسخ نباید فقط به خاطر وجود کوئری از کش شدن حذف بشن. همونطور که تو فصل ۴ توضیح داده شده، برای کنترل رفتار واسطه‌های کش باید از هدرهای HTTP استفاده بشه، نه کوئری‌ها.

⭕️ قسمت کوئری URI می‌تونه برای فیلتر کردن کالکشن ها یا Store ها استفاده بشه.
یعنی می‌تونی ازش برای مشخص کردن معیار جستجو توی یه مجموعه یا ذخیره استفاده کنی. یه مثال بزنیم:

GET /users
GET /users?role=admin


تو درخواست اول، کلاینت از سرور لیست همه کاربران رو درخواست می‌کنه.
تو درخواست دوم، کلاینت از سرور لیست کاربرانی که نقش (role) "admin" دارن رو می‌خواد.

در واقع، قسمت کوئری (?role=admin) داره لیست کاربرا رو فیلتر می‌کنه تا فقط اونایی که نقش "admin" دارن تو پاسخ نمایش داده بشن.


⭕️ قسمت کوئری (Query) توی URI باید برای صفحه‌بندی (pagination) نتایج مجموعه یا ذخیره‌ها استفاده بشه.
کلاینت یه REST API باید از پارامترهای pageSize و pageStartIndex توی کوئری استفاده کنه. پارامتر pageSize تعداد عناصر حداکثری رو که باید توی پاسخ برگردونده بشه مشخص می‌کنه، و pageStartIndex مشخص می‌کنه که اولین عنصر از کجا شروع بشه (با ایندکس صفر).

مثال:
GET /users?pageSize=25&pageStartIndex=50


این درخواست لیستی از ۲۵ کاربر رو برمی‌گردونه که از کاربر شماره ۵۰ شروع می‌شه.

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

POST /users/search


اینجا، به جای استفاده از کوئری توی URI، کلاینت می‌تونه درخواست‌های پیچیده‌تری مثل محدوده‌های خاص یا ترتیب‌های خاص رو توی بدنه (body) پیام ارسال کنه. فقط باید مطمئن باشی که نتایج کش‌شده کنترلر به درستی مدیریت بشن.


📝 قوانین طراحی URI در REST API 📝

این خلاصه، اصطلاحات مهمی رو که تو طراحی URIs برای REST API ها به کار میره توضیح میده:

🔑 Authority: بخشی از URI که مسئول فضای نام هست.
📂 Collection: نوعی منبع که مثل یه دایرکتوری از منابع سرور مدیریت می‌شه.
🛠 Controller: منبعی که عملکردهای اجرایی رو مدل‌سازی می‌کنه (مثل یه تابع).
💾 CRUD: مخفف چهار عمل اصلی: ایجاد، خواندن، بروزرسانی و حذف.
🌐 Developer portal: یه رابط کاربری وب برای جذب کلاینت‌های جدید به API.
🏠 Docroot: نقطه شروع مدل REST API که والد همه منابع دیگه است.
📄 Document: منبعی که یه مفهوم منفرد رو مدل‌سازی می‌کنه.
🔗 Forward slash separator (/): علامتی که برای جدا کردن منابع مرتبط در URI به کار می‌ره.
👁 Opacity of URIs: یه اصل که می‌گه ساختار URI برای کاربر نباید مهم باشه.
📍 Parent resource: منبعی که یه مفهوم زیرمجموعه رو مدیریت می‌کنه.
🔍 Query: بخشی از URI که برای جستجو و فیلتر استفاده می‌شه.
🔧 Resource archetypes: چهار نوع اصلی منابع (مستند، مجموعه، ذخیره، کنترلر).
🗄 Store: یه منبع که به‌عنوان یه مخزن مدیریت‌شده توسط کلاینت مدل‌سازی می‌شه.
📌 URI path segment: بخشی از URI که نمایانگر یه گره (node) تو مدل سلسله مراتبی منبعه.
📑 URI template: فرمت URI که شامل متغیرهایی هست که قبل از استفاده باید جایگزین بشن.

@ninja_leanr_ir
🔥42👍1
خب فصل دوم هم تموم شد و از فردا فصل ۳ رو استارت میزنیم 💪

درگیر یه پروژه شدیم و سرمون شلوغ شده به همین دلیل بعضی روزا یادمون میره پست از کتاب بذاریم لطفا به بزرگواری خودتون ببخشید 🌹
👍51
بررسی MySQL: همه چیز درباره یکی از محبوب‌ترین دیتابیس‌های دنیا 💎

امروز می‌خوام یه دیتابیس معروف و پرطرفدار رو بررسی کنیم؛ MySQL شاید اسمشو زیاد شنیده باشی ولی دقیق ندونی چیه و چه کاربردایی داره.

حالا MySQL چیه؟

خب MySQL یه دیتابیس رابطه‌ای (Relational) و اوپن سورس هست که توی سال 1995 ساخته شده. دیتابیس‌های رابطه‌ای یعنی دیتایی که توش ذخیره میشه توی جدول‌هایی با ردیف‌ها و ستون‌ها قرار می‌گیره و این جداول با همدیگه ارتباط دارن. 🛠️

این دیتابیس از SQL (زبان ساختارمند جستجو) برای مدیریت و پرس‌وجوی اطلاعات استفاده می‌کنه. از اونجایی که MySQL اوپن سورسه، یعنی هرکسی می‌تونه ازش به صورت رایگان استفاده کنه و حتی به کدهاش دسترسی داشته باشه. 💻

چرا MySQL محبوبه؟

1⃣ سرعت و کارایی بالا 🚀: MySQL یکی از سریع‌ترین دیتابیس‌های رابطه‌ای هست. این یعنی درخواست‌ها و عملیات‌های دیتابیس رو خیلی سریع هندل می‌کنه.

2⃣ پشتیبانی از حجم بالا 💾: MySQL می‌تونه مقیاس‌پذیر باشه و دیتابیس‌هایی با حجم زیاد و تعداد کاربران بالا رو به‌خوبی مدیریت کنه.

3⃣ سازگاری با همه چیز 🔗: این دیتابیس تقریباً با همه زبان‌های برنامه‌نویسی و فریمورک‌های محبوب مثل Python, PHP, Node.js و Django به خوبی کار می‌کنه.

- امنیت 🔒: MySQL امنیت بالایی داره و می‌تونید به راحتی دسترسی کاربران به دیتابیس رو مدیریت کنید.

- پشتیبانی از تراکنش‌ها 💡: تراکنش‌ها (Transactions) توی MySQL به شما این امکان رو میدن که چند عملیات دیتابیسی رو به صورت اتمیک انجام بدید، یعنی یا همه اون عملیات‌ها باهم انجام بشن یا هیچکدوم.

چجوری نصب کنیم؟
نصب MySQL خیلی ساده‌ست. اگه سیستم‌عامل لینوکس یا مک داری، با چند تا دستور ساده می‌تونی نصبش کنی. توی ویندوز هم نصب‌کننده گرافیکی داره که کار رو برات راحت می‌کنه. مثلاً برای نصب توی اوبونتو:

sudo apt-get install mysql-server

بعد از نصب، برای ورود به دیتابیس MySQL از این دستور استفاده کن:

mysql -u root -p

چطوری با MySQL کار کنیم؟

بعد از نصب، می‌تونی جداول و دیتاهای موردنظرت رو با SQL مدیریت کنی. مثلاً برای ساخت یه جدول جدید:

CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(100)
);

حالا برای اضافه کردن اطلاعات:

INSERT INTO users (name, email) VALUES ('John Doe', '[email protected]');

برای گرفتن اطلاعات:

SELECT * FROM users;

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

خب MySQL برای پروژه‌های کوچیک و بزرگ مناسبه، از سایت‌های شخصی گرفته تا اپلیکیشن‌های بزرگ و سیستم‌های پیچیده. اگه نیاز داری یه دیتابیس سبک و سریع داشته باشی که هم اوپن سورسه و هم جامعه بزرگی داره، MySQL گزینه خوبیه. خیلی از سرویس‌های بزرگ مثل Facebook, Twitter, YouTube از MySQL استفاده می‌کنن! 😯

جمع‌بندی 🎯

در کل، MySQL یه دیتابیس رابطه‌ای قدرتمند، سریع و امنه که برای مدیریت اطلاعات توی پروژه‌های مختلف عالیه. چه پروژه‌های کوچیک داشته باشی و چه پروژه‌های بزرگ، MySQL می‌تونه نیازت رو برطرف کنه. اگه دنبال یه دیتابیس اوپن سورس و همه‌کاره هستی، حتماً یه سر به MySQL بزن😎🔥

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

#دیتابیس #mysql #db


@ninja_learn_ir
👍71
روز برنامه نویس مبارک 🎉


۲۱ شهریور (۱۲ سپتامبر) به عنوان "روز برنامه‌نویس" شناخته می‌شود زیرا این روز، ۲۵۶مین روز سال است. عدد ۲۵۶ برای برنامه‌نویسان معنای ویژه‌ای دارد؛ چون در سیستم دودویی (باینری) که اساس کامپیوترها و برنامه‌نویسی است، ۲۵۶ بزرگ‌ترین توانی است که می‌توان با ۸ بیت (یک بایت) نشان داد. همچنین، عدد ۲۵۶ در بسیاری از مفاهیم کامپیوتری به‌ویژه حافظه و پردازش داده‌ها اهمیت زیادی دارد. به همین دلیل، روز ۲۵۶مین روز سال در تقویم میلادی به عنوان روز برنامه‌نویس انتخاب شده است.

در تقویم میلادی، اگر سال کبیسه باشد، این روز به ۱۳ سپتامبر منتقل می‌شود.


@ninja_learn_ir
👏103👍3❤‍🔥1
💎 بررسی Zero-day Exploit و چجوری جلوشو بگیریم؟ 💎

امروز می‌خوایم در مورد یکی از خطرناک‌ترین و مرموزترین حملات امنیتی به نام Zero-day Exploit صحبت کنیم. شاید اسمش رو شنیده باشی ولی دقیق ندونی چی هست و چطوری میشه ازش جلوگیری کرد. بزن بریم که توضیح بدم 😎

حالا این Zero-day Exploit چیه؟ 🤔

خب Zero-day Exploit به سوءاستفاده از یه آسیب‌پذیری ناشناخته توی نرم‌افزار، سیستم‌عامل یا حتی سخت‌افزار گفته میشه که توسط توسعه‌دهنده هنوز شناسایی یا اصلاح نشده. از لحظه‌ای که هکر این آسیب‌پذیری رو کشف می‌کنه و قبل از اینکه یه پچ امنیتی برای رفعش ارائه بشه، فرصت داره ازش بهره‌برداری کنه. 💀

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

مثال از Zero-day Exploit

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

چجوری جلوی Zero-day Exploit رو بگیریم؟ 🛡️

1⃣ آپدیت منظم نرم‌افزارها
   آپدیت کردن همیشه مهمه. خیلی از ما آپدیت‌ها رو پشت گوش می‌ندازیم ولی همین آپدیت‌ها معمولاً پچ‌های امنیتی مهمی دارن که می‌تونن جلوی حملات zero-day رو بگیرن. پس همیشه نرم افزار ، سخت افزار یا مروگر رو آپدیت نگه دار. 🔄

2⃣ استفاده از فایروال و آنتی‌ویروس قوی
   یه فایروال و آنتی‌ویروس خوب می‌تونن جلوی حملات مشکوک رو بگیرن یا حداقل هشدار بدن. مثلاً اگه یه برنامه یا فایل مشکوک بخواد از باگی استفاده کنه، آنتی‌ویروس می‌تونه اون رو قرنطینه کنه. 🛡️

3⃣ محدود کردن دسترسی‌ها
   یکی از راه‌های مهم برای کاهش آسیب اینه که همیشه سطوح دسترسی رو محدود کنی. یعنی نرم‌افزارها و کاربران فقط به چیزهایی که واقعاً نیاز دارن دسترسی داشته باشن. اگه هکرها وارد سیستم بشن، محدودیت دسترسی می‌تونه آسیب رو کم کنه. 🚪

4⃣ نظارت و لاگ‌گیری دقیق  
   همیشه باید روی ترافیک شبکه و سیستم‌های خودت نظارت داشته باشی. لاگ‌ها می‌تونن نشون بدن که آیا فعالیت مشکوکی اتفاق افتاده یا نه. اگه چیز غیرعادی دیدی، باید سریع اقدام کنی تا از گسترش حمله جلوگیری کنی. 👁️‍🗨️

5⃣ آموزش به کاربرها
   بیشتر حملات zero-day از طریق ایمیل‌های فیشینگ یا لینک‌های مخرب شروع میشن. آموزش به کاربرها و تیم‌ت در مورد امنیت و خطرات فیشینگ می‌تونه تا حد زیادی جلوی این حملات رو بگیره. کاربران باید بدونن روی هر لینکی کلیک نکنن! 🎣

جمع‌بندی 🎯

فهمیدیم Zero-day Exploit حمله‌ایه که خیلی خطرناکه چون قبل از اینکه فرصتی برای اصلاحش داشته باشیم، هکرها ازش استفاده می‌کنن. اما با آپدیت منظم نرم‌افزارها، استفاده از ابزارهای امنیتی مناسب و محدود کردن دسترسی‌ها می‌تونیم تا حد زیادی از خطراتش جلوگیری کنیم. 🔐

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

#امنیت #ZDE


@ninja_learn_ir
👍42
Ninja Learn | نینجا لرن
🌹 Sticker
مرسی از دوستانی که تو نظر سنجی شرکت کردن
این گل تقیدم شوما 🌹
🔥52🥰1💘1
💎 پکیج Django Cleanup مدیریت خودکار فایل‌های اضافی 💎

امروز می‌خوام در مورد یه کتابخونه خیلی کاربردی به اسم Django Cleanup صحبت کنم که کلی از مشکلات مربوط به مدیریت فایل‌ها رو توی پروژه‌های جنگو حل می‌کنه. 😎 اگه تا حالا با فایل‌های اضافی و بی‌استفاده دست‌وپنجه نرم کردی، این کتابخونه می‌تونه حسابی به کارت بیاد.

حالا Django Cleanup چیه؟ 🤔
به طور خلاصه، Django Cleanup به صورت خودکار فایل‌ها و تصویرهای ذخیره‌شده توی پروژه رو مدیریت می‌کنه. فرض کن یه فایل یا عکس توی پروژه آپلود کردی و بعد اون رکورد یا مدل رو حذف کردی. معمولاً فایل مرتبط توی سرور باقی می‌مونه و فضای سرور رو اشغال می‌کنه. 😒 Django Cleanup این فایل‌های اضافی رو به صورت خودکار حذف می‌کنه تا دیگه نیاز نباشه خودت دستی این کارو انجام بدی.

چرا Django Cleanup کاربردیه؟ 🤔

1⃣ مدیریت خودکار فایل‌های اضافه 🗑️:
بعد از حذف رکوردهای مدل، فایل‌های مرتبط بهش هم خود به خود حذف میشن.


2⃣ جلوگیری از انباشت فایل‌های اضافی 🚮:
نیازی نیست که خودت دنبال فایل‌های قدیمی بگردی و پاکشون کنی. این کتابخونه همه چیز رو برات مرتب می‌کنه.

3⃣ ساده و راحت در استفاده 🛠️:
فقط با نصب و یه سری تنظیمات ساده، همه چی رو هندل می‌کنه.


چطوری نصبش کنیم؟ 🛠️
نصب و استفاده از Django Cleanup خیلی ساده‌ست. اول از همه باید نصبش کنی:

pip install django-cleanup

بعد از نصب، باید این کتابخونه رو به تنظیمات جنگو اضافه کنی:

INSTALLED_APPS = [
    # بقیه اپ‌ها...
    'django_cleanup.apps.CleanupConfig',
]

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

مثال از استفاده 📂
فرض کن یه مدل ساده برای کاربر داری که یه عکس آپلود می‌کنه:

class UserProfile(models.Model):
    avatar = models.ImageField(upload_to='avatars/')


وقتی یه پروفایل کاربر رو حذف می‌کنی، فایل avatar مربوط به اون کاربر هم به صورت خودکار از پوشه avatars/ پاک میشه و دیگه فضای اضافی نمی‌گیره.

جمع‌بندی
فهمیدیم Django Cleanup یه ابزار خیلی ساده ولی قدرتمنده که کمک می‌کنه پروژه‌هات تمیز و منظم بمونه و از انباشت فایل‌های بی‌استفاده جلوگیری کنی. اگه توی پروژه‌هات با فایل‌های زیادی سروکار داری، حتماً از این کتابخونه استفاده کن تا کارت راحت‌تر بشه 🔥

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


#django #django_clean_up #trick


@ninja_learn_ir
👍112🔥1🥰1👌1
💎 ردیس (Redis) چیه و چرا اینقدر محبوبه؟ 💎

امروز می‌خوام در مورد Redis صحبت کنم. شاید اسمشو شنیده باشی ولی ندونی دقیقاً چیه و چه کاربردی داره. بیاید یه نگاه دقیق‌تر بندازیم به این دیتابیس پرسرعت و جذاب 😎

حالا Redis چیه؟ 🤔

خب Redis یه دیتابیس NoSQL از نوع In-memory هستش. یعنی داده‌ها رو به‌جای اینکه روی دیسک ذخیره کنه، توی RAM نگه می‌داره و این باعث میشه که فوق‌العاده سریع باشه . به خاطر همین، معمولاً از Redis برای کشینگ (Caching)، مدیریت صف‌ها و ذخیره‌سازی موقت داده‌ها استفاده می‌کنن.

باید بدونید که Redis یه سری ساختار داده‌های پیچیده مثل لیست‌ها، مجموعه‌ها (Sets)، هش‌ها و حتی پایگاه‌داده‌های جفت‌کلید/مقدار رو به شکلی خیلی بهینه پشتیبانی می‌کنه. یعنی هر چی داده لازم داری باهاش کار کنی، Redis از پسش برمیاد 😁

حالا Redis چه کاربردهایی داره؟ 🔥

1⃣ کشینگ (Caching) داده‌ها:
خب Redis برای ذخیره موقت داده‌ها توی کش عالیه. مثلاً می‌تونی نتیجه درخواست‌های API یا کوئری‌های سنگین دیتابیس رو توی Redis ذخیره کنی تا دفعات بعد با سرعت بیشتری بهشون دسترسی داشته باشی 🚀
 
2⃣ مدیریت Sessionها:
توی اپلیکیشن‌های تحت وب، می‌تونی Sessionها رو توی Redis ذخیره کنی. اینجوری سریع و با امنیت بیشتری میشه اطلاعات کاربر رو نگه داشت 🔐

3⃣ مدیریت صف‌ها (Queues):
اگه با صف‌های پردازشی سروکار داری (مثل صف ایمیل‌ها یا پیام‌ها)، Redis به راحتی می‌تونه این صف‌ها رو مدیریت کنه. سرعت و پایداری Redis توی این زمینه بی‌نظیره 📩

4⃣ ذخیره داده‌های Real-time:
مثلا اگه یه اپ چت یا اپلیکیشنی که نیاز به پردازش ریل تایم داره، Redis بهترین انتخابه چون داده‌ها رو خیلی سریع مدیریت می‌کنه 🕒

چرا Redis اینقدر سریع و محبوبه؟

1⃣ اول In-memory بودنش:
چون داده‌ها رو توی RAM نگه می‌داره، دسترسی بهشون خیلی سریعه.

2⃣ دوم پشتیبانی از ساختار داده‌های متنوع: برخلاف دیتابیس‌های سنتی، Redis ساختارهای پیشرفته‌ای مثل لیست‌ها، هش‌ها و مجموعه‌ها رو پشتیبانی می‌کنه.

3⃣ سوم سادگی در استفاده:
نصب و راه‌اندازیش خیلی راحته و استفاده از دستوراتش هم سرراست و ساده‌ست.

4⃣ چهارم پشتیبانی از Replication و Persistence:
یعنی می‌تونی داده‌ها رو بین چندین سرور کپی کنی یا اگه خواستی داده‌ها رو به دیسک هم بنویسی تا در صورت قطعی سیستم از بین نرن.

حالا چطوری Redis رو نصب و راه‌اندازی کنیم؟ 🛠️

برای نصب Redis، فقط کافیه که از دستورات زیر استفاده کنی:

روی اوبونتو:
sudo apt update
sudo apt install redis-server

بعد از نصب، Redis به طور پیش‌فرض روی پورت 6379 در حال اجراست. می‌تونی با دستور زیر مطمئن بشی که Redis درسته اجرا شده:
redis-cli ping

اگه جواب PONG رو گرفتی، یعنی Redis داره به درستی کار می‌کنه 👌

جمع‌بندی

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

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

#redis


@ninja_learn_ir
👍127👌1
💎 حمله CSRF و چطوری ازش جلوگیری کنیم؟ 💎

امروز می‌خوایم در مورد یکی از حملات معروف تو دنیای وب، یعنی CSRF یا همون Cross-Site Request Forgery صحبت کنیم.

حالا CSRF چیه؟ 🤔

خب CSRF یه جور حمله‌ست که هکر سعی می‌کنه با سوءاستفاده از سشن (session) کاربر، کارهایی رو به نمایندگی از کاربر انجام بده بدون اینکه اون کاربر خبر داشته باشه 😱. یعنی اگه کاربر توی یه سایت لاگین کرده باشه (مثل یه بانک یا شبکه اجتماعی) و بعد روی لینک یا دکمه‌ای توی یه سایت دیگه کلیک کنه، هکر می‌تونه درخواست‌هایی رو به سایت اصلی (که کاربر لاگین کرده) بفرسته و کارهایی مثل انتقال پول یا تغییر اطلاعات کاربر رو انجام بده.

چجوری این حمله کار می‌کنه؟ 🎯

1️⃣ کاربر لاگین می‌کنه:
مثلا کاربر وارد حساب بانکی خودش میشه و یه سشن معتبر داره.

2️⃣ هکر یه لینک مخرب می‌سازه:
یه هکر توی یه سایت دیگه یه لینک یا فرم مخرب می‌سازه که درخواست‌هایی رو به حساب کاربر توی سایت بانکی ارسال می‌کنه.

3️⃣ کاربر روی لینک کلیک می‌کنه:
وقتی کاربر بدون اینکه خبر داشته باشه روی اون لینک کلیک می‌کنه، درخواست از طرف سشن کاربر به سرور سایت بانکی ارسال میشه.

4️⃣ هکر درخواست‌ها رو اجرا می‌کنه:
سرور چون کاربر لاگین کرده، درخواست رو معتبر می‌دونه و اون کار انجام میشه (مثل انتقال پول، تغییر پسورد و...)

چجوری میشه جلوی CSRF رو گرفت؟ 🛡️

1️⃣ استفاده از CSRF Token
هر وقت کاربر یه فرم رو پر می‌کنه یا عملیاتی رو انجام میده، سرور یه توکن منحصربه‌فرد به فرم اضافه می‌کنه. این توکن رو سرور چک می‌کنه تا مطمئن بشه درخواست از طرف خود کاربر ارسال شده نه یه سایت دیگه. جنگو، فلکس و خیلی از فریمورک‌های معروف به صورت پیش‌فرض از این مکانیزم پشتیبانی می‌کنن 🔑.

2️⃣ استفاده از روش POST به جای GET
برای درخواست‌هایی که نیاز به تایید کاربر دارن (مثل انتقال پول یا تغییر اطلاعات)، از POST استفاده کن، نه GET. توی درخواست‌های GET داده‌ها توی URL قرار می‌گیرن که راحت‌تر میشه ازشون سوءاستفاده کرد. با POST درخواست‌ها ایمن‌تر میشن 🛠️.

3️⃣ محدود کردن Referer Header
   سرورها می‌تونن Referer header رو چک کنن تا مطمئن بشن که درخواست‌ها از یه منبع قابل اعتماد (مثلاً همون سایت خودت) ارسال شدن نه از یه سایت دیگه‌ای که هکرها توش لینک مخرب گذاشتن. اینجوری درخواست‌های مشکوک رد میشن 🚫.

4️⃣ استفاده از Double Submit Cookies
  
   یه راه دیگه برای جلوگیری از CSRF اینه که هم از کوکی‌ها و هم از پارامترها استفاده کنی. توی این روش، یه کوکی حاوی CSRF token ارسال میشه و سرور مطمئن میشه که درخواست معتبره.

5️⃣ لاگ‌اوت خودکار
  
   اگه کاربر برای مدت زیادی هیچ فعالیتی توی سایت نداشت، اونو به صورت خودکار از سیستم خارج کن. اینجوری خطر CSRF کمتر میشه چون سشن کاربر خیلی طولانی باز نمی‌مونه .

جمع‌بندی

فهمیدیم CSRF یه حمله جدیه که اگه درست ازش جلوگیری نشه، می‌تونه خیلی از اطلاعات حساس رو به خطر بندازه. با استفاده از CSRF Token، چک کردن Referer header و بقیه روش‌هایی که گفتیم، می‌تونی از اپلیکیشن‌هات در برابر این حمله محافظت کنی و امنیتشون رو بالا ببری 💪.

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

#csrf #امنیت


@ninja_learn_ir
👍102🔥1
قسمت ۱۹ دوره DRF منتشر شد 🥳

برای مشاهده کلیک کنید

شرمنده برای تاخیری که پیش اومد تواین مدت خیلی سرمون شلوغ بودش 🙏
👍71