目录

  • 请求对象HttpRequest
  • 请求数据集QuerySet
  • 响应对象HttpResponse
    • 主要属性和方法
    • HttpResponse子类扩展
    • 特殊响应类型
  • 实际案例
    • 1.完善注册登录功能
    • 2.完善博客首页
    • 3.完善查询所有文章的功能
    • 4.完善查询指定编号的文章功能
    • 5.完善查询作者的所有文章功能

在Django中请求和响应都是通过内置模块进行自动传递和操作的。在客户端发送请求到服务器时,请求中所有数据会封装在 django.http.request.HttpRequest对象中,传递给视图函数的第一个参数。理论上视图处理函数都会返回一个 django.http.response.HttpResponseBase对象,包含返回给客户端的所有数据。

请求对象HttpRequest

HttpRequest的源码如下:

class HttpRequest:"""A basic HTTP request."""# The encoding used in GET/POST dicts. None means use default setting._encoding = None_upload_handlers = []def __init__(self):# WARNING: The `WSGIRequest` subclass doesn't call `super`.# Any variable assignment made here should also happen in# `WSGIRequest.__init__()`.self.GET = QueryDict(mutable=True)self.POST = QueryDict(mutable=True)self.COOKIES = {}self.META = {}self.FILES = MultiValueDict()self.path = ""self.path_info = ""self.method = Noneself.resolver_match = Noneself.content_type = Noneself.content_params = Nonedef __repr__(self):if self.method is None or not self.get_full_path():return "<%s>" % self.__class__.__name__return "<%s: %s %r>" % (self.__class__.__name__,self.method,self.get_full_path(),)@cached_propertydef headers(self):return HttpHeaders(self.META)@cached_propertydef accepted_types(self):"""Return a list of MediaType instances."""return parse_accept_header(self.headers.get("Accept", "*/*"))def accepts(self, media_type):return any(accepted_type.match(media_type) for accepted_type in self.accepted_types)def _set_content_type_params(self, meta):"""Set content_type, content_params, and encoding."""self.content_type, self.content_params = parse_header_parameters(meta.get("CONTENT_TYPE", ""))if "charset" in self.content_params:try:codecs.lookup(self.content_params["charset"])except LookupError:passelse:self.encoding = self.content_params["charset"]def _get_raw_host(self):"""Return the HTTP host using the environment or request headers. Skipallowed hosts protection, so may return an insecure host."""# We try three options, in order of decreasing preference.if settings.USE_X_FORWARDED_HOST and ("HTTP_X_FORWARDED_HOST" in self.META):host = self.META["HTTP_X_FORWARDED_HOST"]elif "HTTP_HOST" in self.META:host = self.META["HTTP_HOST"]else:# Reconstruct the host using the algorithm from PEP 333.host = self.META["SERVER_NAME"]server_port = self.get_port()if server_port != ("443" if self.is_secure() else "80"):host = "%s:%s" % (host, server_port)return hostdef get_host(self):"""Return the HTTP host using the environment or request headers."""host = self._get_raw_host()# Allow variants of localhost if ALLOWED_HOSTS is empty and DEBUG=True.allowed_hosts = settings.ALLOWED_HOSTSif settings.DEBUG and not allowed_hosts:allowed_hosts = [".localhost", "127.0.0.1", "[::1]"]domain, port = split_domain_port(host)if domain and validate_host(domain, allowed_hosts):return hostelse:msg = "Invalid HTTP_HOST header: %r." % hostif domain:msg += " You may need to add %r to ALLOWED_HOSTS." % domainelse:msg += (" The domain name provided is not valid according to RFC 1034/1035.")raise DisallowedHost(msg)def get_port(self):"""Return the port number for the request as a string."""if settings.USE_X_FORWARDED_PORT and "HTTP_X_FORWARDED_PORT" in self.META:port = self.META["HTTP_X_FORWARDED_PORT"]else:port = self.META["SERVER_PORT"]return str(port)def get_full_path(self, force_append_slash=False):return self._get_full_path(self.path, force_append_slash)def get_full_path_info(self, force_append_slash=False):return self._get_full_path(self.path_info, force_append_slash)def _get_full_path(self, path, force_append_slash):# RFC 3986 requires query string arguments to be in the ASCII range.# Rather than crash if this doesn't happen, we encode defensively.return "%s%s%s" % (escape_uri_path(path),"/" if force_append_slash and not path.endswith("/") else "",("?" + iri_to_uri(self.META.get("QUERY_STRING", "")))if self.META.get("QUERY_STRING", "")else "",)def get_signed_cookie(self, key, default=RAISE_ERROR, salt="", max_age=None):"""Attempt to return a signed cookie. If the signature fails or thecookie has expired, raise an exception, unless the `default` argumentis provided,  in which case return that value."""try:cookie_value = self.COOKIES[key]except KeyError:if default is not RAISE_ERROR:return defaultelse:raisetry:value = signing.get_cookie_signer(salt=key + salt).unsign(cookie_value, max_age=max_age)except signing.BadSignature:if default is not RAISE_ERROR:return defaultelse:raisereturn valuedef build_absolute_uri(self, location=None):"""Build an absolute URI from the location and the variables available inthis request. If no ``location`` is specified, build the absolute URIusing request.get_full_path(). If the location is absolute, convert itto an RFC 3987 compliant URI and return it. If location is relative oris scheme-relative (i.e., ``//example.com/``), urljoin() it to a baseURL constructed from the request variables."""if location is None:# Make it an absolute url (but schemeless and domainless) for the# edge case that the path starts with '//'.location = "//%s" % self.get_full_path()else:# Coerce lazy locations.location = str(location)bits = urlsplit(location)if not (bits.scheme and bits.netloc):# Handle the simple, most common case. If the location is absolute# and a scheme or host (netloc) isn't provided, skip an expensive# urljoin() as long as no path segments are '.' or '..'.if (bits.path.startswith("/")and not bits.schemeand not bits.netlocand "/./" not in bits.pathand "/../" not in bits.path):# If location starts with '//' but has no netloc, reuse the# schema and netloc from the current request. Strip the double# slashes and continue as if it wasn't specified.if location.startswith("//"):location = location[2:]location = self._current_scheme_host + locationelse:# Join the constructed URL with the provided location, which# allows the provided location to apply query strings to the# base path.location = urljoin(self._current_scheme_host + self.path, location)return iri_to_uri(location)@cached_propertydef _current_scheme_host(self):return "{}://{}".format(self.scheme, self.get_host())def _get_scheme(self):"""Hook for subclasses like WSGIRequest to implement. Return 'http' bydefault."""return "http"@propertydef scheme(self):if settings.SECURE_PROXY_SSL_HEADER:try:header, secure_value = settings.SECURE_PROXY_SSL_HEADERexcept ValueError:raise ImproperlyConfigured("The SECURE_PROXY_SSL_HEADER setting must be a tuple containing ""two values.")header_value = self.META.get(header)if header_value is not None:header_value, *_ = header_value.split(",", 1)return "https" if header_value.strip() == secure_value else "http"return self._get_scheme()def is_secure(self):return self.scheme == "https"@propertydef encoding(self):return self._encoding@encoding.setterdef encoding(self, val):"""Set the encoding used for GET/POST accesses. If the GET or POSTdictionary has already been created, remove and recreate it on thenext access (so that it is decoded correctly)."""self._encoding = valif hasattr(self, "GET"):del self.GETif hasattr(self, "_post"):del self._postdef _initialize_handlers(self):self._upload_handlers = [uploadhandler.load_handler(handler, self)for handler in settings.FILE_UPLOAD_HANDLERS]@propertydef upload_handlers(self):if not self._upload_handlers:# If there are no upload handlers defined, initialize them from settings.self._initialize_handlers()return self._upload_handlers@upload_handlers.setterdef upload_handlers(self, upload_handlers):if hasattr(self, "_files"):raise AttributeError("You cannot set the upload handlers after the upload has been ""processed.")self._upload_handlers = upload_handlersdef parse_file_upload(self, META, post_data):"""Return a tuple of (POST QueryDict, FILES MultiValueDict)."""self.upload_handlers = ImmutableList(self.upload_handlers,warning=("You cannot alter upload handlers after the upload has been ""processed."),)parser = MultiPartParser(META, post_data, self.upload_handlers, self.encoding)return parser.parse()@propertydef body(self):if not hasattr(self, "_body"):if self._read_started:raise RawPostDataException("You cannot access body after reading from request's data stream")# Limit the maximum request data size that will be handled in-memory.if (settings.DATA_UPLOAD_MAX_MEMORY_SIZE is not Noneand int(self.META.get("CONTENT_LENGTH") or 0)> settings.DATA_UPLOAD_MAX_MEMORY_SIZE):raise RequestDataTooBig("Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.")try:self._body = self.read()except OSError as e:raise UnreadablePostError(*e.args) from eself._stream = BytesIO(self._body)return self._bodydef _mark_post_parse_error(self):self._post = QueryDict()self._files = MultiValueDict()def _load_post_and_files(self):"""Populate self._post and self._files if the content-type is a form type"""if self.method != "POST":self._post, self._files = (QueryDict(encoding=self._encoding),MultiValueDict(),)returnif self._read_started and not hasattr(self, "_body"):self._mark_post_parse_error()returnif self.content_type == "multipart/form-data":if hasattr(self, "_body"):# Use already read datadata = BytesIO(self._body)else:data = selftry:self._post, self._files = self.parse_file_upload(self.META, data)except MultiPartParserError:# An error occurred while parsing POST data. Since when# formatting the error the request handler might access# self.POST, set self._post and self._file to prevent# attempts to parse POST data again.self._mark_post_parse_error()raiseelif self.content_type == "application/x-www-form-urlencoded":self._post, self._files = (QueryDict(self.body, encoding=self._encoding),MultiValueDict(),)else:self._post, self._files = (QueryDict(encoding=self._encoding),MultiValueDict(),)def close(self):if hasattr(self, "_files"):for f in chain.from_iterable(list_[1] for list_ in self._files.lists()):f.close()# File-like and iterator interface.## Expects self._stream to be set to an appropriate source of bytes by# a corresponding request subclass (e.g. WSGIRequest).# Also when request data has already been read by request.POST or# request.body, self._stream points to a BytesIO instance# containing that data.def read(self, *args, **kwargs):self._read_started = Truetry:return self._stream.read(*args, **kwargs)except OSError as e:raise UnreadablePostError(*e.args) from edef readline(self, *args, **kwargs):self._read_started = Truetry:return self._stream.readline(*args, **kwargs)except OSError as e:raise UnreadablePostError(*e.args) from edef __iter__(self):return iter(self.readline, b"")def readlines(self):return list(self)

可以看到其属性比较多,其中还有@property修饰的方法属性。下面说一下常见的一些属性:

1.HttpRequest.body

原始的 HTTP 请求体作为一个字节字符串。这对于以不同方式处理非常规 HTML 表单的数据很有用:二进制图像,XML 有效负载等。对于处理传统的表单数据,使用 HttpRequest.POST

2.HttpRequest.method

代表请求中使用的 HTTP 方法的字符串,一定是大写字母。

3.HttpRequest.GET

一个类似字典的对象,包含所有给定的 HTTP GET 参数。

4.HttpRequest.POST

一个类似字典的对象,包含所有给定的 HTTP POST 参数,前提是请求包含表单数据。如果你需要访问请求中发布的原始或非表单数据,可以通过 HttpRequest.body 属性来访问。

5.HttpRequest.COOKIES

一个包含所有 cookies 的字典。键和值是字符串。

6.HttpRequest.FILES

一个类似字典的对象,包含所有上传的文件。FILES 中的每个键是 中的 name。FILES 中的每个值是一个 UploadedFile。
FILES 只有在请求方法是 POST,并且发布请求的有 enctype=“multipart/form-data” 的情况下,才会包含数据。否则,FILES 将是一个类似字典的空白对象。

7.HttpRequest.META

一个包含所有可用的 HTTP 头文件的字典。可用的头信息取决于客户端和服务器。一些可能的例子如下:

1.CONTENT_LENGTH —— 请求体的长度(字符串)。

2.CONTENT_TYPE —— 请求体的 MIME 类型。

3.HTTP_ACCEPT —— 可接受的响应内容类型。

4.HTTP_ACCEPT_ENCODING —— 可接受的响应编码。

5.HTTP_ACCEPT_LANGUAGE —— 可接受的响应语言。

6.HTTP_HOST —— 客户端发送的 HTTP 主机头。

7.HTTP_REFERER —— referrer 页面,如果有的话。

8.HTTP_USER_AGENT —— 客户端的用户代理字符串。

9.QUERY_STRING —— 查询字符串,是一个单一的(未解析的)字符串。

10.REMOTE_ADDR —— 客户机的 IP 地址。

11.REMOTE_HOST —— 客户机的主机名。

12.REMOTE_USER —— Web 服务器认证的用户,如果有的话。

13.REQUEST_METHOD —— “GET” 或 “POST” 等字符串。

14.SERVER_NAME —— 服务器的主机名。

15.SERVER_PORT —— 服务器的端口(字符串)。

请求中的任何 HTTP 头都会被转换为 META 键,方法是将所有字符转换为大写字母,用下划线代替任何连字符,并在名称前加上 HTTP_` 前缀。例如,请求头里的X-CSRFToken在META中变为HTTP_X_CSRFTOKEN。

请求数据集QuerySet

上面我们可以看到,HttpRequest对象的 self.GET 和self.POST属性会返回一个数据集对象。对于常规的GET和POST等请求,请求数据都是包含在QuerySet对象中进行传递和处理的。

QuerySet常见的属性和方法如下:

属性/方法 描述
get(key, default=None) 根据key获取数据集中的value
setdefault(key, default=None) 设置数据集中指定key的默认值
update(key, default=None) 更新数据集中指定key的数据
items(key, default=None) 获取数据集中所有键值对数据
values(key, default=None) 更新数据集中所有的value值
getlist(key, default=None) 获取数据集中指定key的对应的多个数据
setlist(key, list_) 设置数据集中指定key的值
appendlist(key, item) 追加数据集中指定key的对应的列表数据
setlistdefault(key, default_list = None) 设置数据集中指定key的对应的默认列表数据
lists() 获取所有列表数据
dict() 获取所有列表数据并黄钻哈UN吃呢个字典输出

其中加粗的比较常用。

响应对象HttpResponse

源码如下:

import datetime
import io
import json
import mimetypes
import os
import re
import sys
import time
from email.header import Header
from http.client import responses
from urllib.parse import quote, urlparsefrom django.conf import settings
from django.core import signals, signing
from django.core.exceptions import DisallowedRedirect
from django.core.serializers.json import DjangoJSONEncoder
from django.http.cookie import SimpleCookie
from django.utils import timezone
from django.utils.datastructures import CaseInsensitiveMapping
from django.utils.encoding import iri_to_uri
from django.utils.http import http_date
from django.utils.regex_helper import _lazy_re_compile_charset_from_content_type_re = _lazy_re_compile(r";\s*charset=(?P<charset>[^\s;]+)", re.I
)class ResponseHeaders(CaseInsensitiveMapping):def __init__(self, data):"""Populate the initial data using __setitem__ to ensure values arecorrectly encoded."""self._store = {}if data:for header, value in self._unpack_items(data):self[header] = valuedef _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."""try:if isinstance(value, str):# Ensure string is valid in given charsetvalue.encode(charset)elif isinstance(value, bytes):# Convert bytestring using given charsetvalue = value.decode(charset)else:value = str(value)# Ensure string is valid in given charset.value.encode(charset)if "\n" in value or "\r" in value:raise BadHeaderError(f"Header values can't contain newlines (got {value!r})")except UnicodeError as e:# Encoding to a string of the specified charset failed, but we# don't know what type that value was, or if it contains newlines,# which we may need to check for before sending it to be# encoded for multiple character sets.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(f"Header values can't contain newlines (got {value!r})") from eif mime_encode:value = Header(value, "utf-8", maxlinelen=sys.maxsize).encode()else:e.reason += ", HTTP response headers must be in %s format" % charsetraisereturn valuedef __delitem__(self, key):self.pop(key)def __setitem__(self, key, value):key = self._convert_to_charset(key, "ascii")value = self._convert_to_charset(value, "latin-1", mime_encode=True)self._store[key.lower()] = (key, value)def pop(self, key, default=None):return self._store.pop(key.lower(), default)def setdefault(self, key, value):if key not in self:self[key] = valueclass BadHeaderError(ValueError):passclass 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 = 200def __init__(self, content_type=None, status=None, reason=None, charset=None, headers=None):self.headers = ResponseHeaders(headers)self._charset = charsetif "Content-Type" not in self.headers:if content_type is None:content_type = f"text/html; charset={self.charset}"self.headers["Content-Type"] = content_typeelif content_type:raise ValueError("'headers' must not contain 'Content-Type' when the ""'content_type' parameter is provided.")self._resource_closers = []# This parameter is set by the handler. It's necessary to preserve the# historical behavior of request_finished.self._handler_class = Noneself.cookies = SimpleCookie()self.closed = Falseif 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@propertydef reason_phrase(self):if self._reason_phrase is not None:return self._reason_phrase# Leave self._reason_phrase unset in order to use the default# reason phrase for status code.return responses.get(self.status_code, "Unknown Status Code")@reason_phrase.setterdef reason_phrase(self, value):self._reason_phrase = value@propertydef charset(self):if self._charset is not None:return self._charset# The Content-Type header may not yet be set, because the charset is# being inserted *into* it.if content_type := self.headers.get("Content-Type"):if matched := _charset_from_content_type_re.search(content_type):# Extract the charset and strip its double quotes.# Note that having parsed it from the Content-Type, we don't# store it back into the _charset for later intentionally, to# allow for the Content-Type to be switched again later.return matched["charset"].replace('"', "")return settings.DEFAULT_CHARSET@charset.setterdef charset(self, value):self._charset = valuedef serialize_headers(self):"""HTTP headers as a bytestring."""return b"\r\n".join([key.encode("ascii") + b": " + value.encode("latin-1")for key, value in self.headers.items()])__bytes__ = serialize_headers@propertydef _content_type_for_repr(self):return (', "%s"' % self.headers["Content-Type"]if "Content-Type" in self.headerselse "")def __setitem__(self, header, value):self.headers[header] = valuedef __delitem__(self, header):del self.headers[header]def __getitem__(self, header):return self.headers[header]def has_header(self, header):"""Case-insensitive check for a header."""return header in self.headers__contains__ = has_headerdef items(self):return self.headers.items()def get(self, header, alternate=None):return self.headers.get(header, alternate)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``.``max_age`` can be:- int/float specifying seconds,- ``datetime.timedelta`` object."""self.cookies[key] = valueif expires is not None:if isinstance(expires, datetime.datetime):if timezone.is_naive(expires):expires = timezone.make_aware(expires, datetime.timezone.utc)delta = expires - datetime.datetime.now(tz=datetime.timezone.utc)# Add one second so the date matches exactly (a fraction of# time gets lost between converting to a timedelta and# then the date string).delta = delta + datetime.timedelta(seconds=1)# Just set max_age - the max_age logic will set expires.expires = Noneif max_age is not None:raise ValueError("'expires' and 'max_age' can't be used together.")max_age = max(0, delta.days * 86400 + delta.seconds)else:self.cookies[key]["expires"] = expireselse:self.cookies[key]["expires"] = ""if max_age is not None:if isinstance(max_age, datetime.timedelta):max_age = max_age.total_seconds()self.cookies[key]["max-age"] = int(max_age)# IE requires expires, so set it if hasn't been already.if not expires:self.cookies[key]["expires"] = http_date(time.time() + max_age)if path is not None:self.cookies[key]["path"] = pathif domain is not None:self.cookies[key]["domain"] = domainif secure:self.cookies[key]["secure"] = Trueif httponly:self.cookies[key]["httponly"] = Trueif samesite:if samesite.lower() not in ("lax", "none", "strict"):raise ValueError('samesite must be "lax", "none", or "strict".')self.cookies[key]["samesite"] = samesitedef setdefault(self, key, value):"""Set a header unless it has already been set."""self.headers.setdefault(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, samesite=None):# Browsers can ignore the Set-Cookie header if the cookie doesn't use# the secure flag and:# - the cookie name starts with "__Host-" or "__Secure-", or# - the samesite is "none".secure = key.startswith(("__Secure-", "__Host-")) or (samesite and samesite.lower() == "none")self.set_cookie(key,max_age=0,path=path,domain=domain,secure=secure,expires="Thu, 01 Jan 1970 00:00:00 GMT",samesite=samesite,)# Common methods used by subclassesdef make_bytes(self, value):"""Turn a value into a bytestring encoded in the output charset."""# Per PEP 3333, this response body must be bytes. To avoid returning# an instance of a subclass, this function returns `bytes(value)`.# This doesn't make a copy when `value` already contains bytes.# Handle string types -- we can't rely on force_bytes here because:# - Python attempts str conversion first# - when self._charset != 'utf-8' it re-encodes the contentif isinstance(value, (bytes, memoryview)):return bytes(value)if isinstance(value, str):return bytes(value.encode(self.charset))# Handle non-string types.return str(value).encode(self.charset)# These methods partially implement the file-like object interface.# See https://docs.python.org/library/io.html#io.IOBase# The WSGI server must call this method upon completion of the request.# See http://blog.dscpl.com.au/2012/10/obligations-for-calling-close-on.htmldef close(self):for closer in self._resource_closers:try:closer()except Exception:pass# Free resources that were still referenced.self._resource_closers.clear()self.closed = Truesignals.request_finished.send(sender=self._handler_class)def write(self, content):raise OSError("This %s instance is not writable" % self.__class__.__name__)def flush(self):passdef tell(self):raise OSError("This %s instance cannot tell its position" % self.__class__.__name__)# These methods partially implement a stream-like object interface.# See https://docs.python.org/library/io.html#io.IOBasedef readable(self):return Falsedef seekable(self):return Falsedef writable(self):return Falsedef writelines(self, lines):raise OSError("This %s instance is not writable" % self.__class__.__name__)class HttpResponse(HttpResponseBase):"""An HTTP response class with a string as content.This content can be read, appended to, or replaced."""streaming = Falsedef __init__(self, content=b"", *args, **kwargs):super().__init__(*args, **kwargs)# Content is a bytestring. See the `content` property methods.self.content = contentdef __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@propertydef content(self):return b"".join(self._container)@content.setterdef content(self, value):# Consume iterators upon assignment to allow repeated iteration.if hasattr(value, "__iter__") and not isinstance(value, (bytes, memoryview, str)):content = b"".join(self.make_bytes(chunk) for chunk in value)if hasattr(value, "close"):try:value.close()except Exception:passelse:content = self.make_bytes(value)# Create a list of properly encoded bytestrings to support write().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.contentdef writable(self):return Truedef writelines(self, lines):for line in lines:self.write(line)class StreamingHttpResponse(HttpResponseBase):"""A streaming HTTP response class with an iterator as content.This should only be iterated once, when the response is streamed to theclient. However, it can be appended to or replaced with a new iteratorthat wraps the original content (or yields entirely new content)."""streaming = Truedef __init__(self, streaming_content=(), *args, **kwargs):super().__init__(*args, **kwargs)# `streaming_content` should be an iterable of bytestrings.# See the `streaming_content` property methods.self.streaming_content = streaming_contentdef __repr__(self):return "<%(cls)s status_code=%(status_code)d%(content_type)s>" % {"cls": self.__class__.__qualname__,"status_code": self.status_code,"content_type": self._content_type_for_repr,}@propertydef content(self):raise AttributeError("This %s instance has no `content` attribute. Use ""`streaming_content` instead." % self.__class__.__name__)@propertydef streaming_content(self):return map(self.make_bytes, self._iterator)@streaming_content.setterdef streaming_content(self, value):self._set_streaming_content(value)def _set_streaming_content(self, value):# Ensure we can never iterate on "value" more than once.self._iterator = iter(value)if hasattr(value, "close"):self._resource_closers.append(value.close)def __iter__(self):return self.streaming_contentdef getvalue(self):return b"".join(self.streaming_content)class FileResponse(StreamingHttpResponse):"""A streaming HTTP response class optimized for files."""block_size = 4096def __init__(self, *args, as_attachment=False, filename="", **kwargs):self.as_attachment = as_attachmentself.filename = filenameself._no_explicit_content_type = ("content_type" not in kwargs or kwargs["content_type"] is None)super().__init__(*args, **kwargs)def _set_streaming_content(self, value):if not hasattr(value, "read"):self.file_to_stream = Nonereturn super()._set_streaming_content(value)self.file_to_stream = filelike = valueif hasattr(filelike, "close"):self._resource_closers.append(filelike.close)value = iter(lambda: filelike.read(self.block_size), b"")self.set_headers(filelike)super()._set_streaming_content(value)def set_headers(self, filelike):"""Set some common response headers (Content-Length, Content-Type, andContent-Disposition) based on the `filelike` response content."""filename = getattr(filelike, "name", "")filename = filename if isinstance(filename, str) else ""seekable = hasattr(filelike, "seek") and (not hasattr(filelike, "seekable") or filelike.seekable())if hasattr(filelike, "tell"):if seekable:initial_position = filelike.tell()filelike.seek(0, io.SEEK_END)self.headers["Content-Length"] = filelike.tell() - initial_positionfilelike.seek(initial_position)elif hasattr(filelike, "getbuffer"):self.headers["Content-Length"] = (filelike.getbuffer().nbytes - filelike.tell())elif os.path.exists(filename):self.headers["Content-Length"] = (os.path.getsize(filename) - filelike.tell())elif seekable:self.headers["Content-Length"] = sum(iter(lambda: len(filelike.read(self.block_size)), 0))filelike.seek(-int(self.headers["Content-Length"]), io.SEEK_END)filename = os.path.basename(self.filename or filename)if self._no_explicit_content_type:if filename:content_type, encoding = mimetypes.guess_type(filename)# Encoding isn't set to prevent browsers from automatically# uncompressing files.content_type = {"bzip2": "application/x-bzip","gzip": "application/gzip","xz": "application/x-xz",}.get(encoding, content_type)self.headers["Content-Type"] = (content_type or "application/octet-stream")else:self.headers["Content-Type"] = "application/octet-stream"if filename:disposition = "attachment" if self.as_attachment else "inline"try:filename.encode("ascii")file_expr = 'filename="{}"'.format(filename.replace("\\", "\\\\").replace('"', r"\""))except UnicodeEncodeError:file_expr = "filename*=utf-8''{}".format(quote(filename))self.headers["Content-Disposition"] = "{}; {}".format(disposition, file_expr)elif self.as_attachment:self.headers["Content-Disposition"] = "attachment"class HttpResponseRedirectBase(HttpResponse):allowed_schemes = ["http", "https", "ftp"]def __init__(self, redirect_to, *args, **kwargs):super().__init__(*args, **kwargs)self["Location"] = iri_to_uri(redirect_to)parsed = urlparse(str(redirect_to))if parsed.scheme and parsed.scheme not in self.allowed_schemes:raise DisallowedRedirect("Unsafe redirect to URL with protocol '%s'" % parsed.scheme)url = property(lambda self: self["Location"])def __repr__(self):return ('<%(cls)s status_code=%(status_code)d%(content_type)s, url="%(url)s">'% {"cls": self.__class__.__name__,"status_code": self.status_code,"content_type": self._content_type_for_repr,"url": self.url,})class HttpResponseRedirect(HttpResponseRedirectBase):status_code = 302class HttpResponsePermanentRedirect(HttpResponseRedirectBase):status_code = 301class HttpResponseNotModified(HttpResponse):status_code = 304def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)del self["content-type"]@HttpResponse.content.setterdef content(self, value):if value:raise AttributeError("You cannot set content to a 304 (Not Modified) response")self._container = []class HttpResponseBadRequest(HttpResponse):status_code = 400class HttpResponseNotFound(HttpResponse):status_code = 404class HttpResponseForbidden(HttpResponse):status_code = 403class HttpResponseNotAllowed(HttpResponse):status_code = 405def __init__(self, permitted_methods, *args, **kwargs):super().__init__(*args, **kwargs)self["Allow"] = ", ".join(permitted_methods)def __repr__(self):return "<%(cls)s [%(methods)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,"methods": self["Allow"],}class HttpResponseGone(HttpResponse):status_code = 410class HttpResponseServerError(HttpResponse):status_code = 500class Http404(Exception):passclass JsonResponse(HttpResponse):"""An HTTP response class that consumes data to be serialized to JSON.:param data: Data to be dumped into json. By default only ``dict`` objectsare allowed to be passed due to a security flaw before ECMAScript 5. Seethe ``safe`` parameter for more information.:param encoder: Should be a json encoder class. Defaults to``django.core.serializers.json.DjangoJSONEncoder``.:param safe: Controls if only ``dict`` objects may be serialized. Defaultsto ``True``.:param json_dumps_params: A dictionary of kwargs passed to json.dumps()."""def __init__(self,data,encoder=DjangoJSONEncoder,safe=True,json_dumps_params=None,**kwargs,):if safe and not isinstance(data, dict):raise TypeError("In order to allow non-dict objects to be serialized set the ""safe parameter to False.")if json_dumps_params is None:json_dumps_params = {}kwargs.setdefault("content_type", "application/json")data = json.dumps(data, cls=encoder, **json_dumps_params)super().__init__(content=data, **kwargs)

看源码可以看到,HttpResponse继承HttpResponseBase。

主要属性和方法

HttpResponse的主要属性和方法如下:

属性/方法 描述
has_header(header) 是否忽略请求头的大小写限制
setdefault(header, value) 设置响应头数据
set_cookie(key, value, **kwargs) 设置cookie数据
set_signed_cookie(key, value, **kwargs) 设置安全的cookie数据
delete_cookie(key, path=‘/’, domain=None) 删除指定key的cookie数据
write(content) 向客户端写入返回的数据,可以追加
flush(h) 刷新响应数据,数据全部返回

HttpResponse子类扩展

同时,针对响应对象,根据不同的场景,Django封装了一些子类扩展其功能,如下表:

子类类型 描述
HttpResponseRedirect 请求重定向,状态码302
HttpResponsePermanentRedirect 请求永久重定向,状态码301
HttpResponseNotModified 请求资源未修改,状态码304
HttpResponseBadRequest 错误的请求,状态码400
HttpResponseNotFound 请求资源未找到,状态码404
HttpResponseForbidden 请求资源禁止访问,状态码403
HttpResponseNotAllowed 请求不允许,状态码405
HttpResponseGone 请求资源已失效,状态码410
HttpResponseServerError 服务器内部错误,状态码500

特殊响应类型

此外,Django还提供了一些特定场景下的特殊响应类型

响应类型 描述
JsonResponse 返回json数据
StreamingHttpResponse 返回流式数据,如用于大文件的返回
FileResponse 返回j文件数据

实际案例

以我们前的博客项目为例,继续完善我们的代码。

1.完善注册登录功能

在注册和登录函数上进行访问请求的限制,只允许GET和POST请求。

重构用户子项目的视图处理模块personal_blog/author/views.py,代码如下:

from django.core.handlers.wsgi import WSGIRequest
from django.http import HttpResponse
from django.shortcuts import render
from django.views.decorators.http import require_http_methodsfrom .models import Author@require_http_methods(['GET', 'POST'])
def author_register(request: WSGIRequest) -> HttpResponse:"""作者注册"""if request.method == "GET":return render(request, 'author/register.html', {})elif request.method == "POST":# 获取前端传递的数据username = request.POST.get('username')password = request.POST.get('password')realname = request.POST.get('realname')# 判断账号是否注册过authors = Author.objects.filter(username=username)if len(authors) > 0:return render(request, 'author/register.html',{'msg_code': 0, 'msg_info': '账号已经存在,请使用其他账号注册'})# 创建作者对象author = Author(username=username, password=password, realname=realname)author.save()# 返回登录页return render(request, 'author/login.html',{'msg_code': 0, 'msg_info': '账号注册成功'})@require_http_methods(['GET', 'POST'])
def author_login(request: WSGIRequest) -> HttpResponse:"""作者登录"""if request.method == "GET":return render(request, 'author/register.html', {})else:username = request.POST.get('username')password = request.POST.get('password')# 查询是否存在该用户try:author = Author.objects.get(username=username, password=password)# 登录成功,记录登录状态request.session['author'] = author# 跳首页print("----", request.session)return render(request, 'author/index.html',{'msg_code': 0, 'msg_info': '登录成功'})except:# 登录失败return render(request, 'author/login.html',{'msg_code': -1, 'msg_info': '账号或密码错误,请重新登录'})

2.完善博客首页

用户登录后会跳转到博客首页。

把首页文件index.html移到很目录下的静态文件下下personal_blog/html_file/index,html,并完善登录的视图函数,修改返回主页路径,代码如下:

@require_http_methods(['GET', 'POST'])
def author_login(request: WSGIRequest) -> HttpResponse:"""作者登录"""if request.method == "GET":return render(request, 'author/register.html', {})else:username = request.POST.get('username')password = request.POST.get('password')# 查询是否存在该用户try:author = Author.objects.get(username=username, password=password)# 登录成功,记录登录状态request.session['author'] = author# 跳首页,静态文件的路径在配置文件指定后,可以不写绝对路径print("----", request.session)return render(request, 'index.html',{'msg_code': 0, 'msg_info': '登录成功'})except:# 登录失败return render(request, 'author/login.html',{'msg_code': -1, 'msg_info': '账号或密码错误,请重新登录'})

3.完善查询所有文章的功能

用户登录后,可以查询博客中所有用户发表的所有文章,文章默认按照发表时间降序排列。

首先完善文章子项目中的models.py模块的Article模型的Meta信息:

class Article(models.Model):"""文章类型"""...class Meta:# 数据排序ordering = ['-pub_time', 'id']# 类型展示提示信息verbose_name = '文章'verbose_name_plural = verbose_name# 添加索引支持indexes = [models.Index(fields=['title'])]

然后在文章子目录下建存放html网页的文件夹,创建展示所有文章的html网页,编写文件personal_blog/article/templates/article/articles.html,代码如下:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>所有文章列表</title>
</head>
<body><h1>用户查询所有文章列表</h1><div>要查询的文章列表</div>
</body>
</html>

然后完善查询所有文章的视图函数,编写文件personal_blog/article/views.py,修改article_list视图代码如下:

@gzip_page
def article_list(request):"""查询指文章列表"""articles = get_list_or_404(Article)# 指定展示页面为article/articles.html# 同时返回自定义操作结果错误码res_code和res_msg,以及展示的文章数据articlesreturn render(request, 'article/articles.html',{'res_code': '200','res_msg': '查看所有文章','articles': articles})

然后添加路由,编写文件personal_blog/article/urls.py,代码如下:

from django.urls import path, re_path
from . import views# 路由模块名称
app_name = 'article'# 添加路由配置
urlpatterns = [path('article_list/', views.article_list, name='article_list'),...
]

4.完善查询指定编号的文章功能

创建静态文件personal_blog/article/templates/article/article.html,代码如下:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>所有文章详情</title>
</head>
<body><h1>查询指定文章详情</h1><div>展示文章信息: {{ article.title }}</div>
</body>
</html>

编写文件personal_blog/article/views.py,完善单篇文章详情视图:

@gzip_page
def article_detail(request, article_id):"""查询指定编号的文章,article_id变量会接受路由中传递的数据"""print("查询指定编号的文章,编号:%s" % article_id)article = get_object_or_404(Article, pk=article_id)return render(request, 'article/article.html',{'res_code': '200','res_msg': '查看文章详情','article': article})

添加路由,编写文件personal_blog/article/urls.py,代码如下:

from django.urls import path, re_path
from . import views# 路由模块名称
app_name = 'article'# 添加路由配置
urlpatterns = [...re_path('^articles/<int:article_id>/', views.article_detail, name='article_detail'),
]

查询所有文章,完善文章的查询链接功能,代码如下:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>所有文章列表</title>
</head>
<body><h1>用户查询所有文章列表</h1><div>要查询的文章列表</div><!-- articles.0表示获取从后台传递的articles列表中的第一项文章对象数据,id是文章对象的编号属性 --><a href="/article/{{articles.0.id}}/detail/">查看第一篇文章详情</a>
</body>
</html>

5.完善查询作者的所有文章功能

静态页面还是使用前面文章列表的articles.html。

完善personal_blog/article/views.py中articles_author视图函数,代码如下:

@gzip_page
def articles_author(request, author_id):'''查询指定作者的所有文章'''# 查询得到作者对象author = get_object_or_404(Author, pk=author_id)# 查询作者的所有文章articles = author.article_set.all()return render(request, 'article/articles.html',{'res_code': '200','res_msg': '查看作者的所有文章','articles': articles,'f_index': 'none', 'f_main': 'none', 'f_message': 'none', 'f_article': 'active'})

完善路由如下:

from django.urls import path, re_path
from . import views# 路由模块名称
app_name = 'article'# 添加路由配置
urlpatterns = [...# 查询指定作者的文章path('<uuid:author_id>/', views.articles_author, name='articles_author'),
]

打开博客首页文件personal_blog/html_file/index.html,添加查询作者文章的链接:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>博客首页</title>
</head>
<body><h1>博客首页</h1><h3><a href="/article/{{request.session.author.id}}/">查看作者文章</a></h3>{{ message }}
</body>
</html>

还有其他功能完善这里不再展示。

Django(8):请求对象和响应对象相关推荐

  1. Django视图之HttpRequest对象和HttpResponse对象

    五.HttpRequest对象 回想一下,利用HTTP协议向服务器传参有几种途径? 提取URL的特定部分,如/weather/beijing/2018,可以在服务器端的路由中用正则表达式截取: 查询字 ...

  2. python中response对象的属性_Django 中的响应对象 Response

    视图在接收请求并处理后,必须返回HttpResponse对象或子对象.HttpRequest对象由Django创建,HttpResponse对象由开发人员创建. 一.HttpResponse: 可以使 ...

  3. Servlet+常用头信息+响应状态码+响应对象

    一.Servlet 1.概念 什么是Servlet? 1.Servlet 是 java EE 的规范之一.也就是接口.Servlet 接口定义了一套网络请求的规范2.Servlet 是 javaweb ...

  4. 设置响应对象的编码格式

    设置响应对象的编码格式 爬虫爬取到的数据乱码? 方式一:在发送请求完获取响应对象response对象后,需要对response对象设置编码格式 response.encoding = response ...

  5. Django请求响应对象

    请求与响应对象 HttpRequest HttpRequest存储了客户请求的相关参数和一些查询方法. path 请求页面的全路径,不包括域名-例如, "/hello/". met ...

  6. Django请求和响应对象

    Django请求和响应对象 Django 使用请求和响应对象在系统中传递状态. 当一个页面被请求时,Django 会创建一个 HttpRequest 对象,这个对象包含了请求的元数据.然后,Djang ...

  7. WEB HTTP:浏览器HTTP协议漫谈、请求对象Httprequest、响应对象HttpResponse、浏览器内部工作原理(待完善)

    0 系列目录 WEB请求处理 WEB请求处理一:浏览器请求发起处理 WEB请求处理二:Nginx请求反向代理 WEB请求处理三:Servlet容器请求处理 WEB请求处理四:Tomcat配置实践 WE ...

  8. 响应输出HTML处理,JSP response对象:响应客户端的请求并向客户端输出信息

    response 对象和 request 对象相对应,用于响应客户请求,向客户端输出信息.response 是 HttpServletResponse 的实例,封装了 JSP 产生的响应客户端请求的有 ...

  9. CakePHP 2.x CookBook 中文版 第五章 控制器 之 请求和响应对象

    请求和响应对象 在 CakePHP 2.0 中请求和响应对象是新的.在之前的版本中,这些对象是用数组来表示的,相关的方法分散在RequestHandlerComponent.Router. Dispa ...

最新文章

  1. php好的mvc中index方法,创建一个mvc应用目录架构并创建入口文件index.php
  2. Python读写文件 - 转
  3. SDOI2014 LIS
  4. MOBA项目问题记录
  5. jpeg6 安装问题!
  6. CodeForces - 427D Match Catch(后缀数组/广义后缀自动机)
  7. 图解MySQL5.5详细安装与配置过程
  8. Ubuntu系列10.04、11.04、12.04等虚拟机中安装VMware Tools
  9. Python 爬取张国荣最火的 8 首歌,60000 评论看完泪奔!
  10. Kotlin协程简介(一) Hello,coroutines!
  11. mysql 连边聚合_MySQL分组,聚合函数,连表查询,子查询
  12. new Random().nextInt
  13. htc思想[second]
  14. pythoncss50使用教程_Python学习(二十) —— 前端之CSS
  15. nachos中文教程java_Nachos实验环境搭建
  16. Struts 2的基石——拦截器(Interceptor)详细讲解
  17. A JNI error has occurred, please check your installation and try again
  18. 怎样做一个拥有全局观的人
  19. python起笔落笔_书法讲究的是起笔和落笔落是什么意思
  20. Office 2016安装失败解决方案

热门文章

  1. Windows用ffmpeg批量自动转换视频格式(以下以ts转mp4为例)
  2. WebGIS开发总结
  3. 小学生三分钟学会Python程序语法元素分析~总结笔记
  4. Java知识体系最详细总结(2020版)
  5. 2022-05-28克隆节点cloneNode
  6. Linux中echo $命令的作用
  7. ROS的vlp16、RS-Ruby Lite和FLIR Bliackfly S驱动配置+Autoware-AI安装
  8. string数组定义
  9. 每天定时获取必应每日一图并保存做壁纸
  10. java遍历是什么意思_详解java中多种通用遍历方式