Its time for a confession: weve been deliberately ignoring an incredibly important aspect of Web development prior to this point. So far, weve thought of the traffic visiting our sites as some faceless, anonymous mass hurtling itself against our carefully designed pages.
鏄椂鍊欐壙璁や簡锛氭垜浠湁鎰忕殑閬垮紑浜唚eb寮鍙戜腑鏋佸叾閲嶈鐨勬柟闈€傚埌鐩墠涓烘锛屾垜浠兘鍦ㄥ亣瀹氾紝缃戠珯娴侀噺鏄ぇ閲忕殑鍖垮悕鐢ㄦ埛甯︽潵鐨勩
This isnt true, of course. The browsers hitting our sites have real humans behind them (some of the time, at least). Thats a big thing to ignore: the Internet is at its best when it serves to connect people , not machines. If were going to develop truly compelling sites, eventually were going to have to deal with the bodies behind the browsers.
杩欏綋鐒朵笉瀵癸紝娴忚鍣ㄧ殑鑳屽悗閮芥槸娲荤敓鐢熺殑浜(鑷冲皯鏌愪簺鏃跺欐槸)銆傛垜浠拷鐣ヤ簡涓浠堕噸瑕佺殑浜嬫儏锛氫簰鑱旂綉鏈嶅姟浜庝汉鑰屼笉鏄満鍣ㄣ傝寮鍙戜竴涓湡姝d护浜哄績鍔ㄧ殑缃戠珯锛屾垜浠繀椤婚潰瀵规祻瑙堝櫒鍚庨潰娲荤敓鐢熺殑浜恒
Unfortunately, its not all that easy. HTTP is designed to be stateless that is, each and every request happens in a vacuum. Theres no persistence between one request and the next, and we cant count on any aspects of a request (IP address, user agent, etc.) to consistently indicate successive requests from the same person.
寰堜笉骞革紝杩欏苟涓嶅鏄撱侶TTP琚璁′负”鏃犵姸鎬”锛屾瘡娆¤姹傞兘澶勪簬鐩稿悓鐨勭┖闂翠腑銆傚湪涓娆¤姹傚拰涓嬩竴娆¤姹備箣闂存病鏈変换浣曠姸鎬佷繚鎸侊紝鎴戜滑鏃犳硶鏍规嵁璇锋眰鐨勪换浣曟柟闈(IP鍦板潃锛岀敤鎴蜂唬鐞嗙瓑)鏉ヨ瘑鍒潵鑷悓涓浜虹殑杩炵画璇锋眰銆
In this chapter youll learn how to handle this lack of state. Well start at the lowest level (cookies ), and work up to the high-level tools for handling sessions, users and registration.
鍦ㄦ湰绔犱腑浣犲皢瀛︿細濡備綍鎼炲畾鐘舵佺殑闂銆傚ソ浜嗭紝鎴戜滑浼氫粠杈冧綆鐨勫眰娆(cookies)寮濮嬶紝鐒跺悗杩囨浮鍒扮敤楂樺眰鐨勫伐鍏锋潵鎼炲畾浼氳瘽锛岀敤鎴峰拰娉ㄥ唽鐨勯棶棰樸
Browser developers long ago recognized that HTTPs statelessness poses a huge problem for Web developers, and thus cookies were born. A cookie is a small piece of information that browsers store on behalf of Web servers. Every time a browser requests a page from a certain server, it gives back the cookie that it initially received.
娴忚鍣ㄧ殑寮鍙戣呭湪寰堟棭鐨勬椂鍊欏氨宸茬粡鎰忚瘑鍒帮紝 HTTP’s 鐨勬棤鐘舵佷細瀵筗eb寮鍙戣呭甫鏉ュ緢澶х殑闂锛屼簬鏄(cookies)搴旇繍鑰岀敓銆俢ookies 鏄祻瑙堝櫒涓 Web 鏈嶅姟鍣ㄥ瓨鍌ㄧ殑涓灏忔淇℃伅銆傛瘡娆℃祻瑙堝櫒浠庢煇涓湇鍔″櫒璇锋眰椤甸潰鏃讹紝瀹冨悜鏈嶅姟鍣ㄥ洖閫佷箣鍓嶆敹鍒扮殑cookies
Lets take a look how this might work. When you open your browser and type in google.com , your browser sends an HTTP request to Google that starts something like this:
鏉ョ湅鐪嬪畠鏄庝箞宸ヤ綔鐨勩傚綋浣犳墦寮娴忚鍣ㄥ苟璁块棶 google.com 锛屼綘鐨勬祻瑙堝櫒浼氱粰Google鍙戦佷竴涓狧TTP璇锋眰锛岃捣濮嬮儴鍒嗗氨璞¤繖鏍凤細
GET / HTTP/1.1 Host: google.com ...
When Google replies, the HTTP response looks something like the following:
褰 Google鍝嶅簲鏃讹紝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 ...
Notice the Set-Cookie header. Your browser will store that cookie value (PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671 ) and serve it back to Google every time you access the site. So the next time you access Google, your browser is going to send a request like this:
娉ㄦ剰 Set-Cookie 鐨勫ご閮ㄣ備綘鐨勬祻瑙堝櫒浼氬瓨鍌╟ookie鍊( PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671 ) 锛岃屼笖姣忔璁块棶google 绔欑偣閮戒細鍥為佽繖涓猚ookie鍊笺傚洜姝ゅ綋浣犱笅娆¤闂瓽oogle鏃讹紝浣犵殑娴忚鍣ㄤ細鍙戦佸儚杩欐牱鐨勮姹傦細
GET / HTTP/1.1 Host: google.com Cookie: PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671 ...
Google then can use that Cookie value to know that youre the same person who accessed the site earlier. This value might, for example, be a key into a database that stores user information. Google could (and does) use it to display your name on the page.
浜庢槸 Cookies 鐨勫间細鍛婅瘔Google锛屼綘灏辨槸鏃╀簺鏃跺欒闂繃Google缃戠珯鐨勪汉銆傝繖涓煎彲鑳芥槸鏁版嵁搴撲腑瀛樺偍鐢ㄦ埛淇℃伅鐨刱ey锛屽彲浠ョ敤瀹冨湪椤甸潰涓婃樉绀轰綘鐨勭敤鎴峰悕銆
When dealing with persistence in Django, most of the time youll want to use the higher-level session and/or user frameworks discussed a little later in this chapter. However, well pause and look at how to read and write cookies at a low level. This should help you understand how the rest of the tools discussed in the chapter actually work, and it will come in handy if you ever need to play with cookies directly.
鍦―jango涓鐞嗘寔涔呭寲锛屽ぇ閮ㄥ垎鏃跺欎綘浼氭洿鎰挎剰鐢ㄩ珮灞備簺鐨剆ession 鍜/鎴 鍚庨潰瑕佽璁虹殑user 妗嗘灦銆備絾鍦ㄦ涔嬪墠锛屾垜浠渶瑕佸仠涓嬫潵鍦ㄥ簳灞傜湅鐪嬪浣曡鍐檆ookies銆傝繖浼氬府鍔╀綘鐞嗚В鏈珷鑺傚悗闈㈣璁ㄨ鐨勫伐鍏锋槸濡備綍宸ヤ綔鐨勶紝鑰屼笖濡傛灉浣犻渶瑕佽嚜宸辨搷浣渃ookies锛岃繖涔熶細鏈夋墍甯姪銆
Reading cookies that are already set is incredibly simple. Every request object has a COOKIES object that acts like a dictionary; you can use it to read any cookies that the browser has sent to the view:
璇诲彇宸茬粡璁剧疆濂界殑cookies鏋佸叾绠鍗曪紝姣忎釜request瀵硅薄閮芥湁涓涓 COOKIES 瀵硅薄锛屽彲浠ヨ薄浣跨敤瀛楀吀鑸娇鐢ㄥ畠锛屼綘鍙互璇诲彇浠讳綍娴忚鍣ㄥ彂缁欒鍥(view)鐨勪换浣昪ookies:
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.")
Writing cookies is slightly more complicated. You need to use the set_cookie() method on an HttpResponse object. Heres an example that sets the favorite_color cookie based on a GET parameter:
鍐檆ookies绋嶅井澶嶆潅鐐癸紝闇瑕佺敤 HttpResponse 瀵硅薄鐨 set_cookie() 鏂规硶鏉ュ啓銆傝繖鍎挎湁涓熀浜 GET 鍙傛暟鏉ヨ缃 favorite_color cookie鐨勪緥瀛愶細
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.")
You can also pass a number of optional arguments to response.set_cookie() that control aspects of the cookie, as shown in Table 12-1.
浣犲彲浠ョ粰 response.set_cookie() 浼犻掍竴浜涘彲閫夌殑鍙傛暟鏉ユ帶鍒禼ookie鐨勮涓猴紝璇﹁琛12-1銆
Parameter | Default | Description |
---|---|---|
max_age | None | Age (in seconds) that the cookie should last. If this parameter is None , the cookie will last only until the browser is closed. |
expires | None | The actual date/time when the cookie should expire. It needs to be in the format "Wdy, DD-Mth-YY HH:MM:SS GMT" . If given, this parameter overrides the max_age parameter. |
path | "/" | The path prefix that this cookie is valid for. Browsers will only pass the cookie back to pages below this path prefix, so you can use this to prevent cookies from being sent to other sections of your site. This is especially useful when you dont control the top level of your sites domain. |
domain | None | The domain that this cookie is valid for. You can use this parameter to set a cross-domain cookie. For example, domain=".example.com" will set a cookie that is readable by the domains www.example.com , www2.example.com , and an.other.sub.domain.example.com . If this parameter is set to None , a cookie will only be readable by the domain that set it. |
secure | False | If set to True , this parameter instructs the browser to only return this cookie to pages accessed over HTTPS. |
鍙傛暟 | 缂虹渷鍊 | 鎻忚堪 |
---|---|---|
max_age | None | cookies鐨勬寔缁湁鏁堟椂闂达紙浠ョ璁★級锛屽鏋滆缃负 None cookies 鍦ㄦ祻瑙堝櫒鍏抽棴鐨勬椂鍊欏氨澶辨晥浜嗐 |
expires | None | cookies鐨勮繃鏈熸椂闂达紝鏍煎紡锛 "Wdy, DD-Mth-YY HH:MM:SS GMT" 濡傛灉璁剧疆杩欎釜鍙傛暟锛屽畠灏嗚鐩 max_age 鍙傛暟銆 |
path | "/" | cookie鐢熸晥鐨勮矾寰勫墠缂锛屾祻瑙堝櫒鍙細鎶奵ookie鍥炰紶缁欏甫鏈夎璺緞鐨勯〉 闈紝杩欐牱浣犲彲浠ラ伩鍏嶅皢cookie浼犵粰绔欑偣涓殑鍏朵粬鐨勫簲鐢ㄣ 褰撲綘鐨勫簲鐢ㄤ笉澶勪簬绔欑偣椤跺眰鐨勬椂鍊欙紝杩欎釜鍙傛暟浼氶潪甯告湁鐢ㄣ |
domain | None | cookie鐢熸晥鐨勭珯鐐广備綘鍙敤杩欎釜鍙傛暟鏉ユ瀯閫犱竴涓法绔檆ookie銆傚锛 domain=".example.com" 鎵鏋勯犵殑cookie瀵逛笅闈㈣繖浜涚珯鐐归兘鏄彲 璇荤殑锛 www.example.com 銆 www2.example.com 鍜 an.other.sub.domain.example.com 銆 濡傛灉璇ュ弬鏁拌缃负 None 锛宑ookie鍙兘鐢辫缃畠鐨勭珯鐐硅鍙栥 |
secure | False | 濡傛灉璁剧疆涓 True 锛屾祻瑙堝櫒灏嗛氳繃HTTPS鏉ュ洖浼燾ookie銆 |
You might notice a number of potential problems with the way cookies work. Lets look at some of the more important ones:
涔熻浣犲凡缁忔敞鎰忓埌浜嗭紝cookies鐨勫伐浣滄柟寮忓彲鑳藉鑷寸殑闂锛屼竴璧锋潵鐪嬬湅鍏朵腑涓浜涢噸瑕佺殑鏂归潰锛
Storage of cookies is essentially voluntary; browsers dont guarantee anything. In fact, all browsers enable users to control the policy for accepting cookies. If you want to see just how vital cookies are to the Web, try turning on your browsers prompt to accept every cookie option.
cookies瀛樺彇瀹屽叏鏄潪寮哄埗鎬х殑锛屾祻瑙堝櫒涓嶄繚璇佽繖涓鐐广備簨瀹炰笂锛屾墍鏈夌殑娴忚鍣ㄩ兘璁╃敤鎴疯嚜宸辨帶鍒 鏄惁鎺ュ彈cookies銆傚鏋滀綘鎯崇煡閬揷ookies瀵逛簬web搴旂敤鏈夊閲嶈锛屼綘鍙互璇曠潃鎵撳紑杩欎釜娴忚鍣ㄧ殑 閫夐」锛氭彁绀烘垜鎺ュ彈姣忔cookie銆
Despite their nearly universal use, cookies are still the definition of unreliability. This means that developers should check that a user actually accepts cookies before relying on them.
灏界cookies骞夸负浣跨敤锛屼絾浠嶈璁や负鏄笉鍙潬鐨勭殑銆傝繖鎰忓懗鐫锛屽紑鍙戣呬娇鐢╟ookies涔嬪墠蹇呴』 妫鏌ョ敤鎴锋槸鍚﹀彲浠ユ帴鏀禼ookie銆
More important, you should never store important data in cookies. The Web is filled with horror stories of developers whove stored unrecoverable information in browser cookies only to have that data purged by the browser for one reason or another.
鏇撮噸瑕佺殑鏄紝*姘歌繙* 涔熶笉瑕佸湪cookie涓瓨鍌ㄩ噸瑕佺殑鏁版嵁銆傚紑鍙戣呭湪cookie涓瓨鍌ㄤ簡涓嶅彲鎭㈠ 鐨勬暟鎹紝鑰屾祻瑙堝櫒鍗村洜涓烘煇绉嶅師鍥犲皢cookie涓殑鏁版嵁娓呭緱涓骞蹭簩鍑锛岃繖鏍蜂护浜哄彂鎸囩殑鏁呬簨鍦 Web涓栫晫涓瘮姣旂殕鏄
Cookies (especially those not sent over HTTPS) are not secure. Because HTTP data is sent in cleartext, cookies are extremely vulnerable to snooping attacks. That is, an attacker snooping on the wire can intercept a cookie and read it. This means you should never store sensitive information in a cookie.
Cookie(鐗瑰埆鏄偅浜涙病閫氳繃HTTPS浼犺緭鐨)鏄潪甯镐笉瀹夊叏鐨勩傚洜涓篐TTP鏁版嵁鏄互鏄庢枃鍙戦佺殑锛屾墍浠 鐗瑰埆瀹规槗鍙楀埌鍡呮帰鏀诲嚮銆備篃灏辨槸璇达紝鍡呮帰鏀诲嚮鑰呭彲浠ュ湪缃戠粶涓嫤鎴苟璇诲彇cookies锛屽洜姝や綘瑕 缁濆閬垮厤鍦╟ookies涓瓨鍌ㄦ晱鎰熶俊鎭
Theres an even more insidious attack, known as a man-in-the-middle attack, wherein an attacker intercepts a cookie and uses it to pose as another user. Chapter 19 discusses attacks of this nature in depth, as well as ways to prevent it.
杩樻湁涓绉嶈绉颁负”涓棿浜”鐨勬敾鍑绘洿闃撮櫓锛屾敾鍑昏呮嫤鎴竴涓猚ookie骞跺皢鍏剁敤浜庡彟涓涓敤鎴枫 绗19绔犲皢娣卞叆璁ㄨ杩欑鏀诲嚮鐨勬湰璐ㄤ互鍙婂浣曢伩鍏嶃
Cookies arent even secure from their intended recipients. Most browsers provide easy ways to edit the content of individual cookies, and resourceful users can always use tools like mechanize ( http://wwwsearch.sourceforge.net/mechanize/ ) to construct HTTP requests by hand.
鍗充娇浠庨鎯充腑鐨勬帴鏀惰呰繑鍥炵殑cookie涔熸槸涓嶅畨鍏ㄧ殑锛屽洜涓哄ぇ澶氭暟娴忚鍣ㄩ兘鎻愪緵浜嗗緢鏂逛究鐨勬柟娉曟潵 淇敼cookies鐨勫唴瀹癸紝鏈夋妧鏈儗鏅殑鐢ㄦ埛鐢氳嚦鍙互鐢ㄥ儚mechanize ( http://wwwsearch.sourceforge.net/mechanize/ ) 杩欐牱鐨勫伐鍏锋潵鎵嬪伐鏋勯燞TTP璇锋眰銆
So you cant store data in cookies that might be sensitive to tampering. The canonical mistake in this scenario is storing something like IsLoggedIn=1 in a cookie when a user logs in. Youd be amazed at the number of sites that make mistakes of this nature; it takes only a second to fool these sites security systems.
鍥犳涓嶈兘鍦╟ookies涓瓨鍌ㄥ彲鑳戒細琚鏀圭殑鏁忔劅鏁版嵁锛屸滅粡鍏糕濋敊璇槸锛氬湪cookies涓瓨鍌 IsLoggedIn=1 锛屼互鏍囪瘑鐢ㄦ埛宸茬粡鐧诲綍銆傜姱杩欑被閿欒鐨勭珯鐐规暟閲忓鐨勪护浜洪毦浠ョ疆淇★紱 缁曡繃杩欎簺缃戠珯鐨勫畨鍏ㄧ郴缁熶篃鏄槗濡傚弽鎺屻
With all of these limitations and potential security holes, its obvious that cookies and persistent sessions are examples of those pain points in Web development. Of course, Djangos goal is to be an effective painkiller, so it comes with a session framework designed to smooth over these difficulties for you.
鐢变簬瀛樺湪鐨勯檺鍒朵笌瀹夊叏婕忔礊锛宑ookies鍜屾寔缁т細璇濆凡缁忔垚涓篧eb寮鍙戜腑浠や汉澶寸柤鐨勫吀鑼冦傚ソ娑堟伅鏄紝Django鐨勭洰鏍囨鏄珮鏁堢殑鈥滃ご鐤兼潃鎵嬧濓紝瀹冭嚜甯︾殑session妗嗘灦浼氬府浣犳悶瀹氳繖浜涢棶棰樸
This session framework lets you store and retrieve arbitrary data on a per-site visitor basis. It stores data on the server side and abstracts the sending and receiving of cookies. Cookies use only a hashed session IDnot the data itselfthus protecting you from most of the common cookie problems.
浣犲彲浠ョ敤session 妗嗘灦鏉ュ瓨鍙栨瘡涓闂呬换鎰忔暟鎹紝杩欎簺鏁版嵁鍦ㄦ湇鍔″櫒绔瓨鍌紝骞剁敤閫氳繃cookie鏉ヤ紶杈撴暟鎹憳瑕併俢ookies鍙瓨鍌ㄦ暟鎹殑鍝堝笇浼氳瘽ID锛岃屼笉鏄暟鎹湰韬紝浠庤岄伩鍏嶄簡澶ч儴鍒嗙殑甯歌cookie闂銆
Lets look at how to enable sessions and use them in views.
涓嬮潰鎴戜滑鏉ョ湅鐪嬪浣曟墦寮session鍔熻兘锛屽苟鍦ㄨ鍥句腑浣跨敤瀹冦
Sessions are implemented via a piece of middleware (see Chapter 15) and a Django model. To enable sessions, youll need to follow these steps:
Sessions 鍔熻兘鏄氳繃涓涓腑闂翠欢(middleware)鍜屼竴涓ā鍨(model)鏉ュ疄鐜扮殑銆傝鎵撳紑sessions鍔熻兘锛岄渶瑕佷互涓嬪嚑姝ユ搷浣滐細
Edit your MIDDLEWARE_CLASSES setting and make sure MIDDLEWARE_CLASSES contains 'django.contrib.sessions.middleware.SessionMiddleware' .
缂栬緫 MIDDLEWARE_CLASSES 閰嶇疆锛岀‘淇 MIDDLEWARE_CLASSES 涓寘鍚 'django.contrib.sessions.middleware.SessionMiddleware'
Make sure 'django.contrib.sessions' is in your INSTALLED_APPS setting (and run manage.py syncdb if you have to add it).
纭 INSTALLED_APPS 涓湁 'django.contrib.sessions' (濡傛灉浣犳槸鍒氭墦寮杩欎釜搴旂敤锛屽埆蹇樹簡杩愯 manage.py syncdb )
The default skeleton settings created by startproject have both of these bits already installed, so unless youve removed them, you probably dont have to change anything to get sessions to work.
濡傛灉椤圭洰鏄敤 startproject 鏉ュ垱寤虹殑锛岄厤缃枃浠朵腑閮藉凡缁忓畨瑁呬簡杩欎簺涓滆タ锛岄櫎闈炰綘鑷繁鍒犻櫎锛屾甯告儏鍐典笅锛屼綘鏃犻渶浠讳綍璁剧疆灏卞彲浠ヤ娇鐢╯ession鍔熻兘銆
If you dont want to use sessions, you might want to remove the SessionMiddleware line from MIDDLEWARE_CLASSES and 'django.contrib.sessions' from your INSTALLED_APPS . It will save you only a small amount of overhead, but every little bit counts.
濡傛灉涓嶉渶瑕乻ession鍔熻兘锛屼綘鍙互鍒犻櫎 MIDDLEWARE_CLASSES 璁剧疆涓殑 SessionMiddleware 鍜 INSTALLED_APPS 璁剧疆涓殑 'django.contrib.sessions' 銆傝櫧鐒惰繖鍙細鑺傜渷寰堝皯鐨勫紑閿锛屼絾绉皯鎴愬鍟娿
When SessionMiddleware is activated, each HttpRequest objectthe first argument to any Django view functionwill have a session attribute, which is a dictionary-like object. You can read it and write to it in the same way youd use a normal dictionary. For example, in a view you could do stuff like this:
SessionMiddleware 婵娲诲悗锛屾瘡涓紶缁欒鍥(view)鍑芥暟鐨勭涓涓弬鏁癭`HttpRequest`` 瀵硅薄閮芥湁涓涓 session 灞炴э紝杩欐槸涓涓瓧鍏稿瀷鐨勫璞°備綘鍙互璞$敤鏅氬瓧鍏镐竴鏍锋潵鐢ㄥ畠銆備緥濡傦紝鍦ㄨ鍥(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: ...
You can also use other mapping methods like keys() and items() on request.session .
鍏朵粬鐨勬槧灏勬柟娉曪紝濡 keys() 鍜 items() 瀵 request.session 鍚屾牱鏈夋晥锛
There are a couple of simple rules for using Djangos sessions effectively:
涓嬮潰鏄竴浜涙湁鏁堜娇鐢―jango sessions鐨勭畝鍗曡鍒欙細
Use normal Python strings as dictionary keys on request.session (as opposed to integers, objects, etc.). This is more of a convention than a hard-and-fast rule, but its worth following.
鐢ㄦ甯哥殑瀛楃涓蹭綔涓簁ey鏉ヨ闂瓧鍏 request.session 锛 鑰屼笉鏄暣鏁般佸璞℃垨鍏跺畠浠涔堢殑銆 杩欎笉鏄粈涔堝己纭殑鏉¤锛屼絾鍊煎緱閬靛惊銆
Session dictionary keys that begin with an underscore are reserved for internal use by Django. In practice, the framework uses only a small number of underscore-prefixed session variables, but unless you know what they all are (and you are willing to keep up with any changes in Django itself), staying away from underscore prefixes will keep Django from interfering with your application.
Session瀛楀吀涓互涓嬪垝绾垮紑澶寸殑key鍊兼槸Django鍐呴儴淇濈暀key鍊笺傛鏋跺彧浼氱敤寰堝皯鐨勫嚑涓笅鍒掔嚎 寮澶寸殑session鍙橀噺锛岄櫎闈炰綘鐭ラ亾浠栦滑鐨勫叿浣撳惈涔夛紝鑰屼笖鎰挎剰璺熶笂Django鐨勫彉鍖栵紝鍚﹀垯锛屾渶濂 涓嶈鐢ㄨ繖浜涗笅鍒掔嚎寮澶寸殑鍙橀噺锛屽畠浠細璁〥jango鎼呬贡浣犵殑搴旂敤銆
Dont replace request.session with a new object, and dont access or set its attributes. Use it like a Python dictionary.
涓嶈鐢ㄤ竴涓柊瀵硅薄鏉ユ浛鎹㈡帀 request.session 锛屼篃涓嶈瀛樺彇鍏跺睘鎬э紝璞$敤鏅歅ython瀛楀吀涓鏍风敤瀹冦
Lets take a look at a few quick examples. This simplistic view sets a has_commented variable to True after a user posts a comment. Its a simple (but not particularly secure) way of preventing a user from posting more than one comment:
鎴戜滑鏉ョ湅涓畝鍗曠殑渚嬪瓙銆傝繖鏄釜绠鍗曞埌涓嶈兘鍐嶇畝鍗曠殑渚嬪瓙锛氬湪鐢ㄦ埛鍙戜簡涓娆¤瘎璁哄悗灏 has_commented 璁剧疆涓 True 锛岃繖鏄釜绠鍗曪紙浣嗕笉寰堝畨鍏級鐨勩侀槻姝㈢敤鎴峰娆¤瘎璁虹殑鏂规硶銆
def post_comment(request, new_comment): if request.session.get('has_commented', False): return HttpResponse("You've already commented.") c = comments.Comment(comment=new_comment) c.save() request.session['has_commented'] = True return HttpResponse('Thanks for your comment!')
This simplistic view logs in a member of the site:
涓嬮潰鏄竴涓緢绠鍗曠殑绔欑偣鐧诲綍瑙嗗浘(view)锛
def login(request): try: m = Member.objects.get(username__exact=request.POST['username']) if m.password == request.POST['password']: request.session['member_id'] = m.id return HttpResponse("You're logged in.") except Member.DoesNotExist: return HttpResponse("Your username and password didn't match.")
And this one logs out a member, according to login() :
杩欐槸閫鍑虹櫥褰曪紝鏍规嵁 login() :
def logout(request): try: del request.session['member_id'] except KeyError: pass return HttpResponse("You're logged out.")
Note
娉ㄦ剰
In practice, this is a lousy way of logging users in. The authentication framework discussed shortly handles this task for you in a much more robust and useful manner. These examples are deliberately simplistic so that you can easily see whats going on.
鍦ㄥ疄璺典腑锛岃繖鏄緢鐑傜殑鐢ㄦ埛鐧诲綍鏂瑰紡锛岀◢鍚庤璁虹殑璁よ瘉(authentication )妗嗘灦浼氬府浣犱互鏇村仴澹拰鏈夊埄鐨勬柟寮忔潵澶勭悊杩欎簺闂銆傝繖浜涢潪甯哥畝鍗曠殑渚嬪瓙鍙槸鎯宠浣犵煡閬撹繖涓鍒囨槸濡備綍宸ヤ綔鐨勩
As just mentioned, you cant rely on every browser accepting cookies. So, as a convenience, Django provides an easy way to test whether a users browser accepts cookies. You just need to call request.session.set_test_cookie() in a view, and check request.session.test_cookie_worked() in a subsequent viewnot in the same view call.
灏卞儚鍓嶉潰鎻愬埌鐨勶紝浣犱笉鑳芥寚鏈涙墍鏈夌殑娴忚鍣ㄩ兘鍙互鎺ュ彈cookie锛屽洜姝わ紝Django涓轰簡鏂逛究锛屼篃鎻愪緵浜嗘鏌ョ敤鎴锋祻瑙堝櫒鏄惁鎺ュ彈cookie鐨勭畝鍗曟柟娉曘備綘鍙渶鍦ㄨ鍥(view)涓皟鐢 request.session.set_test_cookie() 锛屽苟鍦ㄥ悗缁殑瑙嗗浘(view)銆佽屼笉鏄綋鍓嶇殑瑙嗗浘(view)涓鏌 request.session.test_cookie_worked() 銆
This awkward split between set_test_cookie() and test_cookie_worked() is necessary due to the way cookies work. When you set a cookie, you cant actually tell whether a browser accepted it until the browsers next request.
铏界劧鎶 set_test_cookie() 鍜 test_cookie_worked() 鍒嗗紑鐨勫仛娉曠湅璧锋潵鏈変簺绗ㄦ嫏锛屼絾鐢变簬cookie鐨勫伐浣滄柟寮忥紝杩欐棤鍙伩鍏嶃傚綋璁剧疆涓涓猚ookie鏃跺欙紝鍙兘绛夋祻瑙堝櫒涓嬫璁块棶鐨勬椂鍊欙紝浣犳墠鑳界煡閬撴祻瑙堝櫒鏄惁鎺ュ彈cookie銆
Its good practice to use delete_test_cookie() to clean up after yourself. Do this after youve verified that the test cookie worked.
妫鏌ookie鏄惁鍙互姝e父宸ヤ綔鍚庯紝浣犲緱鑷繁鐢 delete_test_cookie() 鏉ユ竻闄ゅ畠锛岃繖鏄釜濂戒範鎯
Heres a typical usage example:
杩欐槸涓吀鍨嬩緥瀛愶細
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 # was 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')
Note
娉ㄦ剰
Again, the built-in authentication functions handle this check for you.
鍐嶆寮鸿皟锛屽唴缃殑璁よ瘉鍑芥暟浼氬府浣犲仛妫鏌ョ殑銆
Internally, each session is just a normal Django model defined in django.contrib.sessions.models . Each session is identified by a more-or-less random 32-character hash stored in a cookie. Because its a normal model, you can access sessions using the normal Django database API:
浠庡唴閮ㄦ潵鐪嬶紝姣忎釜session閮藉彧鏄竴涓櫘閫氱殑Django model锛堝湪 django.contrib.sessions.models 涓畾涔)銆傛瘡涓猻ession閮界敱涓涓殢鏈虹殑32瀛楄妭鍝堝笇涓叉潵鏍囪瘑锛屽苟瀛樺偍浜庢暟鎹簱涓傜敱浜庤繖鏄竴涓櫘閫氱殑model锛屼綘鍙互鐢ㄤ竴鑸殑Django 鏁版嵁搴揂PI鏉ヨ鍙杝ession銆
>>> from django.contrib.sessions.models import Session >>> s = Session.objects.get(pk='2b1189a188b44ad18c35e113ac6ceead') >>> s.expire_date datetime.datetime(2005, 8, 20, 13, 35, 12)
Youll need to call get_decoded() to get the actual session data. This is necessary because the dictionary is stored in an encoded format:
浣犲緱鐢 get_decoded() 鏉ヨ鍙栧疄闄呯殑session鏁版嵁锛屽洜涓簊ession瀛楀吀缁忚繃浜嗙紪鐮佸瓨鍌ㄣ
>>> s.session_data 'KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj...' >>> s.get_decoded() {'user_id': 42}
By default, Django only saves to the database if the session has been modified that is, if any of its dictionary values have been assigned or deleted:
缂虹渷鐨勬儏鍐典笅锛孌jango鍙細鍦╯ession鍙戠敓鍙樺寲鐨勬椂鍊欐墠浼氬瓨鍏ユ暟鎹簱锛屾瘮濡傝锛屽瓧鍏歌祴鍊兼垨鍒犻櫎銆
# 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'
To change this default behavior, set SESSION_SAVE_EVERY_REQUEST to True . If SESSION_SAVE_EVERY_REQUEST is True , Django will save the session to the database on every single request, even if it wasnt changed.
浣犲彲浠ヨ缃 SESSION_SAVE_EVERY_REQUEST 涓 True 鏉ユ敼鍙樿繖涓缂虹渷琛屼负銆傚鏋 SESSION_SAVE_EVERY_REQUEST 璁剧疆涓 True 锛孌jango 浼氬湪姣忔璇锋眰鐨勬椂鍊欓兘鎶妔ession瀛樺埌鏁版嵁搴撲腑锛屽嵆浣挎病鏈変换浣曟敼鍙樸
Note that the session cookie is sent only when a session has been created or modified. If SESSION_SAVE_EVERY_REQUEST is True , the session cookie will be sent on every request. Similarly, the expires part of a session cookie is updated each time the session cookie is sent.
娉ㄦ剰锛屼細璇漜ookie鍙細鍦ㄥ垱寤哄拰淇敼鐨勬椂鍊欐墠浼氶佸嚭銆備絾濡傛灉 SESSION_SAVE_EVERY_REQUEST 璁剧疆涓 True 锛屼細璇漜ookie浼氬湪姣忔璇锋眰鐨勬椂鍊欓兘浼氶佸嚭銆傚悓鏃讹紝姣忔浼氳瘽cookie閫佸嚭鐨勬椂鍊欙紝鍏 expires 鍙傛暟閮戒細鏇存柊銆
You might have noticed that the cookie Google sent us contained expires=Sun, 17-Jan-2038 19:14:07 GMT; . Cookies can optionally contain an expiration date that advises the browser on when to remove the cookie. If a cookie doesnt contain an expiration value, the browser will expire it when the user closes his or her browser window. You can control the session frameworks behavior in this regard with the SESSION_EXPIRE_AT_BROWSER_CLOSE setting.
浣犲彲鑳芥敞鎰忓埌浜嗭紝Google缁欐垜浠彂閫佺殑cookie涓湁 expires=Sun, 17-Jan-2038 19:14:07 GMT; cookie鍙互鏈夎繃鏈熸椂闂达紝杩欐牱娴忚鍣ㄥ氨鐭ラ亾浠涔堟椂鍊欏彲浠ュ垹闄ookie浜嗐傚鏋渃ookie娌℃湁璁剧疆杩囨湡鏃堕棿锛屽綋鐢ㄦ埛鍏抽棴娴忚鍣ㄧ殑鏃跺欙紝cookie灏辫嚜鍔ㄨ繃鏈熶簡銆備綘鍙互鏀瑰彉 SESSION_EXPIRE_AT_BROWSER_CLOSE 鐨勮缃潵鎺у埗session妗嗘灦鐨勮繖涓琛屼负銆
By default, SESSION_EXPIRE_AT_BROWSER_CLOSE is set to False , which means session cookies will be stored in users browsers for SESSION_COOKIE_AGE seconds (which defaults to two weeks, or 1,209,600 seconds). Use this if you dont want people to have to log in every time they open a browser.
缂虹渷鎯呭喌涓嬶紝 SESSION_EXPIRE_AT_BROWSER_CLOSE 璁剧疆涓 False 锛岃繖鏍凤紝浼氳瘽cookie鍙互鍦ㄧ敤鎴锋祻瑙堝櫒涓繚鎸佹湁鏁堣揪 SESSION_COOKIE_AGE 绉掞紙缂虹渷璁剧疆鏄袱鍛紝鍗1,209,600 绉掞級銆傚鏋滀綘涓嶆兂鐢ㄦ埛姣忔鎵撳紑娴忚鍣ㄩ兘蹇呴』閲嶆柊鐧婚檰鐨勮瘽锛岀敤杩欎釜鍙傛暟鏉ュ府浣犮
If SESSION_EXPIRE_AT_BROWSER_CLOSE is set to True , Django will use browser-length cookies.
濡傛灉 SESSION_EXPIRE_AT_BROWSER_CLOSE 璁剧疆涓 True 锛屽綋娴忚鍣ㄥ叧闂椂锛孌jango浼氫娇cookie澶辨晥銆
Besides the settings already mentioned, a few other settings influence how Djangos session framework uses cookies, as shown in Table 12-2.
闄や簡涓婇潰鎻愬埌鐨勮缃紝杩樻湁涓浜涘叾浠栫殑璁剧疆鍙互褰卞搷Django session妗嗘灦濡備綍浣跨敤cookie锛岃瑙佽〃 12-2.
Setting | Description | Default |
---|---|---|
SESSION_COOKIE_DOMAIN | The domain to use for session cookies. Set this to a string such as ".lawrence.com" for cross-domain cookies, or use None for a standard cookie. | None |
SESSION_COOKIE_NAME | The name of the cookie to use for sessions. This can be any string. | "sessionid" |
SESSION_COOKIE_SECURE | Whether to use a secure cookie for the session cookie. If this is set to True , the cookie will be marked as secure, which means that browsers will ensure that the cookie is only sent via HTTPS. | False |
璁剧疆 | 鎻忚堪 | 缂虹渷鍊 |
---|---|---|
SESSION_COOKIE_DOMAIN | session cookie鐢熸晥鐨勭珯鐐癸紝璺ㄧ珯鐐圭敓鏁堢殑 cookie鍙互杩欐牱璁剧疆锛歚`”.lawrence.com”`` None 涓烘爣鍑哻ookie | None |
SESSION_COOKIE_NAME | 鐢ㄤ簬session 鐨刢ookie鍚嶇О锛屽彲浠ユ槸浠讳綍 瀛楃涓 | "sessionid" |
SESSION_COOKIE_SECURE | 鏄惁鍦╯ession涓娇鐢ㄥ畨鍏╟ookie锛屽鏋滆缃 True , cookie灏变細鏍囪涓哄畨鍏紝 杩欐剰鍛崇潃cookie鍙細閫氳繃HTTPS鏉ヤ紶杈 | False |
Technical Details
鎶鏈粏鑺
For the curious, here are a few technical notes about the inner workings of the session framework:
濡傛灉浣犺繕鏄ソ濂囩殑璇濓紝涓嬮潰鏄竴浜涘叧浜巗ession妗嗘灦鍐呴儴宸ヤ綔鏂瑰紡鐨勬妧鏈粏鑺傦細
The session dictionary accepts any Python object capable of being pickled. See the documentation for Pythons built-in pickle module for information about how this works.
session 瀛楀吀鍜屾櫘閫歅ython瀵硅薄涓鏍凤紝鏀寔搴忓垪鍖栵紝璇﹁Python鏂囨。涓唴缃 pickle 妯″潡鐨勯儴鍒嗐
Session data is stored in a database table named django_session .
Session 鏁版嵁瀛樺湪鏁版嵁搴撹〃 django_session 涓
Session data is fetched upon demand. If you never access request.session , Django wont hit that database table.
Session 鏁版嵁鍦ㄩ渶瑕佺殑鏃跺欐墠浼氳鍙栵紝濡傛灉浣犱粠涓嶄娇鐢 request.session 锛 Django涓嶄細鍔ㄧ浉鍏虫暟鎹簱琛ㄧ殑涓鏍规瘺銆
Django only sends a cookie if it needs to. If you dont set any session data, it wont send a session cookie (unless SESSION_SAVE_EVERY_REQUEST is set to True ).
Django 鍙湪闇瑕佺殑鏃跺欐墠閫佸嚭cookie銆傚鏋滀綘鍘嬫牴鍎垮氨娌℃湁璁剧疆浠讳綍浼氳瘽鏁版嵁锛屽畠涓嶄細 閫佸嚭浼氳瘽cookie(闄ら潪 SESSION_SAVE_EVERY_REQUEST 璁剧疆涓 True )
The Django sessions framework is entirely, and solely, cookie based. It does not fall back to putting session IDs in URLs as a last resort, as some other tools (PHP, JSP) do.
Django session 妗嗘灦瀹屽叏鑰屼笖鍙兘鍩轰簬cookie锛屼笉浼氬悗閫鍒版妸浼氳瘽ID缂栫爜鍦║RL涓傦紙鍍忔煇浜涘伐鍏(PHP,JSP)閭f牱锛
This is an intentional design decision. Putting sessions in URLs dont just make URLs ugly, but also make your site vulnerable to a certain form of session ID theft via the Referer header.
杩欐槸涓涓湁鎰忚屼负涔嬬殑璁捐锛屾妸session鏀惧湪URL涓笉鍙槸闅剧湅锛屾洿閲嶈鐨勬槸杩欒浣犵殑绔欑偣 寰堝鏄撳彈鍒版敾鍑烩斺旈氳繃 Referer header杩涜session ID”绐冨惉”鑰屽疄鏂界殑鏀诲嚮銆
If youre still curious, the source is pretty straightforward; look in django.contrib.sessions for more details.
濡傛灉浣犺繕鏄ソ濂囷紝闃呰婧愪唬鐮佹槸鏈鐩存帴鍔炴硶锛岃瑙 django.contrib.sessions 銆
Were now halfway to linking browsers directly to Real People. Sessions give us a way of persisting data through multiple browser requests; the second part of the equation is using those sessions for user login. Of course, we cant just trust that users are who they say they are, so we need to authenticate them along the way.
鐜板湪锛屾垜浠氳繃娴忚鍣ㄨ繛鎺ョ湡瀹炵敤鎴风殑鐩爣宸茬粡瀹屾垚涓鍗婁簡銆傞氳繃session锛屾垜浠彲浠ュ湪澶氭娴忚鍣ㄨ姹備腑淇濇寔鏁版嵁锛 鎺ヤ笅鏉ョ殑閮ㄥ垎灏辨槸鐢╯ession鏉ュ鐞嗙敤鎴风櫥褰曚簡銆傚綋鐒讹紝涓嶈兘浠呭嚟鐢ㄦ埛鐨勪竴闈箣璇嶏紝鎴戜滑灏辩浉淇★紝鎵浠ユ垜浠渶瑕佽璇併
Naturally, Django provides tools to handle this common task (and many others). Djangos user authentication system handles user accounts, groups, permissions, and cookie-based user sessions. This system is often referred to as an auth/auth (authentication and authorization) system. That name recognizes that dealing with users is often a two-step process. We need to
褰撶劧浜嗭紝Django 涔熸彁渚涗簡宸ュ叿鏉ュ鐞嗚繖鏍风殑甯歌浠诲姟锛堝氨鍍忓叾浠栧父瑙佷换鍔′竴鏍凤級銆侱jango 鐢ㄦ埛璁よ瘉绯荤粺澶勭悊鐢ㄦ埛甯愬彿锛岀粍锛屾潈闄愪互鍙婂熀浜巆ookie鐨勭敤鎴蜂細璇濄傝繖涓郴缁熶竴鑸绉颁负 auth/auth (璁よ瘉涓庢巿鏉)绯荤粺锛岃繖涓郴缁熺殑鍚嶇О鍚屾椂涔熻〃鏄庝簡鐢ㄦ埛甯歌鐨勪袱姝ュ鐞嗐傛垜浠渶瑕
Verify (authenticate ) that a user is who he or she claims to be (usually by checking a username and password against a database of users)
楠岃瘉 (璁よ瘉) 鐢ㄦ埛鏄惁鏄粬鎵瀹gО鐨勭敤鎴(涓鑸氳繃鏌ヨ鏁版嵁搴撻獙璇佸叾鐢ㄦ埛鍚嶅拰瀵嗙爜)
Verify that the user is authorized to perform some given operation (usually by checking against a table of permissions)
楠岃瘉鐢ㄦ埛鏄惁鎷ユ湁鎵ц鏌愮鎿嶄綔鐨 鎺堟潈 (閫氬父浼氶氳繃妫鏌ヤ竴涓潈闄愯〃鏉ョ‘璁)
Following these needs, Djangos auth/auth system consists of a number of parts:
鏍规嵁杩欎簺闇姹傦紝Django 璁よ瘉/鎺堟潈 绯荤粺浼氬寘鍚互涓嬬殑閮ㄥ垎锛
Users : People registered with your site
鐢ㄦ埛 : 鍦ㄧ綉绔欐敞鍐岀殑浜
Permissions : Binary (yes/no) flags designating whether a user may perform a certain task
鏉冮檺 : 鐢ㄤ簬鏍囪瘑鐢ㄦ埛鏄惁鎷ユ湁鏌愮鎿嶄綔鐨勪簩杩涘埗(yes/no)鏍囧織
Groups : A generic way of applying labels and permissions to more than one user
缁 :涓绉嶅彲浠ュ皢鏍囪鍜屾潈闄愬簲鐢ㄤ簬澶氫釜鐢ㄦ埛鐨勫父鐢ㄦ柟娉
Messages : A simple way to queue and display system messages to users
Messages : 鍚戠敤鎴锋樉绀洪槦鍒楀紡鐨勭郴缁熸秷鎭殑甯哥敤鏂规硶
Profiles : A mechanism to extend the user object with custom fields
Profiles : 閫氳繃鑷畾涔夊瓧娈垫墿灞曠敤鎴峰璞$殑鏈哄埗
If youve used the admin tool (detailed in Chapter 6), youve already seen many of these tools, and if youve edited users or groups in the admin tool, youve actually been editing data in the auth systems database tables.
濡傛灉浣犲凡缁忕敤浜哸dmin宸ュ叿(璇﹁绗6绔)锛屽氨浼氱湅瑙佽繖浜涘伐鍏风殑澶ч儴鍒嗐傚鏋滃凡缁忕敤浜哸dmin宸ュ叿鏉ョ紪杈戠敤鎴峰拰缁勶紝浣犲疄闄呬笂灏卞凡缁忓湪缂栬緫璁よ瘉绯荤粺涓暟鎹簱琛ㄣ
Like the session tools, authentication support is bundled as a Django application in django.contrib , which needs to be installed. Like the session system, its also installed by default, but if youve removed it, youll need to follow these steps to install it:
鍍弒ession宸ュ叿涓鏍凤紝璁よ瘉鏀寔涔熸槸涓涓狣jango搴旂敤锛屾斁鍦 django.contrib 涓紝鎵浠ヤ篃闇瑕佸畨瑁呫備笌session绯荤粺鐩镐技锛屽畠涔熸槸缂虹渷瀹夎鐨勶紝浣嗗鏋滃畠宸茬粡琚垹闄や簡锛岄氳繃浠ヤ笅姝ラ涔熻兘閲嶆柊瀹夎涓婏細
Make sure the session framework is installed as described earlier in this chapter. Keeping track of users obviously requires cookies, and thus builds on the session framework.
鏍规嵁鏈珷鏃╁墠鐨勯儴鍒嗙‘璁ゅ凡缁忓畨瑁呬簡session 妗嗘灦锛岄渶瑕佺‘璁ょ敤鎴蜂娇鐢╟ookie锛岃繖鏍穝esson 妗嗘灦鎵嶈兘姝e父浣跨敤銆
Put 'django.contrib.auth' in your INSTALLED_APPS setting and run manage.py syncdb .
灏 'django.contrib.auth' 鏀惧湪浣犵殑 INSTALLED_APPS 璁剧疆涓紝鐒跺悗杩愯 manage.py syncdb
Make sure that 'django.contrib.auth.middleware.AuthenticationMiddleware' is in your MIDDLEWARE_CLASSES setting*after* SessionMiddleware .
纭 SessionMiddleware 鍚庨潰鐨 MIDDLEWARE_CLASSES 璁剧疆涓寘鍚 'django.contrib.auth.middleware.AuthenticationMiddleware'
With that installation out of the way, were ready to deal with users in view functions. The main interface youll use to access users within a view is request.user ; this is an object that represents the currently logged-in user. If the user isnt logged in, this will instead be an AnonymousUser object (see below for more details).
杩欐牱瀹夎鍚庯紝鎴戜滑灏卞彲浠ュ湪瑙嗗浘(view)鐨勫嚱鏁颁腑鐢ㄥ鐞唘ser浜嗐傚湪瑙嗗浘涓瓨鍙杣sers锛屼富瑕佺敤 request.user 锛涜繖涓璞¤〃绀哄綋鍓嶅凡鐧诲綍鐨勭敤鎴凤紝濡傛灉鐢ㄦ埛杩樻病鐧诲綍锛岃繖灏辨槸涓涓 鍖垮悕 瀵硅薄(缁嗚妭瑙佷笅)
You can easily tell if a user is logged in with the is_authenticated() method:
浣犲彲浠ュ緢瀹规槗鐨勯氳繃 is_authenticated() 鏂规硶鏉ュ垽鏂竴涓敤鎴锋槸鍚﹀凡缁忕櫥褰曚簡
if request.user.is_authenticated(): # Do something for authenticated users. else: # Do something for anonymous users.
Once you have a User often from request.user , but possibly through one of the other methods discussed shortlyyou have a number of fields and methods available on that object. AnonymousUser objects emulate some of this interface, but not all of it, so you should always check user.is_authenticated() before assuming youre dealing with a bona fide user object. Tables 12-3 and 12-4 list the fields and methods, respectively, on User objects.
User 瀹炰緥涓鑸粠 request.user 锛屾垨鏄叾浠栦笅闈㈠嵆灏嗚璁ㄨ鍒扮殑鏂规硶鍙栧緱锛屽畠鏈夊緢澶氬睘鎬у拰鏂规硶銆 AnonymousUser 瀵硅薄妯℃嫙浜 閮ㄥ垎 鐨勬帴鍙o紝浣嗕笉鏄叏閮紝鍦ㄦ妸瀹冨綋鎴愮湡姝g殑user瀵硅薄 浣跨敤鍓嶏紝浣犲緱妫鏌ヤ竴涓 user.is_authenticated()
Field | Description |
---|---|
username | Required; 30 characters or fewer. Alphanumeric characters only (letters, digits, and underscores). |
first_name | Optional; 30 characters or fewer. |
last_name | Optional; 30 characters or fewer. |
Optional. Email address. | |
password | Required. A hash of, and metadata about, the password (Django doesnt store the raw password). See the Passwords section for more about this value. |
is_staff | Boolean. Designates whether this user can access the admin site. |
is_active | Boolean. Designates whether this account can be used to log in. Set this flag to False instead of deleting accounts. |
is_superuser | Boolean. Designates that this user has all permissions without explicitly assigning them. |
last_login | A datetime of the users last login. This is set to the current date/time by default. |
date_joined | A datetime designating when the account was created. This is set to the current date/time by default when the account is created. |
灞炴 | 鎻忚堪 |
---|---|
username | 蹇呭~; 灏戜簬绛変簬30瀛楃. 鍙厑璁稿瓧绗︼紝鏁板瓧锛屼笅鍒掔嚎 |
first_name | 鍙; 灏戜簬绛変簬30瀛楃. |
last_name | 鍙; 灏戜簬绛変簬30瀛楃. |
鍙. 閭欢鍦板潃. | |
password | 蹇呭~. 瀵嗙爜鐨勬憳瑕乭ash(Django涓嶄細瀛樺偍鍘熷瀵嗙爜)锛岃瑙佸瘑鐮佺珷鑺傞儴鍒 |
is_staff | 甯冨皵鍊. 鐢ㄦ埛鏄惁鎷ユ湁缃戠珯鐨勭鐞嗘潈闄. |
is_active | 甯冨皵鍊. 鏄惁鍏佽鐢ㄦ埛鐧诲綍, 璁剧疆涓篳`False``锛屽彲浠ヤ笉鐢ㄥ垹闄ょ敤鎴锋潵绂佹 鐢ㄦ埛鐧诲綍 |
is_superuser | 甯冨皵鍊. 鐢ㄦ埛鏄惁鎷ユ湁鎵鏈夋潈闄愶紝鑰屾棤闇浠讳綍鏄惧紡鐨勬潈闄愬垎閰嶅畾涔 |
last_login | 鐢ㄦ埛鏈鍚庣櫥褰曠殑鏃堕棿锛岀己鐪佷細璁剧疆涓哄綋鍓嶆椂闂 |
date_joined | 鍒涘缓鐢ㄦ埛鐨勬椂闂达紝褰撶敤鎴峰垱寤烘椂锛岀己鐪佺殑璁剧疆涓哄綋鍓嶇殑鏃堕棿 |
Method | Description |
---|---|
is_authenticated() | Always returns True for real User objects. This is a way to tell if the user has been authenticated. This does not imply any permissions, and it doesnt check if the user is active. It only indicates that the user has sucessfully authenticated. |
is_anonymous() | Returns True only for AnonymousUser objects (and False for real User objects). Generally, you should prefer using is_authenticated() to this method. |
get_full_name() | Returns the first_name plus the last_name , with a space in between. |
set_password(passwd) | Sets the users password to the given raw string, taking care of the password hashing. This doesnt actually save the User object. |
check_password(passwd) | Returns True if the given raw string is the correct password for the user. This takes care of the password hashing in making the comparison. |
get_group_permissions() | Returns a list of permission strings that the user has through the groups he or she belongs to. |
get_all_permissions() | Returns a list of permission strings that the user has, both through group and user permissions. |
has_perm(perm) | Returns True if the user has the specified permission, where perm is in the format "package.codename" . If the user is inactive, this method will always return False . |
has_perms(perm_list) | Returns True if the user has all of the specified permissions. If the user is inactive, this method will always return False . |
has_module_perms(app_label) | Returns True if the user has any permissions in the given app_label . If the user is inactive, this method will always return False . |
get_and_delete_messages() | Returns a list of Message objects in the users queue and deletes the messages from the queue. |
email_user(subj, msg) | Sends an email to the user. This email is sent from the DEFAULT_FROM_EMAIL setting. You can also pass a third argument, from_email , to override the From address on the email. |
get_profile() | Returns a site-specific profile for this user. See the Profiles section for more on this method. |
鏂规硶 | 鎻忚堪 |
---|---|
is_authenticated() | 濡傛灉鏄湡姝g殑 User 瀵硅薄锛岃繑鍥炲兼亽涓 True 銆 鐢ㄤ簬妫鏌ョ敤鎴锋槸鍚﹀凡缁忛氳繃浜嗚璇併傞氳繃璁よ瘉骞朵笉鎰忓懗鐫 鐢ㄦ埛鎷ユ湁浠讳綍鏉冮檺锛岀敋鑷充篃涓嶆鏌ヨ鐢ㄦ埛鏄惁澶勪簬婵娲荤姸 鎬侊紝杩欏彧鏄〃鏄庣敤鎴锋垚鍔熺殑閫氳繃浜嗚璇併 |
is_anonymous() | 濡傛灉鏄釜 AnonymousUser 锛岃繑鍥炲间负 True 锛 濡傛灉鏄 User 瀵硅薄锛岃繑鍥炲间负 False 銆備竴鑸潵 璇达紝 is_authenticated() 浼氭瘮杩欎釜鏂规硶鏇村父鐢ㄤ簺銆 |
get_full_name() | 杩斿洖鍊间负锛 first_name 鍔犱笂 last_name 锛屼互 绌烘牸鍒嗛殧銆 |
set_password(passwd) | 灏嗙敤鎴风殑瀵嗙爜璁剧疆涓虹粰瀹氱殑瀛楃涓诧紝瀹為檯瀵嗙爜宸茶鍝堝笇 澶勭悊銆傝繖鏃跺苟涓嶄細鐪熸淇濆瓨 User 瀵硅薄銆 |
check_password(passwd) | 濡傛灉缁欏畾鐨勫瓧绗︿覆閫氳繃浜嗗瘑鐮佹鏌ワ紝杩斿洖 True 銆 瀵嗙爜姣旇緝宸茶繘琛屼簡鍝堝笇澶勭悊銆 |
get_group_permissions() | 杩斿洖鐢ㄦ埛閫氳繃鎵灞炵粍鑾峰緱鐨勬潈闄愬垪琛 |
get_all_permissions() | 杩斿洖鐢ㄦ埛閫氳繃鎵灞炵粍鍜岀敤鎴疯嚜韬潈闄愭墍鑾峰緱鐨勬墍鏈夋潈闄 鍒楄〃銆 |
has_perm(perm) | 濡傛灉鐢ㄦ埛鎷ユ湁缁欏畾鐨勬潈闄愶紝杩斿洖 True 锛 perm 搴斿舰濡 "package.codename" 鐨勬牸寮忋傚鏋滅敤鎴峰浜 闈炴縺娲荤姸鎬侊紝鍒欐绘槸杩斿洖 False 銆 |
has_perms(perm_list) | 濡傛灉鐢ㄦ埛鎷ユ湁鎵鏈夌粰瀹氱殑鏉冮檺锛岃繑鍥 True 銆 濡傛灉鐢ㄦ埛澶勪簬闈炴縺娲荤姸鎬侊紝鍒欐绘槸杩斿洖 False 銆 |
has_module_perms(app_label) | 濡傛灉鐢ㄦ埛鎷ユ湁浠讳綍缁欏畾 app_label 鐨勬潈闄愶紝杩斿洖 True 銆傚鏋滅敤鎴峰浜庨潪婵娲荤姸鎬侊紝鍒欐绘槸杩斿洖 False |
get_and_delete_messages() | 杩斿洖鐢ㄦ埛鐨 Message 瀵硅薄鍒楄〃锛屽苟浠庨槦鍒椾腑鍒犻櫎銆 |
email_user(subj, msg) | 缁欑敤鎴峰彂閫佺數瀛愰偖浠讹紝鐢 DEFAULT_FROM_EMAIL 鐨勮 缃綔涓哄彂浠朵汉銆備篃鍙互鐢ㄧ3涓弬鏁 from_email 鏉 瑕嗙洊璁剧疆銆 |
get_profile() | 杩斿洖鐢ㄦ埛鐨勭綉绔欒嚜瀹氫箟profile锛岃瑙丳rofile绔犺妭 |
Finally, User objects have two many-to-many fields: groups and permissions . User objects can access their related objects in the same way as any other many-to-many field:
鏈鍚庯紝 User 瀵硅薄鏈変袱涓瀵瑰鐨勫睘鎬э細 groups 鍜 permissions 銆 User 瀵硅薄鍙互 璞′娇鐢ㄥ叾浠栧瀵瑰灞炴х殑鏂规硶涓鏍蜂娇鐢ㄥ畠浠
# 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 provides built-in view functions for handling logging in and out (and a few other nifty tricks), but before we get to those, lets take a look at how to log users in and out by hand. Django provides two functions to perform these actions in django.contrib.auth : authenticate() and login() .
Django 鎻愪緵鍐呯疆鐨勮鍥(view)鍑芥暟鐢ㄤ簬澶勭悊鐧诲綍鍜岄鍑 (浠ュ強鍏朵粬濂囨妧娣阀)锛屼絾鍦ㄥ紑濮嬪墠锛屾垜浠潵鐪嬬湅濡備綍鎵嬪伐鐧诲綍鍜岄鍑猴紝Django 鍦 django.contrib.auth 涓彁渚涗簡涓や釜鍑芥暟鏉ュ鐞嗚繖浜涗簨鎯呪斺 authenticate() 鍜 login() 銆
To authenticate a given username and password, use authenticate() . It takes two keyword arguments, username and password , and it returns a User object if the password is valid for the given username. If the password is invalid, authenticate() returns None :
璁よ瘉缁欏嚭鐨勭敤鎴峰悕鍜屽瘑鐮侊紝浣跨敤 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 "Oops, that's wrong!"
authenticate() only verifies a users credentials. To log in a user, use login() . It takes an HttpRequest object and a User object and saves the users ID in the session, using Djangos session framework.
authenticate() 鍙槸楠岃瘉涓涓敤鎴风殑璇佷功鑰屽凡銆傝岃鐧诲綍涓涓敤鎴凤紝浣跨敤 login() 銆傝鍑芥暟鎺ュ彈涓涓 HttpRequest 瀵硅薄鍜屼竴涓 User 瀵硅薄浣滀负鍙傛暟骞朵娇鐢―jango鐨勪細璇濓紙 session 锛夋鏋舵妸鐢ㄦ埛鐨処D淇濆瓨鍦ㄨ浼氳瘽涓
This example shows how you might use both authenticate() and login() within a view function:
涓嬮潰鐨勪緥瀛愭紨绀轰簡濡備綍鍦ㄤ竴涓鍥句腑鍚屾椂浣跨敤 authenticate() 鍜 login() 鍑芥暟锛
from django.contrib import auth def login(request): username = request.POST['username'] password = request.POST['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/")
To log out a user, use django.contrib.auth.logout() within your view. It takes an HttpRequest object and has no return value:
娉ㄩ攢涓涓敤鎴凤紝鍦ㄤ綘鐨勮鍥句腑浣跨敤 django.contrib.auth.logout() 銆傝鍑芥暟鎺ュ彈涓涓 HttpRequest 瀵硅薄浣滀负鍙傛暟锛屾病鏈夎繑鍥炲笺
from django.contrib import auth def logout(request): auth.logout(request) # Redirect to a success page. return HttpResponseRedirect("/account/loggedout/")
Note that logout() doesnt throw any errors if the user wasnt logged in.
娉ㄦ剰锛屽嵆浣跨敤鎴锋病鏈夌櫥褰曪紝 logout() 涔熶笉浼氭姏鍑轰换浣曞紓甯搞
In practice, you usually will not need to write your own login/logout functions; the authentication system comes with a set of views for generically handling logging in and out.
鍦ㄥ疄闄呬腑锛屼綘涓鑸笉闇瑕佽嚜宸卞啓鐧诲綍/鐧诲嚭鐨勫嚱鏁帮紱璁よ瘉绯荤粺鎻愪緵浜嗕竴绯讳緥瑙嗗浘鐢ㄦ潵澶勭悊鐧诲綍鍜岀櫥鍑恒
The first step in using the authentication views is to wire them up in your URLconf. Youll need to add this snippet:
浣跨敤璁よ瘉瑙嗗浘鐨勭涓姝ユ槸鎶婂畠浠啓鍦ㄤ綘鐨刄RLconf涓 浣犻渶瑕佽繖鏍峰啓锛
from django.contrib.auth.views import login, logout urlpatterns = patterns('', # existing patterns here... (r'^accounts/login/$', login), (r'^accounts/logout/$', logout), )
/accounts/login/ and /accounts/logout/ are the default URLs that Django uses for these views.
/accounts/login/ 鍜 /accounts/logout/ 鏄疍jango鎻愪緵鐨勮鍥剧殑榛樿URL銆
By default, the login view renders a template at registration/login.html (you can change this template name by passing an extra view argument ,``template_name``). This form needs to contain a username and a password field. A simple template might look like this:
缂虹渷鎯呭喌涓嬶紝 login 瑙嗗浘娓叉煋 registragiton/login.html 妯℃澘(鍙互閫氳繃瑙嗗浘鐨勯澶栧弬鏁 template_name 淇敼杩欎釜妯℃澘鍚嶇О)銆傝繖涓〃鍗曞繀椤诲寘鍚 username 鍜 password 鍩熴傚涓嬬ず渚嬶細
{% 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 action='.' method='post'> {% endblock %}
If the user successfully logs in, he or she will be redirected to /accounts/profile/ by default. You can override this by providing a hidden field called next with the URL to redirect to after logging in. You can also pass this value as a GET parameter to the login view and it will be automatically added to the context as a variable called next that you can insert into that hidden field.
濡傛灉鐢ㄦ埛鐧诲綍鎴愬姛锛岀己鐪佷細閲嶅畾鍚戝埌 /accounts/profile 銆傝〃鍗曚腑鏈変竴涓猦idden瀛楁鍙 next 锛屽彲浠ョ敤鍦ㄧ櫥褰曞悗鎸囧畾url銆備篃鍙互鎶婅繖涓硷紙鎸囧畾鐨剈rl锛変綔涓 GET 鍙傛暟浼犻掔粰login瑙嗗浘锛岃繖涓弬鏁颁細鎴愪负Context涓悕涓 next 鐨勫彉閲忥紝浣犲彲浠ユ妸杩欎釜鍙橀噺璁剧疆缁欒〃鍗曚腑瀵瑰簲鐨勯殣鍚瓧娈点
The logout view works a little differently. By default it renders a template at registration/logged_out.html (which usually contains a Youve successfully logged out message). However, you can call the view with an extra argument, next_page , which will instruct the view to redirect after a logout.
logout瑙嗗浘鏈変竴浜涗笉鍚屻傜己鐪佺殑瀹冩覆鏌 registration/logged_out.html 妯℃澘锛堣繖涓鍥句竴鑸寘鍚綘宸茬粡鎴愬姛閫鍑虹殑淇℃伅锛夈傝鍥句腑杩樺彲浠ュ寘鍚竴涓弬鏁 next_page 鐢ㄤ簬閫鍑哄悗閲嶅畾鍚戙
Of course, the reason were going through all this trouble is so we can limit access to parts of our site.
鏈夊緢澶氬師鍥犻渶瑕佹帶鍒剁敤鎴疯闂珯鐐圭殑鏌愰儴鍒嗐
The simple, raw way to limit access to pages is to check request.user.is_authenticated() and redirect to a login page:
涓涓畝鍗曞師濮嬬殑闄愬埗鏂规硶鏄鏌 request.user.is_authenticated() ,鐒跺悗閲嶅畾鍚戝埌鐧婚檰椤甸潰锛
from django.http import HttpResponseRedirect def my_view(request): if not request.user.is_authenticated(): return HttpResponseRedirect('/login/?next=%s' % request.path) # ...
or perhaps display an error message:
鎴栬呮樉绀轰竴涓嚭閿欎俊鎭細
def my_view(request): if not request.user.is_authenticated(): return render_to_response('myapp/login_error.html') # ...
As a shortcut, you can use the convenient login_required decorator:
浣滀负涓涓揩鎹锋柟寮, 浣犲彲浠ヤ娇鐢ㄤ究鎹风殑 login_required 淇グ绗:
from django.contrib.auth.decorators import login_required @login_required def my_view(request): # ...
login_required does the following:
login_required 鍋氫笅闈㈢殑浜嬫儏:
If the user isnt logged in, redirect to /accounts/login/ , passing the current absolute URL in the query string as next , for example: /accounts/login/?next=/polls/3/ .
濡傛灉鐢ㄦ埛娌℃湁鐧诲綍, 閲嶅畾鍚戝埌 /accounts/login/ , 鎶婂綋鍓嶇粷瀵筓RL浣滀负 next 鍦ㄦ煡璇㈠瓧绗︿覆涓紶閫掕繃鍘, 渚嬪: /accounts/login/?next=/polls/3/ .
If the user is logged in, execute the view normally. The view code can then assume that the user is logged in.
濡傛灉鐢ㄦ埛宸茬粡鐧诲綍, 姝e父鍦版墽琛岃鍥惧嚱鏁. 瑙嗗浘浠g爜灏卞彲浠ュ亣瀹氱敤鎴峰凡缁忕櫥褰曚簡.
瀵归氳繃娴嬭瘯鐨勭敤鎴烽檺鍒惰闂
Limiting access based on certain permissions or some other test, or providing a different location for the login view works essentially the same way.
闄愬埗璁块棶鍙互鍩轰簬鏌愮鏉冮檺锛屾煇浜涙鏌ユ垨鑰呬负login瑙嗗浘鎻愪緵涓嶅悓鐨勪綅缃紝杩欎簺瀹炵幇鏂瑰紡澶ц嚧鐩稿悓
The raw way is to run your test on request.user in the view directly. For example, this view checks to make sure the user is logged in and has the permission polls.can_vote (more about how permissions works follows):
涓鑸殑鏂规硶鏄洿鎺ュ湪瑙嗗浘鐨 request.user 涓婅繍琛屾鏌ャ備緥濡傦紝涓嬮潰瑙嗗浘妫鏌ョ敤鎴风櫥闄嗗苟鏄惁鏈 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.")
Again, Django provides a shortcut called user_passes_test . It takes arguments and generates a specialized decorator for your particular situation:
骞朵笖Django鏈変竴涓О涓 user_passes_test 鐨勭畝娲佹柟寮忋傚畠鏍规嵁鎯呭喌浣跨敤鍙傛暟骞朵笖浜х敓鐗规畩瑁呴グ绗︺
def user_can_vote(user): return user.is_authenticated() and user.has_perm("polls.can_vote") @user_passes_text(user_can_vote, login_url="/login/") def vote(request): # Code here can assume a logged-in user with the correct permission. ...
user_passes_test takes one required argument: a callable that takes a User object and returns True if the user is allowed to view the page. Note that user_passes_test does not automatically check that the User is authenticated; you should do that yourself.
user_passes_test 浣跨敤涓涓繀闇鐨勫弬鏁帮細涓涓彲璋冪敤鐨勬柟娉曪紝褰撳瓨鍦 User 瀵硅薄骞跺綋姝ょ敤鎴峰厑璁告煡鐪嬭椤甸潰鏃惰繑鍥 True 銆 娉ㄦ剰 user_passes_test 涓嶄細鑷姩妫鏌 User 鏄惁璁よ瘉锛屼綘搴旇鑷繁鍋氳繖浠朵簨銆
In this example were also showing the second optional argument, login_url , which lets you specify the URL for your login page (/accounts/login/ by default).
渚嬪瓙涓垜浠篃灞曠ず浜嗙浜屼釜鍙夌殑鍙傛暟 login_url 锛屽畠璁╀綘鎸囧畾浣犵殑鐧诲綍椤甸潰鐨刄RL锛堥粯璁や负 /accounts/login/ 锛夈
Since its a relatively common task to check whether a user has a particular permission, Django provides a shortcut for that case: the permission_required() decorator. Using this decorator, the earlier example can be written as follows:
鏃㈢劧妫鏌ョ敤鎴锋槸鍚︽湁涓涓壒娈婃潈闄愭槸鐩稿甯歌鐨勪换鍔★紝Django涓鸿繖绉嶆儏褰㈡彁渚涗簡涓涓嵎寰勶細 permission_required() 瑁呴グ鍣 浣跨敤杩欎釜瑁呴グ鍣紝鍓嶉潰鐨勪緥瀛愬彲浠ヨ繖鏍峰啓:
from django.contrib.auth.decorators import permission_required @permission_required('polls.can_vote', login_url="/login/") def vote(request): # ...
Note that permission_required() also takes an optional login_url parameter, which also defaults to '/accounts/login/' .
娉ㄦ剰, permission_required() 涔熸湁涓涓彲閫夌殑 login_url 鍙傛暟, 杩欎釜鍙傛暟榛樿涓 '/accounts/login/' 銆
Limiting Access to Generic Views
闄愬埗閫氱敤瑙嗗浘鐨勮闂
One of the most frequently asked questions on the Django users list deals with limiting access to a generic view. To pull this off, youll need to write a thin wrapper around the view and point your URLconf to your wrapper instead of the generic view itself:
鍦―jango鐢ㄦ埛閭欢鍒楄〃涓棶鍒版渶澶氱殑闂鏄叧浜庡閫氱敤瑙嗗浘鐨勯檺鍒舵ц闂備负瀹炵幇杩欎釜鍔熻兘锛屼綘闇瑕佽嚜宸卞寘瑁呰鍥撅紝骞朵笖鍦║RLconf涓紝灏嗕綘鑷繁鐨勭増鏈浛鎹㈤氱敤瑙嗗浘锛
from dango.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)
You can, of course, replace login_required with any of the other limiting decorators.
褰撶劧, 浣犲彲浠ョ敤浠讳綍鍏朵粬闄愬畾淇グ绗︽潵鏇挎崲 login_required 銆
The easiest way by far to manage the auth system is through the admin interface. Chapter 6 discusses how to use Djangos admin interface to edit users and control their permissions and access, and most of the time youll just use that interface.
绠$悊璁よ瘉绯荤粺鏈绠鍗曠殑鏂规硶鏄氳繃绠$悊鐣岄潰銆 绗叚绔犺璁轰簡鎬庢牱浣跨敤Django鐨勭鐞嗙晫闈㈡潵缂栬緫鐢ㄦ埛鍜屾帶鍒朵粬浠殑鏉冮檺鍜屽彲璁块棶鎬э紝骞朵笖澶у鏁版椂闂翠綘閮戒細鍙娇鐢ㄨ繖涓晫闈€
However, there are low-level APIs you can delve into when you need absolute control, and we discuss these in the sections that follow.
鐒惰岋紝褰撲綘闇瑕佺粷瀵圭殑鎺у埗鏉冪殑鏃跺欙紝鏈変竴浜涗綆灞 API 闇瑕佹繁鍏ヤ笓鐮旓紝鎴戜滑灏嗗湪涓嬮潰鐨勭珷鑺備腑璁ㄨ瀹冧滑銆
Create users with the create_user helper function:
浣跨敤 create_user 杈呭姪鍑芥暟鍒涘缓鐢ㄦ埛:
>>> from django.contrib.auth.models import User >>> user = User.objects.create_user(username='john', ... email='jlennon@beatles.com', ... password='glass onion')
At this point, user is a User instance ready to be saved to the database (create_user() doesnt actually call save() itself). You can continue to change its attributes before saving, too:
鍦ㄨ繖閲岋紝 user 鏄 User 绫荤殑涓涓疄渚嬶紝鍑嗗鐢ㄤ簬鍚戞暟鎹簱涓瓨鍌ㄦ暟鎹 create_user() 鍑芥暟骞舵病鏈夊湪鏁版嵁搴撲腑鍒涘缓璁板綍锛屽湪淇濆瓨鏁版嵁涔嬪墠锛屼綘浠嶇劧鍙互缁х画淇敼瀹冪殑灞炴у笺
>>> user.is_staff = True >>> user.save()
You can change a password with set_password() :
浣犲彲浠ヤ娇鐢 set_password() 鏉ヤ慨鏀瑰瘑鐮侊細
>>> user = User.objects.get(username='john') >>> user.set_password('goo goo goo joob') >>> user.save()
Dont set the password attribute directly unless you know what youre doing. The password is actually stored as a salted hash and thus cant be edited directly.
闄ら潪浣犳竻妤氱殑鐭ラ亾鑷繁鍦ㄥ仛浠涔堬紝鍚﹀垯涓嶈鐩存帴淇敼 password 灞炴с傚叾涓繚瀛樼殑鏄瘑鐮佺殑 鍔犲叆salt鐨刪ash鍊 锛屾墍浠ヤ笉鑳界洿鎺ョ紪杈戙
More formally, the password attribute of a User object is a string in this format:
涓鑸潵璇达紝 User 瀵硅薄鐨 password 灞炴ф槸涓涓瓧绗︿覆锛屾牸寮忓涓嬶細
hashtype$salt$hash
Thats a hash type, the salt, and the hash itself, separated by the dollar sign ($) character.
杩欐槸鍝堝笇绫诲瀷锛宻alt鍜屽搱甯屾湰韬紝鐢ㄧ編鍏冪鍙凤紙$锛夊垎闅斻
hashtype is either sha1 (default) or md5 , the algorithm used to perform a one-way hash of the password. salt is a random string used to salt the raw password to create the hash, for example:
hashtype 鏄 sha1 锛堥粯璁わ級鎴栬 md5 锛屽畠鏄敤鏉ュ鐞嗗崟鍚戝瘑鐮佸搱甯岀殑绠楁硶锛孲alt鏄竴涓敤鏉ュ姞瀵嗗師濮嬪瘑鐮佹潵鍒涘缓鍝堝笇鐨勯殢鏈哄瓧绗︿覆锛屼緥濡:
sha1$a1976$a36cc8cbf81742a8fb52e221aaeab48ed7f58ab4
The User.set_password() and User.check_password() functions handle the setting and checking of these values behind the scenes.
User.set_password() 鍜 User.check_password() 鍑芥暟鍦ㄥ悗鍙板鐞嗗拰妫鏌ヨ繖浜涘笺
Is a Salted Hash Some Kind of Drug?
涓涓姞鍏alt鐨勫搱甯岀畻娉曟槸鏌愮姣掑搧鍚楋紵
No, a salted hash has nothing to do with marijuana; its actually a common way to securely store passwords. A hash is a one-way cryptographic functionthat is, you can easily compute the hash of a given value, but its nearly impossible to take a hash and reconstruct the original value.
涓嶆槸锛屼竴涓 鍔犲叆salt鍊肩殑鍝堝笇绠楁硶 鍙笉鏄粈涔堟瘨鍝侊紱浜嬪疄涓婂畠鏄竴绉嶇‘淇濆瘑鐮佸瓨鍌ㄥ畨鍏ㄧ殑甯哥敤鏂规硶銆備竴娆 鍝堝笇 鏄竴娆″崟鍚戠殑鍔犲瘑杩囩▼锛屼綘鑳藉鏄撳湴璁$畻鍑轰竴涓粰瀹氬肩殑鍝堝笇鐮侊紝浣嗘槸鍑犱箮涓嶅彲鑳戒粠涓涓搱甯岀爜瑙e嚭瀹冪殑鍘熷笺
If we stored passwords as plain text, anyone who got their hands on the password database would instantly know everyones password. Storing passwords as hashes reduces the value of a compromised database.
濡傛灉鎴戜滑浠ユ櫘閫氭枃鏈瓨鍌ㄥ瘑鐮,浠讳綍鑳借繘鍏ユ暟鎹簱鐨勪汉閮借兘杞绘槗鐨勮幏鍙栨瘡涓汉鐨勫瘑鐮併備娇鐢ㄥ搱甯屾柟寮忔潵瀛樺偍瀵嗙爜鐩稿簲鐨勫噺灏戜簡鏁版嵁搴撴硠闇插瘑鐮佺殑鍙兘銆
However, an attacker with the password database could still run a brute- force attack, hashing millions of passwords and comparing those hashes against the stored values. This takes some time, but less than you might thinkcomputers are incredibly fast.
鐒惰岋紝鏀诲嚮鑰呬粛鐒跺彲浠ヤ娇鐢 鏆村姏鐮磋В 浣跨敤涓婄櫨涓囦釜瀵嗙爜涓庡瓨鍌ㄧ殑鍊煎姣旀潵鑾峰彇鏁版嵁搴撳瘑鐮侊紝杩欓渶瑕佽姳涓浜涙椂闂达紝浣嗘槸鏅鸿兘鐢佃剳鎯婁汉鐨勯熷害瓒呭嚭浜嗕綘鐨勬兂璞
Worse, there are publicly available rainbow tables , or databases of precomputed hashes of millions of passwords. With a rainbow table, an attacker can break most passwords in seconds.
鏇寸碂绯曠殑鏄垜浠彲浠ュ叕寮鍦板緱鍒 rainbow tables 锛堜竴绉嶆毚鍔涘瘑鐮佺牬瑙h〃锛夋垨棰勫鏈変笂鐧句竾鍝堝笇瀵嗙爜鍊肩殑鏁版嵁搴撱備娇鐢╮ainbow tables鍙互鍦ㄥ嚑绉掍箣鍐呭氨鑳芥悶瀹氭渶澶嶆潅鐨勪竴涓瘑鐮併
Adding a salt basically an initial random valueto the stored hash adds another layer of difficulty to breaking passwords. Since salts differ from password to password, they also prevent the use of a rainbow table, thus forcing attackers to fall back on a brute-force attack, itself made more difficult by the extra entropy added to the hash by the salt.
鍦ㄥ瓨鍌ㄧ殑hash鍊肩殑鍩虹涓婏紝鍔犲叆 salt 鍊硷紙涓涓殢鏈哄硷級锛屽鍔犱簡瀵嗙爜鐨勫己搴︼紝浣垮緱鐮磋В鏇村姞鍥伴毦銆傚洜涓烘瘡涓瘑鐮佺殑salt鍊奸兘涓嶇浉鍚岋紝杩欎篃闄愬埗浜唕ainbow table鐨勪娇鐢紝浣垮緱鏀诲嚮鑰呭彧鑳戒娇鐢ㄦ渶鍘熷鐨勬毚鍔涚牬瑙f柟娉曘傝屽姞鍏ョ殑salt鍊间娇寰梙ash鐨勭喌杩涗竴姝ヨ幏寰楀鍔狅紝浣垮緱鏆村姏鐮磋В鐨勯毦搴﹀張杩涗竴姝ュ姞澶с
While salted hashes arent absolutely the most secure way of storing passwords, theyre a good middle ground between security and convenience.
鍔犲叆salt鍊煎緱hash骞朵笉鏄粷瀵瑰畨鍏ㄧ殑瀛樺偍瀵嗙爜鐨勬柟娉曪紝鐒惰屽湪瀹夊叏鍜屾柟渚夸箣闂存湁寰堝ぇ鐨勪腑闂村湴甯﹂渶瑕佹垜浠潵鍋氬喅瀹氥
澶勭悊娉ㄥ唽
We can use these low-level tools to create views that allow users to sign up. Nearly every developer wants to implement registration differently, so Django leaves writing a registration view up to you. Luckily, its pretty easy.
鎴戜滑鍙互浣跨敤杩欎簺搴曞眰宸ュ叿鏉ュ垱寤哄厑璁哥敤鎴锋敞鍐岀殑瑙嗗浘銆傛渶杩戞瘡涓紑鍙戜汉鍛橀兘甯屾湜瀹炵幇鍚勮嚜涓嶅悓鐨勬敞鍐屾柟娉曪紝鎵浠jango鎶婂啓涓涓敞鍐岃鍥剧殑宸ヤ綔鐣欑粰浜嗕綘銆傚垢杩愮殑鏄紝杩欏緢瀹规槗銆
At its simplest, we could provide a small view that prompts for the required user information and creates those users. Django provides a built-in form you can use for this purpose, which well use in this example:
浣滀负杩欎釜浜嬫儏鐨勬渶绠鍖栧鐞, 鎴戜滑鍙互鎻愪緵涓涓皬瑙嗗浘, 鎻愮ず涓浜涘繀椤荤殑鐢ㄦ埛淇℃伅骞跺垱寤鸿繖浜涚敤鎴. Django涓烘鎻愪緵浜嗗彲鐢ㄧ殑鍐呯疆琛ㄥ崟, 鍦ㄤ笅闈㈣繖涓緥瀛愪腑寰堝ソ鍦颁娇鐢ㄤ簡:
from django import oldforms as forms from django.http import HttpResponseRedirect from django.shortcuts import render_to_response from django.contrib.auth.forms import UserCreationForm def register(request): form = UserCreationForm() if request.method == 'POST': data = request.POST.copy() errors = form.get_validation_errors(data) if not errors: new_user = form.save(data) return HttpResponseRedirect("/books/") else: data, errors = {}, {} return render_to_response("registration/register.html", { 'form' : forms.FormWrapper(form, data, errors) })
This form assumes a template named registration/register.html . Heres an example of what that template might look like:
杩欎釜琛ㄥ崟鏋勬兂浜嗕竴涓彨 registration/register.html 鐨勬ā鏉. 杩欓噷鏄竴涓繖涓ā鏉跨殑鍙兘鐨勬牱瀛愮殑渚嬪瓙:
{% extends "base.html" %} {% block title %}Create an account{% endblock %} {% block content %} <h1>Create an account</h1> <form action="." method="post"> {% if form.error_dict %} <p class="error">Please correct the errors below.</p> {% endif %} {% if form.username.errors %} {{ form.username.html_error_list }} {% endif %} <label for="id_username">Username:</label> {{ form.username }} {% if form.password1.errors %} {{ form.password1.html_error_list }} {% endif %} <label for="id_password1">Password: {{ form.password1 }} {% if form.password2.errors %} {{ form.password2.html_error_list }} {% endif %} <label for="id_password2">Password (again): {{ form.password2 }} <input type="submit" value="Create the account" /> </label> {% endblock %}
Note
澶囨敞
django.contrib.auth.forms.UserCreationForm is, at the time of publication, an oldforms Form. See http://www.djangoproject.com/documentation/0.96/forms/ for details on oldforms. The transition to newforms, as covered in Chapter 7, will be completed in the near future.
鍦ㄦ湰涔﹀嚭鐗堜箣鏃, django.contrib.auth.forms.UserCreationForm 鏄竴涓 oldforms 琛ㄥ崟. 鍙傜湅 http://www.djangoproject.com/documentation/0.96/forms/ 鍙互鑾峰彇鏈夊叧 oldforms 鐨勮缁嗕俊鎭. 杞崲鍒版湁鍏 newforms 鐨勫唴瀹瑰湪绗7绔犱腑灏嗕細璁茶堪, newforms 鍔熻兘 灏嗕細鍦ㄤ笉杩滅殑灏嗘潵瀹屾垚.
The current logged-in user and his or her permissions are made available in the template context when you use RequestContext (see Chapter 10).
褰撳墠鐧诲叆鐨勭敤鎴蜂互鍙婁粬锛堝ス锛夌殑鏉冮檺鍙互閫氳繃 RequestContext 鍦ㄦā鏉跨殑context涓娇鐢紙璇﹁绗10绔狅級銆
Note
澶囨敞
Technically, these variables are only made available in the template context if you use RequestContext and your TEMPLATE_CONTEXT_PROCESSORS setting contains "django.core.context_processors.auth" , which is the default. Again, see Chapter 10 for more information.
浠庢妧鏈笂鏉ヨ锛屽彧鏈夊綋浣犱娇鐢ㄤ簡 RequestContext 骞朵笖 TEMPLATE_CONTEXT_PROCESSORS 璁剧疆鍖呭惈浜 "django.core.context_processors.auth" 锛堥粯璁ゆ儏鍐靛氨鏄姝わ級鏃讹紝杩欎簺鍙橀噺鎵嶈兘鍦ㄦā鏉縞ontext涓娇鐢ㄣ傛洿璇︾粏鐨勫唴瀹癸紝涔熻鍙傝冪10绔犮
When using RequestContext , the current user (either a User instance or an AnonymousUser instance) is stored in the template variable {{ user }} :
褰撲娇鐢 RequestContext 鏃, 褰撳墠鐢ㄦ埛 (鏄竴涓 User 瀹炰緥鎴栦竴涓 AnonymousUser 瀹炰緥) 瀛樺偍鍦ㄦā鏉垮彉閲 {{ user }} 涓:
{% if user.is_authenticated %} <p>Welcome, {{ user.username }}. Thanks for logging in.</p> {% else %} <p>Welcome, new user. Please log in.</p> {% endif %}
This users permissions are stored in the template variable {{ perms }} . This is a template-friendly proxy to a couple of permission methods described shortly.
杩欎簺鐢ㄦ埛鐨勬潈闄愪俊鎭瓨鍌ㄥ湪 {{ perms }} 妯℃澘鍙橀噺涓傝繖鏄竴涓湪妯℃澘涓娇鐢ㄥ緢鏂逛究鐨勪唬鐞嗭紝鍏朵腑鍖呭惈涓浜涙潈闄愮浉鍏冲嚱鏁扮殑绠鍐欍
There are two ways you can use this perms object. You can use something like {{ perms.polls }} to check if the user has any permissions for some given application, or you can use something like {{ perms.polls.can_vote }} to check if the user has a specific permission.
浣犳湁涓ょ鏂瑰紡鏉ヤ娇鐢 perms 瀵硅薄銆備綘鍙互浣跨敤绫讳技浜 {{ perms.polls }} 鐨勫舰寮忔潵妫鏌ワ紝瀵逛簬鏌愪釜鐗瑰畾鐨勫簲鐢紝涓涓敤鎴锋槸鍚﹀叿鏈 浠绘剰 鏉冮檺锛涗綘涔熷彲浠ヤ娇鐢 {{ perms.polls.can_vote }} 杩欐牱鐨勫舰寮忥紝鏉ユ鏌ヤ竴涓敤鎴锋槸鍚︽嫢鏈夌壒瀹氱殑鏉冮檺銆
Thus, you can check permissions in template {% if %} statements:
杩欐牱浣犲氨鍙互鍦ㄦā鏉夸腑鐨 {% 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 %}
There are a few other bits of the authentication framework that weve only dealt with in passing. Well take a closer look at them in the following sections.
鍦ㄨ璇佹鏋朵腑杩樻湁鍏朵粬鐨勪竴浜涘姛鑳姐傛垜浠細鍦ㄦ帴涓嬫潵鐨勫嚑涓儴鍒嗕腑杩涗竴姝ュ湴浜嗚В瀹冧滑銆
Permissions are a simple way to mark users and groups as being able to perform some action. They are usually used by the Django admin site, but you can easily use them in your own code.
鏉冮檺鍙互寰堟柟渚垮湴鏍囪瘑鐢ㄦ埛鍜岀敤鎴风粍鍙互鎵ц鐨勬搷浣溿傚畠浠Django鐨刟dmin绠$悊绔欑偣鎵浣跨敤锛屼綘涔熷彲浠ュ湪浣犺嚜宸辩殑浠g爜涓娇鐢ㄥ畠浠
The Django admin site uses permissions as follows:
Django鐨刟dmin绔欑偣濡備笅浣跨敤鏉冮檺锛
Access to view the add form, and add an object is limited to users with the add permission for that type of object.
鍙湁璁剧疆浜 add 鏉冮檺鐨勭敤鎴锋墠鑳戒娇鐢ㄦ坊鍔犺〃鍗曪紝娣诲姞瀵硅薄鐨勮鍥俱
Access to view the change list, view the change form, and change an object is limited to users with the change permission for that type of object.
鍙湁璁剧疆浜 change 鏉冮檺鐨勭敤鎴锋墠鑳戒娇鐢ㄥ彉鏇村垪琛紝鍙樻洿琛ㄦ牸锛屽彉鏇村璞$殑瑙嗗浘銆
Access to delete an object is limited to users with the delete permission for that type of object.
鍙湁璁剧疆浜 delete 鏉冮檺鐨勭敤鎴锋墠鑳藉垹闄や竴涓璞°
Permissions are set globally per type of object, not per specific object instance. For example, its possible to say Mary may change news stories, but its not currently possible to say Mary may change news stories, but only the ones she created herself or Mary may only change news stories that have a certain status, publication date, or ID.
鏉冮檺鏄牴鎹瘡涓涓被鍨嬬殑瀵硅薄鑰岃缃殑锛屽苟涓嶅叿浣撳埌瀵硅薄鐨勭壒瀹氬疄渚嬨備緥濡傦紝鎴戜滑鍙互鍏佽Mary鏀瑰彉鏂版晠浜嬶紝浣嗘槸鐩墠杩樹笉鍏佽璁剧疆Mary鍙兘鏀瑰彉鑷繁鍒涘缓鐨勬柊鏁呬簨锛屾垨鑰呮牴鎹粰瀹氱殑鐘舵侊紝鍑虹増鏃ユ湡鎴栬匢D鍙锋潵閫夋嫨鏉冮檺銆
These three basic permissionsadd, change, and deleteare automatically created for each Django model that has a class Admin . Behind the scenes, these permissions are added to the auth_permission database table when you run manage.py syncdb .
杩欎笁涓熀鏈潈闄愶細娣诲姞锛屽彉鏇村拰鍒犻櫎锛屼細琚嚜鍔ㄦ坊鍔犲埌鎵鏈夌殑Django妯″瀷涓紝鍙璇ユā鍨嬪寘鍚 class Admin 銆傚綋浣犳墽琛 manage.py syncdb 鐨勬椂鍊欙紝杩欎簺灏辫鑷姩娣诲姞鍒 auth_permission 鏁版嵁琛ㄤ腑銆
These permissions will be of the form "<app>.<action>_<object_name>" . That is, if you have a polls application with a Choice model, youll get permissions named "polls.add_choice" , "polls.change_choice" , and "polls.delete_choice" .
鏉冮檺浠 "<app>.<action>_<object_name>" 鐨勫舰寮忓嚭鐜般傚鏋滀綘鏈変竴涓 polls 鐨勫簲鐢紝鍖呭惈涓涓 Choice 妯″瀷锛屼綘灏辨湁浠ヤ笅涓変釜鏉冮檺锛屽垎鍒彨鍋 "polls.add_choice" 锛 "polls.change_choice" 锛屽拰 "polls.delete_choice" 銆
Note that if your model doesnt have class Admin set when you run syncdb , the permissions wont be created. If you initialize your database and add class Admin to models after the fact, youll need to run syncdb again to create any missing permissions for your installed applications.
娉ㄦ剰锛屽鏋滃綋浣犺繍琛 syncdb 鏃讹紝妯″瀷涓病鏈夊寘鍚 class Admin 锛岃妯″瀷瀵瑰簲鐨勬潈闄愬氨涓嶄細琚垱寤恒傚鏋滀綘鍦ㄥ垵濮嬪寲鏁版嵁搴撲互鍚庯紝鍙堝湪鑷繁鐨勬ā鍨嬩腑鍔犲叆浜 class Admin 锛屼綘灏遍渶瑕侀噸鏂拌繍琛 syncdb 鏉ヤ负搴旂敤鍔犲叆鏉冮檺銆
You can also create custom permissions for a given model object using the permissions attribute on Meta . This example model creates three custom permissions:
浣犱篃鍙互閫氳繃璁剧疆 Meta 涓殑 permissions 灞炴э紝鏉ヤ负缁欏畾鐨勬ā鍨嬪畾鍒舵潈闄愩備笅闈㈢殑渚嬪瓙鍒涘缓浜嗕笁涓嚜瀹氫箟鐨勬潈闄愶細
class USCitizen(models.Model): # ... class Meta: permissions = ( # Permission identifier human-readable permission name ("can_drive", "Can drive"), ("can_vote", "Can vote in elections"), ("can_drink", "Can drink alcohol"), )
This only creates those extra permissions when you run syncdb ; its up to you to check for these permissions in your views.
褰撲綘杩愯 syncdb 鏃讹紝棰濆鐨勬潈闄愭墠浼氳鍔犲叆锛涗綘闇瑕佽嚜宸卞湪瑙嗗浘涓坊鍔犳潈闄愮浉鍏崇殑浠g爜銆
Just like users, permissions are implemented in a Django model that lives in django.contrib.auth.models . This means that you can use Djangos database API to interact directly with permissions if you like.
灏辫窡鐢ㄦ埛涓鏍凤紝鏉冮檺涔熷氨鏄疍jango妯″瀷涓殑 django.contrib.auth.models 銆傚洜姝ゅ鏋滀綘鎰挎剰锛屼綘涔熷彲浠ラ氳繃Django鐨勬暟鎹簱API鐩存帴鎿嶄綔鏉冮檺銆
Groups are a generic way of categorizing users so you can apply permissions, or some other label, to those users. A user can belong to any number of groups.
缁勬彁渚涗簡涓绉嶉氱敤鐨勬柟寮忔潵璁╀綘鎸夌収涓瀹氱殑鏉冮檺瑙勫垯鍜屽叾浠栨爣绛惧皢鐢ㄦ埛鍒嗙被銆備竴涓敤鎴峰彲浠ラ毝灞炰簬浠讳綍鏁伴噺鐨勭粍銆
A user in a group automatically has the permissions granted to that group. For example, if the group Site editors has the permission can_edit_home_page , any user in that group will have that permission.
鍦ㄤ竴涓粍涓殑鐢ㄦ埛鑷姩鑾峰緱浜嗚祴浜堣缁勭殑鏉冮檺銆備緥濡傦紝 Site editors 缁勬嫢鏈 can_edit_home_page 鏉冮檺锛屼换浣曞湪璇ョ粍涓殑鐢ㄦ埛閮芥嫢鏈夎繖涓潈闄愩
Groups are also a convenient way to categorize users to give them some label, or extended functionality. For example, you could create a group 'Special users' , and you could write code that could, say, give those users access to a members-only portion of your site, or send them members-only email messages.
缁勪篃鍙互閫氳繃缁欏畾涓浜涚敤鎴风壒娈婄殑鏍囪锛屾潵鎵╁睍鍔熻兘銆備緥濡傦紝浣犲垱寤轰簡涓涓 'Special users' 缁勶紝骞朵笖鍏佽缁勪腑鐨勭敤鎴疯闂珯鐐圭殑涓浜沄IP閮ㄥ垎锛屾垨鑰呭彂閫乂IP鐨勯偖浠舵秷鎭
Like users, the easiest way to manage groups is through the admin interface. However, groups are also just Django models that live in django.contrib.auth.models , so once again you can always use Djangos database APIs to deal with groups at a low level.
鍜岀敤鎴风鐞嗕竴鏍凤紝admin鎺ュ彛鏄鐞嗙粍鐨勬渶绠鍗曠殑鏂规硶銆傜劧鑰岋紝缁勪篃灏辨槸Django妯″瀷 django.contrib.auth.models 锛屽洜姝や綘鍙互浣跨敤Django鐨勬暟鎹簱API锛屽湪搴曞眰璁块棶杩欎簺缁勩
The message system is a lightweight way to queue messages for given users. A message is associated with a User . Theres no concept of expiration or timestamps.
娑堟伅绯荤粺浼氫负缁欏畾鐨勭敤鎴锋帴鏀舵秷鎭傛瘡涓秷鎭兘鍜屼竴涓 User 鐩稿叧鑱斻傚叾涓病鏈夎秴鏃舵垨鑰呮椂闂存埑鐨勬蹇点
Messages are used by the Django admin interface after successful actions. For example, when you create an object, youll notice a The object was created successfully message at the top of the admin page.
鍦ㄦ瘡涓垚鍔熺殑鎿嶄綔浠ュ悗锛孌jango鐨刟dmin绠$悊鎺ュ彛灏变細浣跨敤娑堟伅鏈哄埗銆備緥濡傦紝褰撲綘鍒涘缓浜嗕竴涓璞★紝浣犱細鍦╝dmin椤甸潰鐨勯《涓婄湅鍒 The object was created successfully 鐨勬秷鎭
You can use the same API to queue and display messages in your own application. The API is simple:
浣犱篃鍙互浣跨敤鐩稿悓鐨凙PI鍦ㄤ綘鑷繁鐨勫簲鐢ㄤ腑鎺掗槦鎺ユ敹鍜屾樉绀烘秷鎭侫PI闈炲父鍦扮畝鍗曪細
To create a new message, use user.message_set.create(message='message_text') .
瑕佸垱寤轰竴鏉℃柊鐨勬秷鎭紝浣跨敤 user.message_set.create(message='message_text') 銆
To retrieve/delete messages, use user.get_and_delete_messages() , which returns a list of Message objects in the users queue (if any) and deletes the messages from the queue.
瑕佽幏寰/鍒犻櫎娑堟伅锛屼娇鐢 user.get_and_delete_messages() 锛岃繖浼氳繑鍥炰竴涓 Message 瀵硅薄鐨勫垪琛紝骞朵笖浠庨槦鍒椾腑鍒犻櫎杩斿洖鐨勯」銆
In this example view, the system saves a message for the user after creating a playlist:
鍦ㄤ緥瀛愯鍥句腑锛岀郴缁熷湪鍒涘缓浜嗘挱鏀惧崟锛坧laylist锛変互鍚庯紝涓虹敤鎴蜂繚瀛樹簡涓鏉℃秷鎭
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))
When you use RequestContext , the current logged-in user and his or her messages are made available in the template context as the template variable {{ messages }} . Heres an example of template code that displays messages:
褰撲娇鐢 RequestContext 锛屽綋鍓嶇櫥褰曠殑鐢ㄦ埛浠ュ強浠栵紙濂癸級鐨勬秷鎭紝灏变細浠ユā鏉垮彉閲 {{ messages }} 鍑虹幇鍦ㄦā鏉跨殑context涓備笅闈㈡槸鏄剧ず娑堟伅鐨勪竴涓緥瀛愭ā鏉夸唬鐮侊細
{% if messages %} <ul> {% for message in messages %} <li>{{ message }}</li> {% endfor %} </ul> {% endif %}
Note that RequestContext calls get_and_delete_messages behind the scenes, so any messages will be deleted even if you dont display them.
闇瑕佹敞鎰忕殑鏄 RequestContext 浼氬湪鍚庡彴璋冪敤 get_and_delete_messages 锛屽洜姝ゅ嵆浣夸綘娌℃湁鏄剧ず瀹冧滑锛屽畠浠篃浼氳鍒犻櫎鎺夈
Finally, note that this messages framework only works with users in the user database. To send messages to anonymous users, use the session framework directly.
鏈鍚庢敞鎰忥紝杩欎釜娑堟伅妗嗘灦鍙兘鏈嶅姟浜庡湪鐢ㄦ埛鏁版嵁搴撲腑瀛樺湪鐨勭敤鎴枫傚鏋滆鍚戝尶鍚嶇敤鎴峰彂閫佹秷鎭紝璇风洿鎺ヤ娇鐢ㄤ細璇濇鏋躲
The final piece of the puzzle is the profile system. To understand what profiles are all about, lets first look at the problem.
鏈鍚庝竴涓毦棰樻槸妗f绯荤粺.涓轰簡鐞嗚В浠涔堟槸妗f,璁╂垜浠厛鐪嬬湅闂.
In a nutshell, many sites need to store more user information than is available on the standard User object. To compound the problem, most sites will have different extra fields. Thus, Django provides a lightweight way of defining a profile object thats linked to a given user. This profile object can differ from project to project, and it can even handle different profiles for different sites served from the same database.
绠鍗曟潵璇达紝璁稿缃戠珯闇瑕佸瓨鍌ㄦ瘮鏍囧噯 User 瀵硅薄鏇村鐨勭敤鎴蜂俊鎭備负浜嗚В鍐宠繖涓棶棰橈紝澶у鏁扮綉绔欓兘浼氭湁涓嶅悓鐨勯澶栧瓧娈点傛墍浠ワ紝Django鎻愪緵涓涓交閲忕骇鐨勬柟寮忓畾涔夋。妗堝璞¢摼鎺ュ埌鎸囧畾鐨勭敤鎴枫傝繖涓。妗堝璞″湪姣忎釜椤圭洰涓彲浠ユ槸涓嶅悓鐨勶紝鐢氳嚦鍙互涓哄悓涓鏁版嵁搴撴湇鍔$殑涓嶅悓鐨勭珯鐐瑰鐞嗕笉鍚岀殑妗f銆
The first step in creating a profile is to define a model that holds the profile information. The only requirement Django places on this model is that it have a unique ForeignKey to the User model; this field must be named user . Other that that, you can use any other fields you like. Heres a strictly arbitrary profile model:
鍒涘缓妗f鐨勭涓姝ユ槸瀹氫箟涓涓ā鍨嬶紙model锛夋潵瀛樺偍妗f淇℃伅銆侱jango瀵硅繖涓ā鍨嬫墍鍋氱殑鍞竴鐨勯檺鍒舵槸锛屽繀椤昏鍖呭惈鍞竴鐨勪竴涓 User 妯″瀷鐨 ForeignKey 锛岃屼笖杩欎釜瀛楁蹇呴』瑕佸彨鍋 user 銆傚叾浠栫殑瀛楁鍙互鐢变綘鑷繁鎺屾帶銆備笅闈㈡槸涓涓。妗堟ā鍨嬬殑渚嬪瓙锛
from django.db import models from django.contrib.auth.models import User class MySiteProfile(models.Model): # This is the only required field user = models.ForeignKey(User, unique=True) # The rest is completely up to you... favorite_band = models.CharField(maxlength=100, blank=True) favorite_cheese = models.CharField(maxlength=100, blank=True) lucky_number = models.IntegerField()
Next, youll need to tell Django where to look for this profile object. You do that by setting the AUTH_PROFILE_MODULE setting to the identifier for your model. So, if your model lives in an application called myapp , youd put this in your settings file:
涓嬩竴姝ワ紝浣犻渶瑕佸憡璇塂jango鍘诲摢閲屾煡鎵炬。妗堝璞°備綘鍙互閫氳繃璁剧疆妯″瀷涓殑 AUTH_PROFILE_MODULE 鍙橀噺杈惧埌杩欎釜鐩殑銆傚洜姝わ紝濡傛灉浣犵殑妯″瀷鍖呭惈鍦 myapp 杩欎釜搴旂敤涓紝浣犲氨闇瑕佸涓嬬紪鍐欎綘鐨勮缃枃浠讹細
AUTH_PROFILE_MODULE = "myapp.mysiteprofile"
Once thats done, you can access a users profile by calling user.get_profile() . This function could raise a SiteProfileNotAvailable exception if AUTH_PROFILE_MODULE isnt defined, or it could raise a DoesNotExist exception if the user doesnt have a profile already (youll usually catch that exception and create a new profile at that time).
涓鏃﹀畬鎴愶紝浣犲氨鍙互閫氳繃璋冪敤 user.get_profile() 鍑芥暟鏉ヨ幏寰楃敤鎴锋。妗堛傚鏋 AUTH_PROFILE_MODULE 鍙橀噺娌℃湁璁剧疆锛岃繖涓嚱鏁板彲鑳戒細鎶涘嚭 SiteProfileNotAvailable 寮傚父锛涘鏋滆繖涓敤鎴蜂笉瀛樺湪妗f锛屼篃鍙兘浼氭姏鍑 DoesNotExist 寮傚父锛堥氬父鎯呭喌涓嬶紝浣犱細鎹曡幏杩欎釜寮傚父骞跺湪褰撴椂鍒涘缓涓涓柊鐨勬。妗堬級銆
Yes, the session and authorization system is a lot to absorb. Most of the time you wont need all the features described in this chapter, but when you need to allow complex interactions between users, its good to have all that power available.
鏄殑锛屼細璇濆拰璁よ瘉绯荤粺鏈夊お澶氱殑涓滆タ瑕佸銆傚ぇ澶氭暟鎯呭喌涓嬶紝浣犲苟涓嶉渶瑕佹湰绔犳墍鎻愬埌鐨勬墍鏈夊姛鑳姐傜劧鑰屽綋浣犻渶瑕佸厑璁哥敤鎴蜂箣闂村鏉傜殑浜掓搷浣滄椂锛屾墍鏈夌殑鍔熻兘閮借兘浣跨敤灏辨樉寰楀緢閲嶈浜嗐
In the next chapter, well take a look at a piece of Django that builds on top of this session/user system: the comments application. It allows you to easily attach commentsfrom anonymous or authenticated usersto arbitrary objects. Onward and upward!
鍦ㄤ笅涓绔犺妭涓紝鎴戜滑浼氭潵娣卞叆浜嗚ВDjango寤虹珛鍦ㄤ細璇/鐢ㄦ埛绯荤粺涔嬩笂鐨勪竴涓郴缁燂細璇勮搴旂敤銆傚畠鍏佽浣犲緢鏂逛究鍦颁互鍖垮悕鐢ㄦ埛鎴栬呮敞鍐岀敤鎴风殑韬唤锛屽悜浠绘剰绫诲瀷鐨勫璞℃坊鍔犺瘎璁恒傝鎴戜滑缁х画鍚戝墠鍚с
鍏充簬鏈瘎娉ㄧ郴缁
鏈珯浣跨敤涓婁笅鏂囧叧鑱旂殑璇勬敞绯荤粺鏉ユ敹闆嗗弽棣堜俊鎭備笉鍚屼簬涓鑸鏁寸珷鍋氳瘎娉ㄧ殑鍋氭硶锛 鎴戜滑鍏佽浣犲姣忎竴涓嫭绔嬬殑鈥滄枃鏈潡鈥濆仛璇勬敞銆備竴涓滄枃鏈潡鈥濈湅璧锋潵鏄繖鏍风殑锛
涓涓滄枃鏈潡鈥濇槸涓涓钀斤紝涓涓垪琛ㄩ」锛屼竴娈典唬鐮侊紝鎴栬呭叾浠栦竴灏忔鍐呭銆 浣犻変腑瀹冧細楂樹寒搴︽樉绀:
瑕佸鏂囨湰鍧楀仛璇勬敞锛屼綘鍙渶瑕佺偣鍑诲畠鏃佽竟鐨勬爣璇嗗潡:
鎴戜滑浼氫粩缁嗛槄璇绘瘡涓瘎璁猴紝濡傛灉鍙兘鐨勮瘽鎴戜滑涔熶細鎶婅瘎娉ㄨ冭檻鍒版湭鏉ョ殑鐗堟湰涓幓:
濡傛灉浣犳効鎰忎綘鐨勮瘎娉ㄨ閲囩敤锛岃纭繚鐣欎笅浣犵殑鍏ㄥ悕 (娉ㄦ剰涓嶆槸鏄电О鎴栫畝绉帮級
Many, many thanks to Jack Slocum; the inspiration and much of the code for the comment system comes from Jack's blog, and this site couldn't have been built without his wonderful
YAHOO.ext
library. Thanks also to Yahoo for YUI itself.