با سلام و درود، خب فرض اول ما این هست که اسناد خود جنگو در مورد میان افزارها (Middlewares) را مطالعه کرده اید. و اینکه در این مطلب با توجه به عنوان ما صرفا میخواهیم با مفهوم میان افزارها در جنگو آشنا بشیم، قصد نداریم اینجا نمونه کدهای فراوان داشته باشیم و یا بخواهیم خیلی تخصصی به این مطلب بپردازیم، صرفا میخواهیم آشنایی اولیه پیدا کنیم و بعدا فرصت پیش آمد حتما خیلی تخصصی تر بهش خواهم پرداخت.
میان افزار (middleware) چیست؟
میان افزارها، هوک-hook هایی هستند که به اصلاح request و response ها می پردازند. تعریف خود اسنادجنگو رو زیر آوردم: (برای هوک یه لینک روی خودش گذاشتم ولی به فارسی و مختصر : کد ماشینی که در یک ماژول یا پیمانه از سیستم عامل جای داده می شود تا کنترل را به روالی که وظیفه ای اضافی انجام می دهد انتقال داده و در محلی متفاوت از ماژول اصلی ذخیره نماید)
Middleware is a framework of hooks into Django’s request/response processing. It’s a light, low-level “plugin” system for globally altering Django’s input or output.
میان افزار چارچوبی از هوک در پردازش request یا response در جنگو می باشد. که افزونه-پلاگین سبک و سطح پایین برای تغییر ورودی و خروجی های محلی جنگو می باشد.
Django Docs
چه زمانی از میان افزار-middleware استفاده کنیم؟
می توانید از میان افزار برای اصلاح request، برای مثال HttpRequest که به سمت view ارسال شده استفاده کنید. یا اینکه میخواهید HttpResponse که از سمت vierw برگشته را تغییر دهید. هر جفت این کارها با استفاده از میانافزارها انجام می شوند.
ممکنه که بخواهید عملی را قبل از اینکه view اجرا شود انجام دهید. در اینجور موارد می توانید از میان افزار استفاده کنید.
جنگو تعدادی میان افزار پیشفرض ارائه داده است برای مثال : AuthenticationMiddleware
خیلی رایجه که شما از request.user داخل view استفاده کنید. در اینجا جنگو می خواهد که ویژگی های user روی request قبل از این که view ای اجرا شود، تنظیم شود. برای رسیدن به این هدف جنگو از رویکرد میان افزار استفاده کرده است و AuthenticationMiddleware فراهم کرده که می تواند شيء request را تغییر دهد.
به طور مشابه شما ممکن هست که اپلیکیشنی داشته باشید که با کاربرانی از timezone های مختلف کار بکند. شما میخواهید که زمانی که صفحه ای را به کاربر نشان می دهید از timezone خود کاربر استفاده کنید. شما میخواهید که به timezone کاربر در تمامی viewها دسترسی داشته باشید.منطقی است که در چنین حالتی آن را در session اضافه کنید. پس می توانید میان افزاری مانند زیر را اضافه کنید:
class TimezoneMiddleware(object): def process_request(self, request): # Assuming user has a OneToOneField to a model called Profile # And Profile stores the timezone of the User. request.session['timezone'] = request.user.profile.timezone
TimezoneMiddleware وابسته به request.user می باشد. و request.user در AuthenticationMiddleware جمع شده است. بنابراین TimezoneMiddlewareکه توسط ما نوشته می شود باید در settings.MIDDLEWARE_CLASSES پس از AuthenticationMiddleware که توسط جنگو فراهم شده است بیاید.
در مورد چینش میان افزار ها در ادامه روی مثالهایی که آورده شده بیشتر صحبت خواهیم کرد.
مواردی که به هنگام میان افزار باید به یاد داشته باشید
- چینش میان افزار ها مهم می باشد
- یک میان افزار باید از یک شیء کلاس گسترش داده شود.
- یک میان افزار برای پیاده سازی و عدم پیاده سازی متد ها آزادی عمل دارد.
- یک میان افزار ممکن است process_request پیاده سازی کند، اما ممکن نیست process_response و process_view پیاده سازی کند.
- یک میان افزار ممکن است process_response پیاده سازی کنم اما process_request پیاده سازی نمی کند.
AuthenticationMiddleware فقط process_request پیاده سازی می کند و process_response را پیاده سازی نمی کند. می تونید اینجا یه نگاهی بهش بیاندازید.
GZipMiddleware فقط process_response پیاده سازی می کند و process_request پیاده سازی نمی کند. این مورد رو هم می تونید اینجا نگاهی بیاندازید.
حتما دو تا لینک فوق را نگاه بیاندازید و متوجه بحث فوق بشید 🙂
خب حالا بیاید تا چند تا میان افزار بنویسیم
اول از همه مطمئن باشید که یک پروژه جنگو با url و viewe دارید که دسترسی به view دارید، چرا که ما با request.user سروکار درایم، و همچنین مطمئن شوید که authentication به درستی برای شما تنظیم شده باشد و request.user موارد درستی را در این view چاپ می کند.
در یکی از app هایی که دارید، فایل middleware.py را بسازید.
من مثال ها رو با توجه به سایت Agiliq پیش میرم. appای به نام books
داریم، در نتیجه داخل books/middleware.py خواهیم نوشت.
class BookMiddleware(object): def process_request(self, request): print "Middleware executed"
میان افزار ساخته شده را در MIDDLEWARE_CLASSES اضافه می کنیم:
MIDDLEWARE_CLASSES = ( 'books.middleware.BookMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', )
با هر request به هر url. باید عبارت زیر روی کنسول نمایش داده شود.
Middleware executed
تغییر BookMiddleware.process_request به شکل زیر خواهد بود:
class BookMiddleware(object): def process_request(self, request): print "Middleware executed" print request.user
دادن یه request مجدد روی url . یک ارور نمایان می شود:
'WSGIRequest' object has no attribute 'user'
چرا ارور فوق اتفاق افتاد؟؟ چون که هنوز ویژگی user
روی request
تنظیم نشده است.
حال می آییم ترتیب چینش میان افزار ها را تغییر می دهیم، در نتیجه BookMiddleware بعد از AuthenticationMiddleware می آوریم:
MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'books.middleware.BookMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', )
مجدد request ای روی یک url داشته باشید. باید متن زیر روی کنسول نمایان شود:
Middleware executed <username>
این میگه که process_request
روی میان افزار بر مبنای چینششان و لیست شدنشان در settings.MIDDLEWARE_CLASSES اجرا می شود.
می توانید آن را در ادامه تایید کنید. میان افزار دیگری در middleware.py خودتان اضافه کنید.
class AnotherMiddleware(object): def process_request(self, request): print "Another middleware executed"
این میان افزار را مجدد به لیست میانافزار ها اضافه کنید:
MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'books.middleware.BookMiddleware', 'books.middleware.AnotherMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', )
حال خروجی به صورت زیر خواهد بود:
Middleware executed <username> Another middleware executed
چگونه برگشتن (returning) HttpResponse از process_request چیزها را تغییر می دهد
BookMiddleware را تغغیر می دهیم و به شکل زیر می شود:
class BookMiddleware(object): def process_request(self, request): print "Middleware executed" print request.user return HttpResponse("some response")
یکی از url ها را امتحان کنید و خروجی به شکل زیر خواهد بود:
Middleware executed <username>
به دو تا نکته توجه داشته باشید:
- view شما دیگر اجرایی نخواهد شد و مهم نیست کدام url را امتحان کنید، شما “some response” را خواهید دید.
- AnotherMiddleware.process_request دیگر اجرا نخواهد شد.
پس اگر یک process_request() از میانافزار، یک شیء HttpResponse برگرداند، سپس process_request از هر میان افزار بعدی ای، کنار گذاشته-bupass می شود. (شاید چون معادلات فارسی استفاده نکردم یه مقدار جمله گیج کننده باشه، ولی لطفا چند مرتبه با توجه به علائم نگارشی و دقت جمله را بخوانید، مطمینم متوجه می شوید). شما به ندرت این کار را می کردید یا در پروژه های خودتان به ان نیازد داشته اید.
کار با process_response
متد process_response را به هر جفت میانافزار ها اضافه کنید.
class AnotherMiddleware(object): def process_request(self, request): print "Another middleware executed" def process_response(self, request, response): print "AnotherMiddleware process_response executed" return response class BookMiddleware(object): def process_request(self, request): print "Middleware executed" print request.user return HttpResponse("some response") #self._start = time.time() def process_response(self, request, response): print "BookMiddleware process_response executed" return response
یک url را امتحان کنید. خروجی به شکل زیر خواهد بود:
Middleware executed <username> Another middleware executed AnotherMiddleware process_response executed BookMiddleware process_response executed
AnotherMiddleware.process_response() قبل از BookMiddleware.process_response() اجرا می شود، درحالی که AnotherMiddleware.process_request() بعد از BookMiddleware.process_request() اجرا می شود. پس process_response() برعکس چیزی که در process_request رخ می دهد را پیروی می کند. process_response() برای آخرین میان افزار اجرا می شود سپس دومی از آحرین میان افزار و به همین ترتیب تا به اولین میان افزار می رسد.
process_view
جنگو پردازش process_view() های میان افزار را به ترتیب تعریف شده در MIDDLEWARE_CLASSES (بالا به پایین) انجام می دهد. شبیه به ترتیب process_request()
همچینن اگر هر process_view() یک شیء HttpResponse برگرداند، سپس فراخوانی های بعدی process_view() کنار گذاشته می شوند و اجرا نمی شود.
مرجع اصلی این نوشته سایت Agiliq بود. این مطلب هم اگر دوست داشتید برای مطالعه بیشتر پیگیر باشید.
برای آشنایی با سیکل request و response در جنگو نیز اینجا رو دوست داشتید یه نگاه بیاندازید.
ارسال پاسخ