http請求中產生兩個核心對象:
http請求:HttpRequest對象
http響應:HttpResponse對象
所在位置:django.http
/usr/local/lib/python3.7/site-packages/django/http目錄下的文件:
ElainedeAir:http elaine$ tree.├── __init__.py├── __pycache__│ ├── __init__.cpython-37.pyc│ ├── cookie.cpython-37.pyc│ ├── multipartparser.cpython-37.pyc│ ├── request.cpython-37.pyc│ └── response.cpython-37.pyc├── cookie.py├── multipartparser.py├── request.py└── response.py
1 directory, 10 filesElainedeAir:http elaine$1. 通過源碼理解HttpResponse對象
在視圖views.py文件中,對於http兩個核心對象的使用方法是:
from django.shortcuts import HttpResponse
django.shortcuts對應具體的文件為:
/usr/local/lib/python3.7/site-packages/django/shortcuts.py
在該文件中有如下內容:
#shortcuts.py文件中內容from django.http import ( Http404, HttpResponse, HttpResponsePermanentRedirect, HttpResponseRedirect,)
上述就是在views.py文件中,對HttpResponse對象的調用邏輯。
HttpResponse對象(源碼)到底在哪個文件中定義?
HttpResponse對象就是下述文件中定義的一個類:
class HttpResponse(HttpResponseBase): """ An HTTP response class with a string as content.
This content that can be read, appended to, or replaced. """
streaming = False
def __init__(self, content=b'', *args, **kwargs): super().__init__(*args, **kwargs) self.content = content
def __repr__(self): return '<%(cls)s status_code=%(status_code)d%(content_type)s>' % { 'cls': self.__class__.__name__, 'status_code': self.status_code, 'content_type': self._content_type_for_repr, }
def serialize(self): """Full HTTP message, including headers, as a bytestring.""" return self.serialize_headers() + b'\r\n\r\n' + self.content
__bytes__ = serialize
@property def content(self): return b''.join(self._container)
@content.setter def content(self, value): if hasattr(value, '__iter__') and not isinstance(value, (bytes, str)): content = b''.join(self.make_bytes(chunk) for chunk in value) if hasattr(value, 'close'): try: value.close() except Exception: pass else: content = self.make_bytes(value) self._container = [content]
def __iter__(self): return iter(self._container)
def write(self, content): self._container.append(self.make_bytes(content))
def tell(self): return len(self.content)
def getvalue(self): return self.content
def writable(self): return True
def writelines(self, lines): for line in lines: self.write(line)從源碼可以看出,HttpResponse是HttpResponseBase的子類:
class HttpResponseBase: """ An HTTP response base class with dictionary-accessed headers.
This class doesn't handle content. It should not be used directly. Use the HttpResponse and StreamingHttpResponse subclasses instead. """
status_code = 200
def __init__(self, content_type=None, status=None, reason=None, charset=None): # _headers is a mapping of the lowercase name to the original case of # the header (required for working with legacy systems) and the header # value. Both the name of the header and its value are ASCII strings. self._headers = {} self._closable_objects = [] # This parameter is set by the handler. It's necessary to preserve the self._handler_class = None self.cookies = SimpleCookie() self.closed = False if status is not None: try: self.status_code = int(status) except (ValueError, TypeError): raise TypeError('HTTP status code must be an integer.')
if not 100 <= self.status_code <= 599: raise ValueError('HTTP status code must be an integer from 100 to 599.') self._reason_phrase = reason self._charset = charset if content_type is None: content_type = '%s; charset=%s' % (settings.DEFAULT_CONTENT_TYPE, self.charset) self['Content-Type'] = content_type
@property def reason_phrase(self): if self._reason_phrase is not None: return self._reason_phrase return responses.get(self.status_code, 'Unknown Status Code')
@reason_phrase.setter def reason_phrase(self, value): self._reason_phrase = value
@property def charset(self): if self._charset is not None: return self._charset content_type = self.get('Content-Type', '') matched = _charset_from_content_type_re.search(content_type) if matched: return matched.group('charset').replace('"', '') return settings.DEFAULT_CHARSET
@charset.setter def charset(self, value): self._charset = value
def serialize_headers(self): """HTTP headers as a bytestring.""" def to_bytes(val, encoding): return val if isinstance(val, bytes) else val.encode(encoding)
headers = [ (to_bytes(key, 'ascii') + b': ' + to_bytes(value, 'latin-1')) for key, value in self._headers.values() ] return b'\r\n'.join(headers)
__bytes__ = serialize_headers
@property def _content_type_for_repr(self): return ', "%s"' % self['Content-Type'] if 'Content-Type' in self else ''
def _convert_to_charset(self, value, charset, mime_encode=False): """ Convert headers key/value to ascii/latin-1 native strings.
`charset` must be 'ascii' or 'latin-1'. If `mime_encode` is True and `value` can't be represented in the given charset, apply MIME-encoding. """ if not isinstance(value, (bytes, str)): value = str(value) if ((isinstance(value, bytes) and (b'\n' in value or b'\r' in value)) or isinstance(value, str) and ('\n' in value or '\r' in value)): raise BadHeaderError("Header values can't contain newlines (got %r)" % value) try: if isinstance(value, str): value.encode(charset) else: value = value.decode(charset) except UnicodeError as e: if mime_encode: value = Header(value, 'utf-8', maxlinelen=sys.maxsize).encode() else: e.reason += ', HTTP response headers must be in %s format' % charset raise return value
def __setitem__(self, header, value): header = self._convert_to_charset(header, 'ascii') value = self._convert_to_charset(value, 'latin-1', mime_encode=True) self._headers[header.lower()] = (header, value)
def __delitem__(self, header): self._headers.pop(header.lower(), False)
def __getitem__(self, header): return self._headers[header.lower()][1]
def has_header(self, header): """Case-insensitive check for a header.""" return header.lower() in self._headers
__contains__ = has_header
def items(self): return self._headers.values()
def get(self, header, alternate=None): return self._headers.get(header.lower(), (None, alternate))[1]
def set_cookie(self, key, value='', max_age=None, expires=None, path='/', domain=None, secure=False, httponly=False, samesite=None): """ Set a cookie.
``expires`` can be: - a string in the correct format, - a naive ``datetime.datetime`` object in UTC, - an aware ``datetime.datetime`` object in any time zone. If it is a ``datetime.datetime`` object then calculate ``max_age``. """ self.cookies[key] = value if expires is not None: if isinstance(expires, datetime.datetime): if timezone.is_aware(expires): expires = timezone.make_naive(expires, timezone.utc) delta = expires - expires.utcnow() delta = delta + datetime.timedelta(seconds=1) expires = None max_age = max(0, delta.days * 86400 + delta.seconds) else: self.cookies[key]['expires'] = expires else: self.cookies[key]['expires'] = '' if max_age is not None: self.cookies[key]['max-age'] = max_age if not expires: self.cookies[key]['expires'] = http_date(time.time() + max_age) if path is not None: self.cookies[key]['path'] = path if domain is not None: self.cookies[key]['domain'] = domain if secure: self.cookies[key]['secure'] = True if httponly: self.cookies[key]['httponly'] = True if samesite: if samesite.lower() not in ('lax', 'strict'): raise ValueError('samesite must be "lax" or "strict".') self.cookies[key]['samesite'] = samesite
def setdefault(self, key, value): """Set a header unless it has already been set.""" if key not in self: self[key] = value
def set_signed_cookie(self, key, value, salt='', **kwargs): value = signing.get_cookie_signer(salt=key + salt).sign(value) return self.set_cookie(key, value, **kwargs)
def delete_cookie(self, key, path='/', domain=None): secure = key.startswith(('__Secure-', '__Host-')) self.set_cookie( key, max_age=0, path=path, domain=domain, secure=secure, expires='Thu, 01 Jan 1970 00:00:00 GMT', )
def make_bytes(self, value): """Turn a value into a bytestring encoded in the output charset."""
if isinstance(value, bytes): return bytes(value) if isinstance(value, str): return bytes(value.encode(self.charset)) return str(value).encode(self.charset)
def close(self): for closable in self._closable_objects: try: closable.close() except Exception: pass self.closed = True signals.request_finished.send(sender=self._handler_class)
def write(self, content): raise IOError("This %s instance is not writable" % self.__class__.__name__)
def flush(self): pass
def tell(self): raise IOError("This %s instance cannot tell its position" % self.__class__.__name__)
def readable(self): return False
def seekable(self): return False
def writable(self): return False
def writelines(self, lines): raise IOError("This %s instance is not writable" % self.__class__.__name__)2. 通過源碼理解HttpRequest對象
django.http是框架最核心的部分,框架把客戶端的url請求轉換成HttpRequest, 傳遞給我們自己寫的view函數,在上面的代碼裡就是index。由view函數負責生成HttpResponse對象返回給框架, 框架根據這個對象做相應的處理,返回給客戶端。
class HttpRequest: def __init__(self): ...
def __repr__(self): ... @cached_property def headers(self): ...
def _get_raw_host(self): ...
def get_host(self): ... def get_port(self): ...
def get_full_path(self, force_append_slash=False): ...
def get_full_path_info(self, force_append_slash=False): ...
def _get_full_path(self, path, force_append_slash): ...
def get_signed_cookie(self, key, default=RAISE_ERROR, salt='', max_age=None): ...
def get_raw_uri(self): ...
def build_absolute_uri(self, location=None): ...
@cached_property def _current_scheme_host(self): ... def _get_scheme(self): ...
@property def scheme(self): ...
def is_secure(self): ...
def is_ajax(self): ... @property def encoding(self): return self._encoding
@encoding.setter def encoding(self, val): ...
def _initialize_handlers(self): ... @property def upload_handlers(self): ...
@upload_handlers.setter def upload_handlers(self, upload_handlers): ...
def parse_file_upload(self, META, post_data): ...
@property def body(self): ...
def _mark_post_parse_error(self): ... def _load_post_and_files(self): ... def close(self): ...
def read(self, *args, **kwargs): ... def readline(self, *args, **kwargs): ...
def __iter__(self): ...
def xreadlines(self): ...
def readlines(self): ...3. HttpRequest類對象方法介紹
HttpRequest類中有幾個比較重要的方法:
3.1Django配置說明
from blog import viewsfrom django.conf.urls import url,includeurlpatterns = [ url(r'login',views.login,name='bieming'), ]
def login(request): if request.method=='POST': username=request.POST.get('username') password=request.POST.get('password') if username=='laowang' and password=='pinginglab': return HttpResponse("登陸成功") return render(request,'login')
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><form action="{% url 'bieming' %}" method="POST"> 用戶名:<input type="text" name="username"> 密碼:<input type="password" name="password"> <input type="submit" value="submit"> </form></body></html>3.2 方法介紹
3.2.1 request.schemeA string representing the scheme of the request (httporhttpsusually).
3.2.2 request.bodyThe raw HTTP request body as a byte string. This is useful for processing data in different ways than conventional HTML forms: binary images, XML payload etc. For processing conventional form data, useHttpRequest.POST.
You can also read from an HttpRequest using a file-like interface. SeeHttpRequest.read().
演示配置中,如果在視圖函數中增加一行:
輸出結果為:
b'' b'username=laowang&password=pinginglab'
3.2.3 request.pathA string representing the full path to the requested page,not including the scheme or domain.
Example:"/music/bands/the_beatles/"
3.2.4 request.path_infoUnder some Web server configurations, the portion of the URL after the host name is split up into a script prefix portion and a path info portion. Thepath_infoattribute always contains the path info portion of the path, no matter what Web server is being used. Using this instead ofpathcan make your code easier to move between test and deployment servers.
For example, if theWSGIScriptAliasfor your application is set to"/minfo", thenpathmight be"/minfo/music/bands/the_beatles/"andpath_infowould be"/music/bands/the_beatles/".
在某些Web伺服器配置下,主機名後的URL部分被分成腳本前綴部分和路徑信息部分。path_info屬性將始終包含路徑信息部分,不論使用的Web伺服器是什麼。使用它代替path可以讓代碼在測試和開發環境中更容易地切換。
例如,如果應用的WSGIScriptAlias設置為/minfo,那麼HttpRequest.path等於/music/bands/the_beatles/,而HttpRequest.path_info為/minfo/music/bands/the_beatles/
3.2.5 request.method
A string representing the HTTP method used in the request. This is guaranteed to be uppercase. Example:
if request.method == 'GET': do_something()elif request.method == 'POST': do_something_else()
通過這個屬性來判斷請求的方法,然後根據請求的方法不同,在視圖中執行不同的代碼。
3.2.6 request.encoding
A string representing the current encoding used to decode form submission data (orNone, which means theDEFAULT_CHARSETsetting is used). You can write to this attribute to change the encoding used when accessing the form data. Any subsequent attribute accesses (such as reading fromGETorPOST) will use the newencodingvalue. Useful if you know the form data is not in theDEFAULT_CHARSETencoding.
字符串類型,表示提交的數據的編碼方式(如果為None 則表示使用DEFAULT_CHARSET設置)。 這個屬性是可寫的,可以通過修改它來改變表單數據的編碼。任何隨後的屬性訪問(例如GET或POST)將使用新的編碼方式。
3.2.7 request.content_type
A string representing the MIME type of the request, parsed from theCONTENT_TYPEheader.
Django1.10中新增。表示從CONTENT_TYPE頭解析的請求的MIME類型
3.2.8 request.content_paramsA dictionary of key/value parameters included in theCONTENT_TYPEheader.
3.2.9 request.GET
A dictionary-like object containing all given HTTP GET parameters. See theQueryDictdocumentation below.
3.2.10 request.POST
A dictionary-like object containing all given HTTP POST parameters, providing that the request contains form data. See theQueryDictdocumentation below. If you need to access raw or non-form data posted in the request, access this through theHttpRequest.bodyattribute instead.
It’s possible that a request can come in via POST with an emptyPOSTdictionary – if, say, a form is requested via the POST HTTP method but does not include form data. Therefore, you shouldn’t useif request.POSTto check for use of the POST method; instead, useifrequest.method == "POST"(seeHttpRequest.method).
POSTdoesnotinclude file-upload information. SeeFILES.
3.2.11 request.COOKIES
A dictionary containing all cookies. Keys and values are strings.
3.2.12 request.FILESA dictionary-like object containing all uploaded files. Each key inFILESis thenamefrom the<input type="file" name="">. Each value inFILESis anUploadedFile.
SeeManaging filesfor more information.
FILESwill only contain data if the request method was POST and the<form>that posted to the request hadenctype="multipart/form-data". Otherwise,FILESwill be a blank dictionary-like object.
3.2.13 request.META
A dictionary containing all available HTTP headers. Available headers depend on the client and server, but here are some examples:
CONTENT_LENGTH– The length of the request body (as a string).
CONTENT_TYPE– The MIME type of the request body.
HTTP_ACCEPT– Acceptable content types for the response.
HTTP_ACCEPT_ENCODING– Acceptable encodings for the response.
HTTP_ACCEPT_LANGUAGE– Acceptable languages for the response.
HTTP_HOST– The HTTP Host header sent by the client.
HTTP_REFERER– The referring page, if any.
HTTP_USER_AGENT– The client’s user-agent string.
QUERY_STRING– The query string, as a single (unparsed) string.
REMOTE_ADDR– The IP address of the client.
REMOTE_HOST– The hostname of the client.
REMOTE_USER– The user authenticated by the Web server, if any.
REQUEST_METHOD– A string such as"GET"or"POST".
SERVER_NAME– The hostname of the server.
SERVER_PORT– The port of the server (as a string).
With the exception of CONTENT_LENGTH and CONTENT_TYPE, as given above, any HTTP headers in the request are converted toMETAkeys by converting all characters to uppercase, replacing any hyphens with underscores and adding an HTTP_ prefix to the name. So, for example, a header called X-Bender would be mapped to the META key HTTP_X_BENDER.
Note thatrunserverstrips all headers with underscores in the name, so you won’t see them in META. This prevents header-spoofing based on ambiguity between underscores and dashes both being normalizing to underscores in WSGI environment variables. It matches the behavior of Web servers like Nginx and Apache 2.4+.
3.2.14 request.resolver_match
An instance ofResolverMatchrepresenting the resolved URL. This attribute is only set after URL resolving took place, which means it’s available in all views but not in middleware which are executed before URL resolving takes place (you can use it inprocess_view()though).
3.2.15 request.session
From theSessionMiddleware: A readable and writable, dictionary-like object that represents the current session.
3.2.16 request.userFrom theAuthenticationMiddleware: An instance ofAUTH_USER_MODELrepresenting the currently logged-in user. If the user isn’t currently logged in,userwill be set to an instance ofAnonymousUser. You can tell them apart withis_authenticated, like so:
if request.user.is_authenticated: ... # Do something for logged-in users.else: ... # Do something for anonymous users.
AuthenticationMiddleware中間件:表示當前登錄的用戶的AUTH_USER_MODEL的實例,這個模型是Django內置的Auth模塊下的User模型。如果用戶當前未登錄,則user將被設置為AnonymousUser的實例。
3.2.17 request..get_host()
Returns the originating host of the request using information from theHTTP_X_FORWARDED_HOST(ifUSE_X_FORWARDED_HOSTis enabled) andHTTP_HOSTheaders, in that order. If they don’t provide a value, the method uses a combination ofSERVER_NAMEandSERVER_PORTas detailed inPEP 3333.
Example:"127.0.0.1:8000"
Note:
Theget_host()method fails when the host is behind multiple proxies. One solution is to use middleware to rewrite the proxy headers, as in the following example:
from django.utils.deprecation import MiddlewareMixin
class MultipleProxyMiddleware(MiddlewareMixin): FORWARDED_FOR_FIELDS = [ 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED_HOST', 'HTTP_X_FORWARDED_SERVER', ]
def process_request(self, request): """ Rewrites the proxy headers so that only the most recent proxy is used. """ for field in self.FORWARDED_FOR_FIELDS: if field in request.META: if ',' in request.META[field]: parts = request.META[field].split(',') request.META[field] = parts[-1].strip()This middleware should be positioned before any other middleware that relies on the value ofget_host()– for instance,CommonMiddlewareorCsrfViewMiddleware.
3.2.18 request.get_port()Returns the originating port of the request using information from theHTTP_X_FORWARDED_PORT(ifUSE_X_FORWARDED_PORTis enabled) andSERVER_PORTMETAvariables, in that order.
3.2.19 request.get_full_path()與get_full_path_info()
Returns thepath, plus an appended query string, if applicable.
Example:"/music/bands/the_beatles/?print=true"
request.get_full_path_info():
New in Django 2.1
Likeget_full_path(), but usespath_infoinstead ofpath.
Example:"/minfo/music/bands/the_beatles/?print=true"
3.2.20 request.build_absolute_uri(location)
Returns the absolute URI form oflocation. If no location is provided, the location will be set torequest.get_full_path().
If the location is already an absolute URI, it will not be altered. Otherwise the absolute URI is built using the server variables available in this request. For example:
>>> request.build_absolute_uri()'https://example.com/music/bands/the_beatles/?print=true'>>> request.build_absolute_uri('/bands/')'https://example.com/bands/'>>> request.build_absolute_uri('https://example2.com/bands/')'https://example2.com/bands/'Note:
Mixing HTTP and HTTPS on the same site is discouraged, thereforebuild_absolute_uri()will always generate an absolute URI with the same scheme the current request has. If you need to redirect users to HTTPS, it’s best to let your Web server redirect all HTTP traffic to HTTPS.
3.2.21 request.get_signed_cookie(key,default=RAISE_ERROR,salt='',max_age=None)
Returns a cookie value for a signed cookie, or raises adjango.core.signing.BadSignatureexception if the signature is no longer valid. If you provide thedefaultargument the exception will be suppressed and that default value will be returned instead.
The optionalsaltargument can be used to provide extra protection against brute force attacks on your secret key. If supplied, themax_ageargument will be checked against the signed timestamp attached to the cookie value to ensure the cookie is not older thanmax_ageseconds.
For example:
>>> request.get_signed_cookie('name')'Tony'>>> request.get_signed_cookie('name', salt='name-salt')'Tony' >>> request.get_signed_cookie('nonexistent-cookie')...KeyError: 'nonexistent-cookie'>>> request.get_signed_cookie('nonexistent-cookie', False)False>>> request.get_signed_cookie('cookie-that-was-tampered-with')...BadSignature: ...>>> request.get_signed_cookie('name', max_age=60)...SignatureExpired: Signature age 1677.3839159 > 60 seconds>>> request.get_signed_cookie('name', False, max_age=60)False3.2.22 request.is_secure()ReturnsTrueif the request is secure; that is, if it was made with HTTPS.
3.2.23 request.is_ajax()
ReturnsTrueif the request was made via anXMLHttpRequest, by checking theHTTP_X_REQUESTED_WITHheader for the string'XMLHttpRequest'. Most modern JavaScript libraries send this header. If you write your ownXMLHttpRequestcall (on the browser side), you』ll have to set this header manually if you wantis_ajax()to work.
If a response varies on whether or not it’s requested via AJAX and you are using some form of caching like Django’scachemiddleware, you should decorate the view withvary_on_headers('X-Requested-With')so that the responses are properly cached.
3.2.24 request.read(size=None)3.2.25 request.readline()3.2.26 request.readlines()3.2.27 request.xreadlines()3.2.28 request.__iter__()Methods implementing a file-like interface for reading from anHttpRequestinstance. This makes it possible to consume an incoming request in a streaming fashion. A common use-case would be to process a big XML payload with an iterative parser without constructing a whole XML tree in memory.
Given this standard interface, anHttpRequestinstance can be passed directly to an XML parser such asElementTree:
import xml.etree.ElementTree as ETfor element in ET.iterparse(request): process(element)
3.3 幾個重要屬性與方法介紹
3.3.1request.headers
修改視圖函數:
def login(request): print (request.headers)查看視圖函數輸出結果:
{'Content-Length': '', 'Content-Type': 'text/plain', 'Host': '127.0.0.1:8080', 'Connection': 'keep-alive', 'Cache-Control': 'max-age=0', 'Upgrade-Insecure-Requests': '1', 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3', 'Accept-Encoding': 'gzip, deflate, br', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', 'Cookie': 'SL_G_WPT_TO=zh; SL_GWPT_Show_Hide_tmp=1; SL_wptGlobTipTmp=1; csrftoken=h0euPNihCLct0kaBefG0oj1aFwAXW4FlFwfOyKhSOSofGUE79OnaW8h5Rx6eVMSh; sessionid=2fq4v9sukcg1i5emuooc2ahteeehve6b'}
3.3.2request.path
修改視圖函數:
查看視圖函數輸出結果:
3.3.3request.method
修改視圖函數:
查看視圖函數輸出結果:
3.3.4request.POST
修改視圖函數:
查看視圖函數輸出結果:
<QueryDict: {}>
<QueryDict: {'username': ['pinginglab'], 'password': ['pinginglab']}>3.3.5request.GET
修改視圖函數:
查看視圖函數輸出結果:
3.3.6request.COOKIES
修改視圖函數:
查看視圖函數輸出結果:
{'SL_G_WPT_TO': 'zh', 'SL_GWPT_Show_Hide_tmp': '1', 'SL_wptGlobTipTmp': '1', 'csrftoken': 'h0euPNihCLct0kaBefG0oj1aFwAXW4FlFwfOyKhSOSofGUE79OnaW8h5Rx6eVMSh', 'sessionid': '2fq4v9sukcg1i5emuooc2ahteeehve6b'}
包含所有cookies的標準Python字典對象;
keys和values都是字符串
3.3.7request.FILES
修改視圖函數:
查看視圖函數輸出結果:
包含所有上傳文件的類字典對象;
FILES中的每一個Key都是<input type="file" name="" />標籤中name屬性的值,FILES中的每一個value同時也是一個標準的python字典對象,包含下面三個Keys:
filename: 上傳文件名,用字符串表示content_type: 上傳文件的Content Typecontent: 上傳文件的原始內容
3.3.8request.user
修改視圖函數:
查看視圖函數輸出結果:
注意:
這個是獲取我們創建的django的後臺管理員帳號,與login文件中的form表單中的username沒有任何關係。
是一個django.contrib.auth.models.User對象,代表當前登陸的用戶。如果訪問用戶當前沒有登陸,user將被初始化為django.contrib.auth.models.AnonymousUser的實例。你可以通過user的is_authenticated()方法來辨別用戶是否登陸:
if req.user.is_authenticated()
只有激活Django中的AuthenticationMiddleware時該屬性才可用。
3.3.9Request.session
修改視圖函數:
查看視圖函數輸出結果:
<django.contrib.sessions.backends.db.SessionStore object at 0x104a42780>
這個是由中間件設置的屬性:
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',]
SessionMiddleware中間件:一個可讀寫的,類似字典的對象,表示當前會話。
我們要保存用戶狀態,會話過程等等,靠的就是這個中間件和這個屬性。
3.3.10request.get_full_path()
修改視圖函數:
print (request.get_full_path())
查看視圖函數輸出結果:
/login/index.html?name=pinginglab
比如:http://127.0.0.1:8080/login/index.html?name=pinginglab
request.get_full_path()得到的結果就是/login/index.html?name=pinginglab
Request.path得到的結果就是/login/index.html
3.3.11request.POST.getlist('')
django中通過getlist()接收頁面form的post數組。
Recently I tried sending a list through a form using something like<input name="mylist[]" value="some val">*. When this form is submitted all of the inputs with that name are grouped together for easy access. In Django, one would think you could simply access the list usingrequest.POST['mylist[]']
. This is not the case.Using that command will give you the last value. Django says this is a featureand to get the list userequest.POST.getlist('my_list[]').
4. redirect()與locals()函數
/usr/local/lib/python3.7/site-packages/django/shortcuts.py文件中定義了一個函數redirect();
Python本身有一個內置函數locals();
4.1 深入理解redirect()函數4.1.1 產生的背景
舉個例子:當用戶在頁面上註冊好後,希望能夠直接自動將登錄頁面加載給用戶。這個時候就涉及到跳轉的問題。用戶註冊執行的是視圖函數中的register函數,用戶登錄執行的是login函數,在register函數中做判斷,當用戶註冊成功後,可以直接調用登錄相關的視圖函數。
redirect()函數源碼:
def redirect(to, *args, permanent=False, **kwargs): """ Return an HttpResponseRedirect to the appropriate URL for the arguments passed.
The arguments could be:
* A model: the model's `get_absolute_url()` function will be called.
* A view name, possibly with arguments: `urls.reverse()` will be used to reverse-resolve the name.
* A URL, which will be used as-is for the redirect location.
Issues a temporary redirect by default; pass permanent=True to issue a permanent redirect. """ redirect_class = HttpResponsePermanentRedirect if permanent else HttpResponseRedirect return redirect_class(resolve_url(to, *args, **kwargs))4.1.2 案例演示from blog import viewsfrom django.conf.urls import url,includeurlpatterns = [ path('admin/', admin.site.urls), path('show_time/', views.show_time), url(r'^blog/',include('blog.urls')), url(r'^register$',views.register,name='register'), url(r'login',views.login,name='bieming'), ]
def register(request): if request.method=='POST': username=request.POST.get('username') password=request.POST.get('password') if username and password: return redirect('/login/') #注意一定要帶上"/",不能直接只寫"login" return render(request,'register')
def login(request): if request.method=='POST': username=request.POST.get('username') password=request.POST.get('password') if username=='laowang' and password=='pinginglab': return HttpResponse("登陸成功") return render(request,'login')#register<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>用戶註冊</title></head><body><form action="{% url 'register' %}" method="POST"> 用戶名:<input type="text" name="username"> 密碼:<input type="password" name="password"> <input type="submit" value="submit"> </form></body></html>
#login<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>用戶登錄</title></head><body><form action="{% url 'login' %}" method="POST"> 用戶名:<input type="text" name="username"> 密碼:<input type="password" name="password"> <input type="submit" value="submit"> </form></body></html>註冊到登錄的跳轉
註冊界面
填寫用戶名與密碼後,點擊提交
點擊提交後,直接跳轉到登錄界面
輸入帳號密碼進行登錄
4.2 深入理解locals()函數
4.2.1 背景理解render與render_to_response函數:
def render_to_response(template_name, context=None, content_type=None, status=None, using=None): """ Return a HttpResponse whose content is filled with the result of calling django.template.loader.render_to_string() with the passed arguments. """ warnings.warn( 'render_to_response() is deprecated in favor of render(). It has the ' 'same signature except that it also requires a request.', RemovedInDjango30Warning, stacklevel=2, ) content = loader.render_to_string(template_name, context, using=using) return HttpResponse(content, content_type, status)def render(request, template_name, context=None, content_type=None, status=None, using=None): """ Return a HttpResponse whose content is filled with the result of calling django.template.loader.render_to_string() with the passed arguments. """ content = loader.render_to_string(template_name, context, request, using=using) return HttpResponse(content, content_type, status)簡單演示render函數的使用:
from django.contrib import adminfrom django.urls import pathfrom blog import viewsfrom django.conf.urls import url,includeurlpatterns = [ path('show_time/', views.show_time), ]
def show_time(request): t = time.ctime() return render(request,"index",{"time":t})
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <Title>Title</Title> <style> * { margin:0; padding:0 }</style></head><body> <h1>hello,{{ time }}</h1></body></html>梳理演示render函數中顯示的結果:
4.2.2 案例演示
通過演示render函數的使用,細心的童鞋會發現一個問題:如果視圖函數中定義了很多個變量時,就需要在視圖函數中調用render函數時,都寫到字典中,導致開發效率低下。怎麼解決這個問題呢?
def show_time(request): t = time.ctime() return render(request,"index",{"time":t})在該視圖函數中,視圖函數將結果保存到t這個變量中,然後將變量發送給模板,喜歡偷懶的程式設計師應該注意到了,不斷地為臨時變量和臨時模板命名有那麼一點點多餘。 不僅多餘,而且需要額外的輸入。
如果你是個喜歡偷懶的程式設計師並想讓代碼看起來更加簡明,可以利用Python的內建函數locals()。它返回的字典對所有局部變量的名稱與值進行映射。
修改視圖函數為:
def show_time(request): t = time.ctime() current_date = datetime.datetime.now() return render(request,"index",locals())修改index這個html文件為:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <Title>Title</Title> <style> * { margin:0; padding:0 }</style></head><body> <h1>hello,{{ t }}</h1> #注意這一行,從{{ time }}變成了{{ t }}</body>
</html>
{{ time }}是因為之前在視圖函數中使用了{"time":t}。
在此,我們沒有像之前那樣手工指定context字典,而是傳入了locals()的值,它囊括了函數執行到該時間點時所定義的一切變量。使用locals()時要注意是它將包括 所有 的局部變量,它們可能比你想讓模板訪問的要多。
——END——