Oruji.org
Oruji.orgPersian Tutorials
ورود
ویرایش: 1396/11/2 22:16

آموزش Session ها، کاربران، و عضویت در جنگو (Django)

وقت آن رسیده است که اعترافی کنیم: ما تا اینجای کتاب آموزشی چارچوب جنگو (Django)، به طور عمد، یک جنبه ی مهم از توسعه ی وب را عقب انداخته ایم.

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

متاسفانه، این موضوع ساده ای نمی باشد. HTTP طوری طراحی شده است که بی حالت باشد – بدین معنی که، هر درخواست در یک فضای تهی اتفاق می افتد. دوامی بین یک درخواست و درخواست بعدی وجود ندارد، و ما نمی توانیم هر یک از جنبه ها درخواست (آدرس IP، مرورگر و غیره ...) را که به طور مداوم توسط یک شخص به طور پی در پی ارسال می شود را شمارش کنیم.

در این آموزش شما نحوه ی کنترل این فقدان حالت را خواهید آموخت. با پایین ترین سطح (کوکی ها) شروع خواهیم کرد، و به سمت ابزار سطح بالاتر برای کنترل session ها، کاربران و عضویت حرکت خواهیم کرد.

کوکی ها (Cookies)

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

اجازه دهید نگاهی به نحوه ای که این عمل ممکن است انجام شود بیاندازیم. هنگامی که شما مرورگر خود را باز می کنید و درون آن google.com را تایپ می کنید، مرورگر شما یک درخواست HTTP را به گوگل می فرستد که با چیزی شبیه به این شروع می شود:

GET / HTTP/1.1 Host: google.com ...

زمانی که گوگل پاسخ می دهد، یک پاسخ HTTP شبیه به پاسخ زیر خواهد بود:

HTTP/1.1 200 OK Content-Type: text/html Set-Cookie: PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com Server: GWS/2.1 ...

به هدر Set_Cookie دقت کنید. مرورگر شما مقدار کوکی که از این قرار است ذخیره خواهد کرد (PREF=ID=5b14f22bdafle81c:TM=1167000671:LM=1167000671) و در هر بار که شما به سایت دسترسی پیدا کنید آن را به گوگل بر می گرداند. بنابراین در مرتبه ی بعدی که به گوگل دسترسی پیدا می کنید، مرورگر شما یک درخواست مانند زیر را ارسال خواهد کرد:

GET / HTTP/1.1 Host: google.com Cookie: PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671 ...

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

قرار دادن و گرفتن کوکی ها

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

خواندن کوکی ها ساده می باشد. هر شیء HttpRequest دارای یک شیء COOKIES می باشد که مانند دیکشنری عمل می کند؛ می توانید برای خواند هر کوکی که مرورگر به view ارسال می کند از آن استفاده کنید:

def show_color(request): if "favorite_color" in request.COOKIES: return HttpResponse("Your favorite color is %s" % \ request.COOKIES["favorite_color"]) else: return HttpResponse("You don't have a favorite color.")

نوشتن کوکی ها قدری پیچیده تر می باشد. نیاز است که از متد set_cookie() در یک شیء HttpResponse استفاده کنید. در زیر مثالی وجود دارد که کوکی favorite_color مستقر در یک پارامتر GET را قرار می دهد:

def set_color(request): if "favorite_color" in request.GET: # Create an HttpResponse object... response = HttpResponse("Your favorite color is now %s" % \ request.GET["favorite_color"]) # ... and set a cookie on the response response.set_cookie("favorite_color", request.GET["favorite_color"]) return response else: return HttpResponse("You didn't give a favorite color.")

می توانید همچنین یک تعداد از آرگومان های اختیاری را به response.set_cookie() که جنبه هایی از کوکی را کنترل می کند ارسال کنید. همانطور که در جدول 1-14 مشاهده می کنید.

جدول

جدول ۱-۱۴

The Mixed Blessing of Cookies

ممکن است دقت کرده باشید که یک تعداد از مشکلات بالقوه روش کار با کوکی ها وجود دارند. اجازه دهید برخی از این مشکلات را مورد بررسی قرار دهیم:

فریم ورک یا چارچوب Session جنگو

با تمام این محدودیت ها و حفره های امنیتی بالقوه، واضح است، آن کوکی ها و session های مقاوم مثال هایی از نقطه های درد در توسعه ی وب می باشند. البته، هدف جنگو داروی مسکن موثر بودن می باشد، بنابراین جنگو حاوی یک فریم روک session طراحی شده جهت تسکین دادن این سختی ها برای شما می باشد.

این چارچوب session به شما اجازه می دهد داده ی دلخواه را بر اساس هر بازدید کننده ی سایت ذخیره و بازیابی کنید. این فریم ورک داده را در سمت سرور ذخیره می کند و ارسال و دریافت کوکی ها را حذف می کند. کوکی ها تنها از یک ID هش شده استفاده می کنند – نه خود داده – در نتیجه شما را از مشکلات رایج کوکی محافظت می کند.

اجازه دهید به نحوه ی فعال کردن session ها و استفاده کردن از آن ها در view ها بپردازیم.

فعال کردن Session ها

session ها از طریق قسمتی از middleware (به فرم مراجعه کنید) و مدل جنگو اجرا می شوند. جهت فعال کردن session ها، نیاز است مراحل زیر را انجام دهید:

  1. تنظیم MIDDLEWARE_CLASSES را ویرایش کرده و اطمینان حاصل کنید که MIDDLEWARE_CLASSES حاوی 'django.contrib.sessions.middleware.SessionMiddleware'می باشد.
  2. اطمینان حاصل کنید که 'django.contrib.sessions' درون تنظیم INSTALLED_APPS وجود دارد (و در صورتیکه ملزم به اضافه کردن آن هستید دستور manage.py syncdb را اجرا کنید).

اسکلت بندی پیشفرض تنظیمات ایجاد توسط startproject دارای هر دوی این قسمت ها فوق می باشد، بنابراین در صورتی که آن ها را حذف نکرده باشید، برای کار کردن session ها نیازی به تغییر چیزی نیست.

در صورتی که نمی خواهید از session ها استفاده کنید، ممکن است بخواهید خط SessionMiddleware را از MIDDLEWARE_CLASSES و 'django.contrib.sessions'از INSTALLED_APPS خود حذف کنید. این شما را تنها از مقدار کمی از بار اضافی حفظ می کند، ولی هر قسمت کوچکی شمارش می شوند.

استفاده از Session ها در view ها

هنگامی که SessionMiddleware فعال شده است، هر شیء HttpRequest – اولین آرگومان برای هر تابع view جنگو – دارای یک attribute، seesion خواهد بود که یک شیء شبیه به دیکشنری می باشد. می تواند درست مثل یک دیکشنری معمولای از آن استفاده کنید. برای مثال در یک view می توانید کاری شبیه به زیر را انجام دهید:

# Set a session value: request.session["fav_color"] = "blue" # Get a session value -- this could be called in a different view, # or many requests later (or both): fav_color = request.session["fav_color"] # Clear an item from the session: del request.session["fav_color"] # Check if the session has a given key: if "fav_color" in request.session: ...

همچنین می توان از متدهای دیگر دیکشنری همانند keys() و items() در request.session استفاده کنید.

تعدادی قوانین ساده برای استفاده ی موثر از session های جنگو وجود دارد:

اجازه دهید مثال های کوچکی را ذکر کنیم. view ساده ی زیر بعد از این که کاربر یک کامنت را پست می کند مقدار True را در یک متغیر has_commented قرار می دهد. جلوگیری از پست کردن بیشتر از یک کامنت توسط کاربر ساده می باشد:

def post_comment(request): if request.method != 'POST': raise Http404('Only POSTs are allowed') if 'comment' not in request.POST: raise Http404('Comment not submitted') if request.session.get('has_commented', False): return HttpResponse("You've already commented.") c = comments.Comment(comment=request.POST['comment']) c.save() request.session['has_commented'] = True return HttpResponse('Thanks for your comment!') view ساده ی ورودی یک عضو در سایت:

view ساده ی ورودی یک عضو در سایت:

def login(request): if request.method != 'POST': raise Http404('Only POSTs are allowed') try: m = Member.objects.get(username=request.POST['username']) if m.password == request.POST['password']: request.session['member_id'] = m.id return HttpResponseRedirect('/you-are-logged-in/') except Member.DoesNotExist: return HttpResponse("Your username and password didn't match.")

و کد زیر خروج یک عضو از سایت که از طریق login() فوق وارد سایت شده است:

def logout(request): try: del request.session['member_id'] except KeyError: pass return HttpResponse("You're logged out.")

آزمون کوکی های ارسال شده

همانطور که در بالا ذکر شد، نمی توان به هر کوکی ارسال شده ی مرورگر اطمینان کرد. بنابراین، برای راحتی کار، جنگو روشی ساده برای آزمون کوکی های ارسال شده ی مرورگر تهیه کرده است. تنها کافیست request.session.set_test_cookie() را در یک view فراخوانی کرده و request.session.test_cookie_worked() را در view بعدی بررسی کنید – نه در فراخوانی view همسان.

این جدایی بین set_test_cookie() و test_cookie_worked به دلیل روش کار کوکی ها ضروری می باشد. زمانی که شما یک کوکی را قرار می دهید، در واقع شما نمی توانید تا درخواست بعدی مرورگر بگویید یک مرورگر آن را ارسال کرده است.

استفاده از delete_test_cookie() برای تمیز کردن بعد از خودتان تمرین خوبی می باشد. این عمل را بعد از تایید کارکرد کوکی آزمون انجام دهید.

در زیر مثال کاربرد معمولی موضوع فوق وجود دارد:

def login(request): # If we submitted the form... if request.method == 'POST': # Check that the test cookie worked (we set it below): if request.session.test_cookie_worked(): # The test cookie worked, so delete it. request.session.delete_test_cookie() # In practice, we'd need some logic to check username/password # here, but since this is an example... return HttpResponse("You're logged in.") # The test cookie failed, so display an error message. If this # were a real site, we'd want to display a friendlier message. else: return HttpResponse("Please enable cookies and try again.") # If we didn't post, send the test cookie along with the login form. request.session.set_test_cookie() return render_to_response('foo/login_form.html')

استفاده از Session ها خارج از view ها

به طور داخلی، هر session تنها یک مدل جنگوی معمولی تعریف شده در django.contrib.sessions.models می باشد. هر session توسط هش تصادفی کمتر یا بیشتر 32 حرفی در یک کوکی شناسایی می شود. به این دلیل که session یک مدل معمولی می باشد، می توان با استفاده از API معمولی پایگاه داده ی جنگو به session ها دسترسی پیدا کرد:

>>> from django.contrib.sessions.models import Session >>> s = Session.objects.get(pk='2b1189a188b44ad18c35e113ac6ceead') >>> s.expire_date datetime.datetime(2005, 8, 20, 13, 35, 12)

برای بدست آوردن داده ی session واقعی، نیاز به فراخوانی get_decoded() می باشد. این موضوع ضروری می باشد، چرا که دیکشنری در قالب بندی رمزی شده ذخیره شده است:

>>> s.session_data 'KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj...' >>> s.get_decoded() {'user_id': 42}

زمانی که Session ها ذخیره شده اند

به طور پیشفرض، جنگو تنها در صورتی که session تغییر کند آن ها را درون پایگاه داده ذخیره می کند – این بدین معنی است که اگر هر کدام از مقادیر دیکشنری آن اختصاص داده شود یا حذف شود:

# Session is modified. request.session['foo'] = 'bar' # Session is modified. del request.session['foo'] # Session is modified. request.session['foo'] = {} # Gotcha: Session is NOT modified, because this alters # request.session['foo'] instead of request.session. request.session['foo']['bar'] = 'baz'

برای تغییر این رفتار پیشفرض، مقدار SESSION_SAVE_EVERY_REQUEST را True قرار دهید. در صورتی که SESSION_SAVE_EVERY_REQUEST، True باشد، جنگو در هر درخواست تنها درون پایگاه داده ذخیره می کند، حتی اگر تغییر نکرده باشد.

توجه داشته باشید که کوکی session تنها زمانی که یک session ساخته شده یا تغییر کرده باشد فرستاده می شود. در صورتی که SESSION_SAVE_EVERY_REQUEST، True باشد، کوکی session در هر درخواست فرستاده خواهد شد. به طور یکسان، بخش expires از یک کوکی session در هر بار که کوکی session فرستاده شود به روز رسانی می شود.

Session های Browser-Length در مقابل Session های مقاوم

ممکن است متوجه شده باشید که کوکی گوگل فرستاده شده به ما در ابتدای این آموزش حاوی expires=Sun، 17-Jan-2038 19:14:07 GMT; بود. کوکی ها به طور اختیاری می توانند حاوی یک تاریخ انقضا باشند که مرورگر را در هنگام حذف کوکی آگاه می سازد. در صورتی که یک کوکی حاوی مقدار انقضا نباشد، زمانی که کاربر پنجره ی مرورگر را ببندد از بین می رود. می توان رفتار فریم ورک یا چارچوب session را رابطه با تنظیم SESSION_EXPIRE_AT_BROWSER_CLOSE کنترل کرد.

به طور پیشفرض، مقدار SESSION_EXPIRE_AT_BROWSER_CLOSE، False در نظر گرفته شده است، که بدین معنی می باشد که کوکی های session در مرورگرهای کاربران برای SESSION_COOKIE_AGE ثانیه (که پیشفرض آن دو هفته یا 1,209,600 ثانیه) می باشد. در صورتی که نمی خواهید مردم در هر بار که مرورگر را باز می گنند به سایت log in نشوند از این تنظیم می توانید استفاده کنید.

در صورتی که مقدار SESSION_EXPIRE_AT_BROWSER_CLOSE، True باشد، جنگو از کوکی های browser-length استفاده خواهد کرد.

تنظیمات دیگر Session

در کنار تنظیمات ذکر شده، چند تنظیم دیگر بر نحوه ی استفاده ی فریم ورک session جنگو از کوکی ها تاثیر می گذارد، همانطور که در جدول 2-14 نشان داده شده است.

جدول

جدول ۲-۱۴

کاربران و تصدیق

session ها راه تداوم داده را از بین چندین درخواست مرورگر به ما می دهند؛ دومین قسمت معادله استفاده از آن session ها برای ورود کاربر می باشد. البته، نمی توانیم به راحتی به هر کاربری که وارد می شود اعتماد کنیم، بنابراین نیاز به تصدیق کردن آن ها در طول مسیر می باشد.

به طور طبیعی، جنگو ابزاری را جهت این وظایف مشترک (و بسیاری دیگر) تهیه کرده است. سیستم تصدیق کاربر جنگو حساب ها کاربر، حق دسترسی ها، و session های بر پایه ی کوکی کاربر را کنترل می کند. این سیستم اغلب به صورت یک سیستم auth/auth (تصدیق و تصدیق) مورد مراجعه قرار گرفته است. آن نام عدم اعتبار کاربران را اغلب با یک پردازش دو مرحله ای تشخیص می دهد. نیاز به نکات زیر می باشد:

  1. تصدیق کاربری که ادعای کاربر بودن می کند (معمولا توسط بررسی یک نام کاربری و رمز عبور در مقابل یک پایگاه داده از کاربران)
  2. تصدیق این که کاربر مجاز به اجرای برخی اعمال داده شده (معمولا توسط بررسی در یک جدول از حق دسترسی ها) می باشد.

در ادامه ی این نیازمندی ها، سیستم auth/auth جنگو حاوی تعدادی از بخش ها می باشد:

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

فعال ساختن پشتیبانی تصدیق

همانند ابزار session، پشتیبانی تصدیق به صورت یک برنامه ی جنگو در django.contrib که نیاز به نصب شدن دارد همراه است. همچنین مانند ابزار session، باید به طور پیشفرض نصب شده باشد، ولی در صورتی که شما آن را حذف کرده باشید، نیاز است مراحل زیر را برای نصب آن دنبال کنید:

  1. اطمینان حاصل کنید فریم ورک session همانطور که قبلا در این آموزش از کتاب توضیح داده شد نصب شده باشد. پیگیری کاربران بدیهی است که مستلزم کوکی ها می باشد، و در نتیجه در فریم ورک session ساخته می شود.
  2. 'django.contrib.auth' را در تنظیم INSTALLED_APPS قرار داده و دستور manage.py syncdb را جهت نصب جداول پایگاه داده ی مناسب اجرا کنید.
  3. از وجود 'django.contrib.auth.middleware.AuthenticationMiddleware'درون تنظیم MIDDLEWARE_CLASSES اطمینان حاصل کنید – بعد از SessionMiddleware.

با انجام مراحل فوق، همه چیز برای سر و کار داشتن با کاربران در توابع view آماده می باشد. رابط اصلی که شما برای دسترسی به کاربران در یک view از آن استفاده خواهید کرد request.user می باشد؛ این یک شیء است که کاربر فعلی وارد شده به سایت را نشان می دهد. در صورتی که کاربر وارد نشده باشد، به جای آن یک شیء AnonymousUser خواهد بود (برای جزئیات بیشتر به ادامه ی این بخش نگاه بیاندازید).

می توانید به سادگی در صورتی که یک کاربر وارد شده است، با متد is_authenticated() تشخیص دهید:

if request.user.is_authenticated(): # Do something for authenticated users. else: # Do something for anonymous users.

استفاده از کاربران

هنگامی که شما یک کاربر دارید – اغلب از request.user، ولی از طریق یکی از روش های مختصر توضیح داده شده – تعدادی از فیلدها و متدهای در دسترس در آن شیء دارید. شیء های AnonymousUser برخی از این رابط ها را تقلید کرده است، ولی نه تمام آن را، بنابراین باید همواره user.is_authenticated() را قبل از آنکه کاربری را که با آن سر و کار دارید را با حسن نیت تصور کنید بررسی کنید.. جدول 3-14 و 4-14 فیلد ها و متد ها را به ترتیب در شیء های User لیست کرده است.

جدول

جدول ۳-۱۴

جدول

جدول ۴-۱۴

در پایان، شیء های User دارای دو فیلد many-to-many می باشند: groups و permissions. شیء های User می توانند همانند فیلدهای many-to-many دیگر به شیء های مربوط به خود دسترسی پیدا کنند:

# Set a user's groups: myuser.groups = group_list # Add a user to some groups: myuser.groups.add(group1, group2,...) # Remove a user from some groups: myuser.groups.remove(group1, group2,...) # Remove a user from all groups: myuser.groups.clear() # Permissions work the same way myuser.permissions = permission_list myuser.permissions.add(permission1, permission2, ...) myuser.permissions.remove(permission1, permission2, ...) myuser.permissions.clear()

وارد و خارج شدن از سایت

جنگو برای کنترل ورود و خروج از سایت، برخی توابع داخلی (و چند فوت و فن جذاب) را ارائه کرده است، ولی قبل از آن، اجازه دهید نگاهی به نحوه ی ورود و خروج از سایت را "به صورت دستی" بیاندازیم. جنگو جهت انجام این اعمال در django.contrib.auth دو تابع با نام های authenticate() و login() را ارائه کرده است.

جهت تصدیق یک نام کاربری و رمز عبور داده شده، از authenticate() استفاده کنید. این تابع دو آرگومان کیورد username و password را دریافت می کند، و در صورتی که رمز عبور برای نام کاربری داده شده معتبر باشد، یک شیء User بر می گرداند. در صورتی که رمز عبور معتبر نباشد، authenticate() مقدار None بر می گرداند:

>>> from django.contrib import auth >>> user = auth.authenticate(username='john', password='secret') >>> if user is not None: ... print "Correct!" ... else: ... print "Invalid password."

authenticate() تنها اعتبار کاربر را تایید می کند. جهت ورود کاربر، از تابع loging() استفاده کنید. این تابع یک شیء HttpRequest و یک شیء User دریافت کرده و با استفاده از فریم ورک session، ID کاربر را درون session ذخیره می کند.

مثال زیر نحوه ی استفاده از هر دوی تابع authenticate() و login() را درون یک تابع view نشان می دهد:

from django.contrib import auth def login_view(request): username = request.POST.get('username', '') password = request.POST.get('password', '') user = auth.authenticate(username=username, password=password) if user is not None and user.is_active: # Correct password, and the user is marked "active" auth.login(request, user) # Redirect to a success page. return HttpResponseRedirect("/account/loggedin/") else: # Show an error page return HttpResponseRedirect("/account/invalid/")

جهت خروج یک کاربر، از django.contrib.auth.logout() داخل view خود استفاده کنید. این تابع یک شیء HttpRequest دریافت کرده و هیچ مقداری بر نمی گرداند:

from django.contrib import auth def logout_view(request): auth.logout(request) # Redirect to a success page. return HttpResponseRedirect("/account/loggedout/")

دقت داشته باشید که auth.logout() در صورتی که کاربر وارد سایت نشده باشد، هیچ خطایی ایجاد نمی کند.

در عمل، نیازی به نوشتن توابع login/logout خودتان نخواهید داشت؛ سیستم تصدیق مجموعه ای از view ها برای کنترل ورود و خروج به طور عمومی ارائه کرده است. اولین گام در استفاده از این view های تصدیق، وصل کردن آن ها به URLconf می باشد. نیاز است تکه کد زیر را اضافه کنید:

from django.contrib.auth.views import login, logout urlpatterns = patterns('', # existing patterns here... (r'^accounts/login/$', login), (r'^accounts/logout/$', logout), )

/accounts/login/ و /accounts/logout/، URL های پیشفرض می باشند که جنگو برای این view استفاده می کند.

به طور پیشفرض view، login یک template را در registeration/login.html، render می کند (می توانید نام این template را از طریق ارسال یک آرگومان view اضافه تغییر دهید، ``template_name``). این فرم نیاز دارد حاوی یک فیلد username و یک فیلد password باشد. یک template ساده ممکن است چیزی شبیه به کد زیر باشد:

{% extends "base.html" %} {% block content %} {% if form.errors %} <p class="error">Sorry, that's not a valid username or password</p> {% endif %} <form action="" method="post"> <label for="username">User name:</label> <input type="text" name="username" value="" id="username"> <label for="password">Password:</label> <input type="password" name="password" value="" id="password"> <input type="submit" value="login"> <input type="hidden" name="next" value="{{ next|escape }}"> </form> {% endblock %}

در صورتی که وارد شدن موفقیت آمیز باشد، کاربر به طور پیشفرض به /accounts/profile/ تغییر مسیر داده خواهد شد. می توان این حالت را توسط یک فیلد hidden به نام next با URL جهت تغییر مسیر بعد از ورود override کرد. همچنین می تواند این مقدار را به صورت یک پارامتر GET به view، login ارسال کرده و آن به صورت خودکار به صورت متغیر next به context اضافه خواهد شد که می توانید درون آن فیلد hidden آن را درج کنید.

view، logout کمی متفاوت تر عمل می کند. به طور پیشفرض این view یک template در registration/loggedout.html (که معمولا حاوی یک پیام "you've successfully logged out" می باشد) را render می کند. می توان view را با یک آرگومان اضافه با نام next_page فراخوانی کرد، که view را جهت تغییر مسیر بعد از خروج راهنمایی خواهد کرد.

محدودیت دسترسی برای کاربران وارد شده

البته، در دست ترجمه/تالیف ....

به طور ساده، راه خام جهت محدود کردن دسترسی برای صفحات، بررسی request.user.is_authenticated() و تغییر مسیر به یک صفحه ی ورود می باشد:

from django.http import HttpResponseRedirect def my_view(request): if not request.user.is_authenticated(): return HttpResponseRedirect('/accounts/login/?next=%s' % request.path) # ...

یا شاید نمایش یک پیام خطا:

def my_view(request): if not request.user.is_authenticated(): return render_to_response('myapp/login_error.html') # ...

به صورت یک میانبر، می توان از decorator مناسب login_required استفاده کرد:

from django.contrib.auth.decorators import login_required @login_required def my_view(request): # ...

login_required به شکل زیر عمل می کند:

محدودیت دسترسی برای کاربرانی که یک آزمون را رد می کنند

محدودیت دسترسی بر پایه ی حق دسترسی ها یا برخی آزمون های دیگر، یا ارائه ی یک مکان مختلف برای view ورود اساسا به یک روش کار می کند.

روش خام اجرای آزمون در request.user به طور مستقیم درون view می باشد. برای مثال، view زیر برای اطمینان از این که کاربر، وارد شده و دارای حق دسترسی polls.can_vote می باشد یا خیر:

def vote(request): if request.user.is_authenticated() and request.user.has_perm('polls.can_vote')): # vote here else: return HttpResponse("You can't vote in this poll.")

بار دیگر، جنگو یک میانبر با نام user_passes_test ارائه کرده است. این میانبر آرگومان هایی دریافت کرده و یک decorator تخصص یافته برای وضعیت خاص شما تولید می کند:

def user_can_vote(user): return user.is_authenticated() and user.has_perm("polls.can_vote") @user_passes_test(user_can_vote, login_url="/login/") def vote(request): # Code here can assume a logged-in user with the correct permission. ...

user_passes_test یک آرگومان الزامی دریافت می کند: یک قابل فراخوانی که یک شیء User دریافت کرده و در صورتی که کاربر اجازه ی تماشای صفحه را داشته باشد مقدار True بر می گرداند. توجه داشته باشید که user_passes_test به طور اتوماتیک تصدیق شدن کاربر را بررسی نمی کند؛ شما باید آن را برای خودتان انجام دهید.

همچنین در این مثال آرگومان دوم (اختیاری) نشان داده شده است، که اجازه ی تعیین URL برای صفحه ی خودتان را می دهد (/accounts/login/ به طور پیشفرض). در صورتی که کاربر آزمون را نگذرانده باشد؛ سپس decorator، user_passes_test کاربر را به login_url تغییر مسیر خواهد داد.

به دلیل آن که بررسی این که یک کاربر دارای یک حق دسترسی خاص است یا خیر یک وظیفه ی نسبتا مشترک می باشد، جنگو یک میانبر برای آن ارائه کرده است که یک decorator با نام permission_required() می باشد. در دست ترجمه/تالیف ...:

from django.contrib.auth.decorators import permission_required @permission_required('polls.can_vote', login_url="/login/") def vote(request): # ...

دقت داشته باشد که همچنین permission_required() یک پارامتر اختیاری login_url دریافت می کند؛ که این نیز به طور پیشفرض '/accounts/login/' می باشد.

from django.contrib.auth.decorators import login_required from django.views.generic.date_based import object_detail @login_required def limited_object_detail(*args, **kwargs): return object_detail(*args, **kwargs)

البته که می توانید، login_required را با هر decorator محدودیت دیگری جا به جا کنید.

مدیریت کاربران، حق دسترسی ها و گروه ها

ساده ترین را برای مدیریت سیستم auth تاکنون، از طریق رابط مدیر بوده است. سایت مدیر در مورد نحوه ی استفاده از سایت مدیر جنگو را جهت ویرایش کاربران و کنترل حق دسترسی آن ها بحث کرده است، و اغلب اوقات شما فقط از این رابط استفاده خواهید کرد.

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

ساختن کاربران

ساختن کاربران با تابع کمکی create_user:

>>> from django.contrib.auth.models import User >>> user = User.objects.create_user(username='john', ... email='jlennon@beatles.com', ... password='glass onion')

در این نقطه، user یک رابط نمونه ی User حاضر برای ذخیره شدن در پایگاه داده (create_user() در واقع save() خودش را فراخوانی نمی کند) می باشد. همچنین می توانید قبل از ذخیره attribute های آن را تغییر دهید:

>>> user.is_staff = True >>> user.save()

تغییر رمزهای عبور

می توان یک رمز عبور را با set_password() تغییر داد:

>>> user = User.objects.get(username='john') >>> user.set_password('goo goo goo joob') >>> user.save()

attribute، password را به طور مستقیم قرار ندهید، مگر اینکه کاملا بدانید که چه کار می کنید. رمز عبور در واقع به صورت یک salted hash دخیره شده و در نتیجه نمی تواند به طور مستقیم ویرایش شود.

به طور رسمی تر، attribute، password از یک شیء User یک رشته در این قالب بندی می باشد:

hashtype$salt$hash

آن یک نوع hash، salt و خود hash، جدا شده توسط حرف ($) می باشد.

hashtype همچنین sha1 (پیشفرض) یا md5 می باشد، الگوریتم استفاده شده برای انجام یک hash یک طرفه از رمز عبور. salt یک رشته ی تصادفی استفاده شده برای اضافه شدن به رمز عبور خام برای ساختن hash برای مثال:

sha1$a1976$a36cc8cbf81742a8fb52e221aaeab48ed7f58ab4

توابع User.set.password() و User.check_password() این مقادیر را در پشت صحنه بررسی کرده و قرار می دهند.

کنترل عضویت

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

در ساده طرین حالت، می توان یک view کوچک برای اطلاعات الزامی کاربر و ساختن آن کاربران تهیه کرد. جنگو یک فرم داخلی ارائه کرده است که می توان برای این منظور از آن استفاده کرد، که در مثال زیر استفاده خواهیم کرد:

from django import forms from django.contrib.auth.forms import UserCreationForm from django.http import HttpResponseRedirect from django.shortcuts import render_to_response def register(request): if request.method == 'POST': form = UserCreationForm(request.POST) if form.is_valid(): new_user = form.save() return HttpResponseRedirect("/books/") else: form = UserCreationForm() return render_to_response("registration/register.html", { 'form': form, })

این فرم یک template با نام registration/register.html را فرض می کند. در اینجا یک مثال از template مورد نظر وجود دارد:

{% extends "base.html" %} {% block title %}Create an account{% endblock %} {% block content %} <h1>Create an account</h1> <form action="" method="post"> {{ form.as_p }} <input type="submit" value="Create the account"> </form> {% endblock %}

استفاده از تصدیق داده در Template ها

کاربر وارد شده ی فعلی و حق دسترسی های وی، زمانی که از RequestContext (آموزش template پیشرفته را نگاه کنید) استفاده می کنید در template context در دسترس می باشد.

هنگامی که از RequestContext استفاده می کنید، کاربر فعلی (که می تواند هم یک نمونه از User یا یک نمونه ی AnonymousUser باشد) در متغیر template {{ user }} ذخیره می شود:

{% if user.is_authenticated %} <p>Welcome, {{ user.username }}. Thanks for logging in.</p> {% else %} <p>Welcome, new user. Please log in.</p> {% endif %}

حق دسترسی های این کاربر در متغیر template {{ perms }} ذخیره شده اند. این یک پروکسی template‑friendly برای تعدادی از متدهای حق دسترسی می باشد که به طور خلاصه توضیح داده شده است.

برای استفاده از شیء perms دو روش وجود دارد. می توان در صورتی که کاربر دارای هیچ حق دسترسی برای برخی برنامه های داده شده نداشته باشد، برای بررسی آن از چیزی شبیه به این {% if perms.polls %} استفاده کرد، یا می توان در صورتی که کاربر دارای حق دسترسی خاصی می باشد برای بررسی آن از چیزی شبیه به این {% if perms.polls.can_vote %} استفاده کرد.

در نتیجه، می توان حق دسترسی ها را در عبارت template {% if %} بررسی کرد:

{% if perms.polls %} <p>You have permission to do something in the polls app.</p> {% if perms.polls.can_vote %} <p>You can vote!</p> {% endif %} {% else %} <p>You don't have permission to do anything in the polls app.</p> {% endif %}

حق دسترسی ها، گروه ها و پیام ها

چند قسمت دیگر از فریم ورک authentication وجود دارد که تنها به طور روزنامه وار از کنار آن عبور کردیم. در ادامه نگاهی نزدیک تر به آن ها خواهیم داشت.

حق دسترسی ها

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

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

حق دسترسی ها به صورت globally برای هر نوع از آجکت، نه هر نمونه ی خاص از شیء قرار داده شده اند. برای مثال، می توان گفت "Mary قادر است اخبار را تغییر دهد" ولی حق دسترسی ها اجازه نمی دهد که به عنوان مثال بگویید "Mary قادر است اخبار تغییر دهید، ولی تنها آنهایی را که خود او ساخته است" یا "Mary قادر است تنها اخباری را تغییر دهید که دارای یک وضعیت خاصی، انتشار یا ID خاص باشد."

این ها سه حق دسترسی اساسی می باشد – اضافه کردن، تغییر دادن، و حذف کردن – که به طور خودکار برای هر مدل جنگو ایجاد شده اند. در پشت صحنه، این حق دسترسی ها هنگامی که شما دستور manage.py sycdb را اجرا می کنید درون جدول پایگاه داده با نام auth_permission اضافه می شوند.

این حق دسترسی ها فرمی از "._" می باشند. بدین معنی که اگر دارای یک برنامه ی polls (نظرسنجی) با یک مدل Choice می باشید، حق دسترسی های "polls.add_choice"، "polls.change_choice" و "polls.delete_choice" را بدست خواهید آورد.

درست مثل کاربران، حق دسترسی ها در یک مدل جنگو موجود در django.contrib.auth.models اجرا شده اند. این یعنی این که می توان در صورت تمایل ارتباط با permission ها، به طور مستقیم از API پایگاه داده ی جنگو استفاده کرد.

گروه ها

گروه ها یک view جنریک از طبقه بندی کاربران می باشد، بنابراین می توان حق دسترسی ها، یا برخی چیزهای دگر را برای آن کاربران بکار برد. یک کاربر می تواند به هر تعدادی از گروه ها تعلق داشته باشد.

یک کاربر در یک گروه به طور خودکار دارای حق دسترسی های داده به آن گروه می باشد. برای مثال، در صورتی که گروه Site editors دارای حق دسترسی can_edit_home_page باشد، هر کاربر در آن گروه آن حق دسترسی را خواهد داشت.

گروه ها همچنین روش مناسبی برای طبقه بندی کاربران برای دادن برخی لیبل ها یا قابلیت تمدید به آن ها می باشد. برای مثال، می توان یک گروه با نام 'Special users' ایجاد نمود، و آن قبیل از کاربرانی را که می خواهیم در یک بخش از سایت که تنها برای دسترسی کاربران در نظر گرفته شده است فعالیت کنند را در آن قرار دهیم، یا پیام هایی که تنها برای کاربران در نظر گرفته ایم را به آن ها ارسال کنیم.

همانند کاربران، ساده ترین روش برای مدیریت گروه ها، از طریق رابط مدیر می باشد. هر چند، گروه ها نیز تنها مدل های جنگو می باشند که در django.contrib.auth.models وجود دارند. بنابراین شما می توانید همواره برای سرو کار داشتن با گروه ها در سطح پایین از API پایگاه داده ی جنگو استفاده کنید.

پیام ها

سیستم پیام یک روش سبک جهت به صف کردن پیام برای کاربران داده شده می باشد. یک پیام، مرتبط با یک User می باشد. هیچ مفهومی از انقضا یا برچسب زمانی وجودد ندارد.

پیام ها توسط رابط مدیر جنگو بعد از اعمال موفقیت آمیز استفاده می شوند. زمانی که شما یک آجکت ایجاد می کند، متوجه ی یک پیام "The object was created successfully" در بالای صفحه ی مدیر خواهید شد.

می توانید از یک API هم شکل برای صف بندی و نمایش پیام ها در برنامه ی خودتان استفاده کنید. API ساده می باشد:

در مثال view زیر، سیستم یک پیام برای کاربر بعد از ساختن یک playlist ذخیره می کند:

def create_playlist(request, songs): # Create the playlist with the given songs. # ... request.user.message_set.create( message="Your playlist was added successfully." ) return render_to_response("playlists/create.html", context_instance=RequestContext(request))

هنگامی که شما از RequestContext استفاده می کند، کاربر وارد شده ی فعلی و پیام او در template context به صورت متغیر template {{ message }} در دسترس هستند. در زیر یک مثال از کد template وجود دارد که پیام ها را نمایش می دهد:

{% if messages %} <ul> {% for message in messages %} <li>{{ message }}</li> {% endfor %} </ul> {% endif %}

دقت داشته باشید که RequestContext در پشت صحنه get_and_delete_messages را فراخوانی می کند، بنابراین هر پیامی حذف شده خواهد بود حتی اگر آن ها را نمایش ندهید.

در پایان، دقت داشته باشید که این فریم ورک پیام ها تنها با کاربران در پایگاه داده ی کاربر کار می کنند. برای ارسال پیام ها به کاربران anonymous، از چارچوب session به طور مستقیم استفاده کنید.