デプロイ時にプロジェクトに必要なライブラリを漏れなくインストールする
結論:pip freezeを使う
サーバーに必要なライブラリをインストールするために開発環境でpip freezeまたはpip listを使い確認しては、サーバーでライブラリをインストールしてきたが一気にインストールしてしまったほうが良い。
pip freeze > requirements.txt
これでテキストファイルができるので、サーバにて以下を実行すれば一気にライブラリをインストールできる。
pip install -r requirements.txt
ドキュメント:https://pip.pypa.io/en/latest/cli/pip_freeze/#examples
django:PCとスマホで表示を変えたい
ユーザーのデバイスによって表示を変更したい
表示方法としてレスポンシブデザインが可能なBootstrapを利用してきた。しかしレスポンシブデザインでは納得いく表現ができなかった。
デバイスによって異なるテンプレートを使い分けれればより良くなると思われる。実現できるか調査し実装できれば実装してみたい。
ユーザーのデバイスはuser-agentsを利用して捕捉することができるようだ。
https://pypi.org/project/user-agents/
その他にdjango-user-agentsなるものもあった。これも同じことが実現できそうか?
https://pypi.org/project/django-user-agents/
そもそも標準ライブラリのplatformを使えば対処できるのでは?という疑問も出てくる。何か分かったら更新する。
platformを使う案について
platformではwebアプリにアクセスするユーザーデータ(どんなデバイスを使っているか)を補足することはできない
"実行中プラットフォームの固有情報を参照する"機能を提供するのがplatformなので、これを使ってもdjangoが動くマシンの情報を得るだけになってしまう。だからユーザーのデバイス情報を取得できない。
デバイスによってテンプレートを変える
django-user-agentsを使う案
django-user-agentsを使うと手軽にテンプレートをデバイスによって使い分けられるようになった。テンプレートの使い分けはviews.pyでユーザのデバイスの種類を特定し、デバイスの種類によってrenderでテンプレートを使い分ける。
その他に同一のテンプレートにスマホ、PC用の内容を記述する方法もある。この場合はdjango-user-agentsのテンプレートタグである
{% load user_agents %}や{% if request|is_pc %}等を使って実装する。これはデバイスごとに複数のテンプレートを準備するviews.pyのデメリットを解消できる。
下記はviews.pyにdjango-user-agentsを使う方法である。
from django_user_agents.utils import get_user_agent class ProductListView(View): def get(self, request, *args, **kwargs): object_list = Product.objects.all() context["object_list"] = object_list user_agent = get_user_agent(request) if user_agent.is_mobile: return render(request, "products/list_mobile.html", context) elif user_agent.is_tablet: return render(request, "products/list_tablet.html", context) else: return render(request, "products/list_pc.html", context)
django-user-agentsを使うとミドルウェアを通してrequestに属性を付け加えられる。これを利用してユーザーのデバイスを特定することができる。
インストール方法
pip install pyyaml ua-parser user-agents pip install django-user-agents
場合によっては以下もインストールが必要。
pip install python-memcached
参考:https://stackoverflow.com/questions/41575601/importerror-no-module-named-memcache-django-project
settings.pyの設定項目
INSTALLED_APPS = ( # Other apps... 'django_user_agents', ) # キャッシュを使うとdjango-user-agentsを素早く働かせることができる CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': '127.0.0.1:11211', } } # キャッシュを使わない場合はNoneと記述。キャッシュを使う場合は基本的には'default'を使う USER_AGENTS_CACHE = 'default' # ミドルウェアにも下記を加える MIDDLEWARE_CLASSES = ( # other middlewares... 'django_user_agents.middleware.UserAgentMiddleware', )
postgresのパスワードが分からない場合
postgresのパスワードが分からない場合
いろんな方のブログを見ていると、postgresqlをインストールすると同時にpostgresユーザーが作成される。そしてpostgresのパスワードも設定することになる、と書いてあるが、自分の場合postgresのパスワードを定める機会がなかった。パスワードを定める機会がなかったり、パスワードを忘れてしまった場合にはデータベースに接続できなくなる。このときの対処法を記録しておく。
手順
まずデータベース接続に無条件で接続できるように認証設定ファイルpg_hba.confを編集してpostgresqlに接続する。接続後、postgresのパスワードを再設定する。
無条件で接続するには, local all all trustに変更し、postgresqlを再起動すればよい。postgresqlがあるローカル環境ならパスワードなしで接続できるようになる。
次にpostgresのパスワード再設定だけれど、これはpostgresに接続した状態で以下のコマンドを実行すれば良い。
ALTER ROLE postgres WITH PASSWORD '新しいパスワード';
結果にALTER ROLEと表示されれば再設定できた。
このあとは再びpg_hba.confでlocal all all md5に戻してpostgresql(postgresql-11)を再起動してあげれば良い。
Bootstrapを見る
ドキュメント
https://getbootstrap.jp/docs/4.2/layout/grid/
レスポンシブレイアウト
レスポンシブレイアウトを構築するための5つの階層が定義されている。(extra small,small,medium,large,exyra large)
デバイスを問わず、同じグリッドの場合に使いたいとき、class="col", class="col-*"を使う。
知りたいこと
カード横の空白を無くす方法
django:sendgridでメール本文において&が&に変換されてしまう件について
状況
Checkクラスにはurlを格納する属性があり、一定時間ごとに各インスタンスurlを利用し、ウェブサイトにアクセスする。そして特定の情報が存在した場合にsendgridを使って自分のメールに送信する仕組みを設けた。メール本文にはアクセスしたurlを載せる。メール本文の作成は大まかに以下のようになっている。
from django.core.mail import send_mail from django.template.loader import render_to_string class CheckView(View): def get(self, request, *args, **kwargs): ・・・ check_objs = Check.objects.all() for obj in check_objs: url = obj.url ・・・ subject = "チェック結果" context = { "url" : url} message = render_to_string("mytemplate.txt", context) send_mail( subject, message, ["mynamesasasasa@gmail.com",], fail_silently=False, ) ・・・
この状況でurlの&部分がすべて& amp;に変換されてしまった。
この問題の対処
この原因がどこによるモノなのか分からなかった。sendgridの諸設定によるものかもしれないし、djangoによるものなのかもしれない。
いろいろ試した結果、メール本文にはテンプレートと変数のレンダリングする方式ではなく、そのまま文字列(url)を送る方法を採用した。これにより&の&変換問題が解消された。
コードは以下のような感じになる。
from django.core.mail import send_mail from django.template.loader import render_to_string class CheckView(View): def get(self, request, *args, **kwargs): ・・・ check_objs = Check.objects.all() for obj in check_objs: url = obj.url ・・・ subject = "チェック結果" # context = { "url" : url} ←削除 message = url send_mail( subject, message, ["mynamesasasasa@gmail.com",], fail_silently=False, ) ・・・
この結果sendgridから送ったメールがそのまま送ることができたので、原因はdjango側にある可能性が高いと推定した。
今回の原因と周辺情報
https://docs.djangoproject.com/ja/2.2/ref/templates/language/#automatic-html-escaping
要点にまとめると、djangoにはクロスサイトスクリプティング対策がされており、テンプレートレンダリングする場合には変数タグの出力を自動的にエスケープする仕様になっている。つまり常時エスケープ機能がonになっているので、今回の件で別案の解決方法としてエスケープ機能をオフにするときだけ設定する方法もあった。
エスケープ機能については具体的にはエスケープのオフにはエスケープオフの範囲によってタグを使い分ける。
変数単位でエスケープする場合、テンプレートまたはテンプレートの部分にエスケープする場合の2パターンがある。 前者は{{ data | safe }}とすればエスケープ機能がオフになる。後者はテンプレート内で{% autoescape off %}と{% endautoescape %}を挟んだ範囲が機能オフになる。
クロスサイトスクリプティングやセキュリティについて理解を深めたいならば、『体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 脆弱性が生まれる原理と対策の実践』を読むと良いだろう。
python:自作モジュールのインポートについて
自作モジュール内でインポートをするのに困ったので解決方法をメモしておく。
そもそもimportするには?
ディレクトリ以下にあるファイルをインポートすることができる。
MY_DIR----- myscript.py L--- myconfig.py L--- init.py
カレントディレクトリがMY_DIRにある時、myscript.py,myconfig.py,init.py内の変数や関数をfrom myscript import var みたいにインポートできる。カレントディレクトリ以下にないpyファイルをインポートする場合はインポートするパスを追加することで対応できる。
具体的には
パスに追加する方法はsys.path.append(パス)で追加できる。
+-----B_DIR------ function.py | L--- settings.py A_DIR | +--- MY_DIR----- myscript.py L--- myconfig.py L--- init.py
例えばfunction.pyの関数をmyscript.py内に呼び出す際には
myscript.pyにて import os, sys sys.sppend(os.path.join(os.path.dirname(os.path.dirname(__file__)),"B_DIR")) from function import func
こうすればB_DIRのパスを追加できるのでインポートすることができる。
python:画像の保存
どうすれば画像を保存できるか書いておく。
データの種類
画像の保存にはバイナリタイプのデータが求められる。urllib3でurlにアクセスするとデフォルトでバイナリタイプの戻り値なので、画像を保存するには向いている。
保存方法
保存方法はファイルをバイナリ形式でバイナリデータを書き込めばよい。したがって以下のような形をとる。
# requestsを使った場合 import requests res = requests.get(url).content with open(IMAGE_FILE, "wb") as file: file.write(res) # urllib3を使った場合 import urllib3 res = urllib3.urlopen(url) with open(IMAGE_FILE, "wb") as file: file.write(res)
seleniumでもバイナリデータで情報を取得できるので画像取得はできる。 https://kurozumi.github.io/selenium-python/api.html#selenium.webdriver.remote.webdriver.WebDriver.get_screenshot_as_png
python: windowsとmacから同一のスクリプトを利用する方法
前提
問題点
windowsとmacではcsvモジュールを使う場合以下の差異が存在する。
1.ファイルパスが異なる。
2.csvファイル書き込みの際にlineterminatorが必要か否か。
1.2も今まではwindows用、mac用のスクリプトを別々に準備し使うOSによってスクリプトを使い分けてきた。今回はこの問題を解決する。
import csv, platform pl = platform.system() if pl == 'Windows': # windowsファイルパス CSV_FILE = "C*************.csv" elif pl == 'Darwin': # macファイルパス CSV_FILE = "Users/********.csv" test_list = ["ロシア", "アメリカ", "中国", "ブラジル"] with open("path/test.csv", "a", encoding="utf-8") as f: if pl == 'Windows': writer = csv.writer(f, lineterminator="\n") elif pl == 'Darwin': writer = csv.writer(f) writer.writerow(test_list)
解決方法
見ればわかる。上記のようにplatformモジュールを使えばよい。
https://docs.python.org/ja/3/library/platform.html#platform.system
Python:CSVモジュール使い方
関連記事
csvモジュールの使い方
リストデータをファイルとしてアウトプットしたい場合にcsvファイルにするのは便利。 csvモジュールの使い方をメモしておく。
csvモジュールはpythonの標準ライブラリなのでpip install csvのようなことはせずに利用することができる。
https://docs.python.org/ja/3/library/csv.html
import csv
使用例 import csv test_list = ["ロシア", "アメリカ", "中国", "ブラジル"] with open("path/test.csv", "a", encoding="utf-8") as f: writer = csv.writer(f, lineterminator="\n") writer.writerow(test_list)
csv.writer()関数を使うとwriterオブジェクトを返す。このwriterオブジェクトにはcsvファイルに書き込むためのメソッドがある。wirerow()メソッド又はwriterows()メソッドである。これらのメソッドに与えた引数がcsvファイルに書き込まれることになる。
writerオブジェクトを作成する
1行書くか複数行書くかのメソッドがある。そのメソッドを使うためのオブジェクトがwriterオブジェクト。 writerで返されるのがwriterオブジェクトである。
csv.writer(ファイルオブジェクト)
1行書く場合
writer で返されたオブジェクトにwriterowメソッドを使う。
w.writerow(シーケンス)
複数行書く場合
writer で返されたオブジェクトにwriterowsメソッドを使う。
w.writerows(シーケンス)
csvファイルの読み込み
csvファイルの一行目がカラムの説明になる場合がある。読み込みの際にこの一行目を省いて2行目以降のデータのみ読み込む方法を調べた。 https://docs.python.jp/3/library/csv.html#reader-objects http://www.bokupy.com/detail/67
import csv with open("book1.csv", "r") as f: reader = csv.reader(f) for row in reader: if reader.line_num == 1: continue print(row)
重要な点として、reader.line_numを使うことのほか、continueも重要である。continueはfor構文中にcontinueに出くわしたとき、今の現在のループを中断し、次の繰り替し処理を行う動作になる。
urllib3についてメモ
document
https://urllib3.readthedocs.io/en/latest/index.html
コードの流れを追う
import urllib3 #1 http = urllib3.PoolManager() #2 r = http.request('GET', 'http://httpbin.org/robots.txt') #3
#1
import urllib3
https://github.com/urllib3/urllib3/blob/master/src/urllib3/__init__.py#L11
urllib3をインポートしたときにディレクトリ内の__init__.pyでPoolManagerもインポートされる。
#2
http = urllib3.PoolManager()
urllib3.PoolManagerのインスタンスhttpを生成する。これでrequestメソッドの呼び出しの準備になる。
#3
r = http.request('GET', 'http://httpbin.org/robots.txt')
PoolManager.request()を実行しているが、正確にはPoolManagerの親クラスRequestMethod.request()メソッドを実行している。
https://github.com/urllib3/urllib3/blob/master/src/urllib3/request.py#L10
urllib3.request.RequestMethods(object).request()
def request(self, method, url, fields=None, headers=None, **urlopen_kw): """ Make a request using :meth:`urlopen` with the appropriate encoding of ``fields`` based on the ``method`` used. This is a convenience method that requires the least amount of manual effort. It can be used in most situations, while still having the option to drop down to more specific methods when necessary, such as :meth:`request_encode_url`, :meth:`request_encode_body`, or even the lowest level :meth:`urlopen`. """ method = method.upper() urlopen_kw["request_url"] = url if method in self._encode_url_methods: return self.request_encode_url( method, url, fields=fields, headers=headers, **urlopen_kw ) else: return self.request_encode_body( method, url, fields=fields, headers=headers, **urlopen_kw )
request()メソッドでは、methodが_encode_url_methodsに含まれているものならばurllib3.request.RequestMethods(object).request_encode_url()メソッドを実行する。具体的に言えばリクエストメソッドが"DELETE", "GET", "HEAD", "OPTIONS"のどれかであれば、RequestMethods(object).request_encode_url()メソッドを実行する。一方で_encode_url_methodsにmethodがない場合は、urllib3.request.RequestMethods(object).request_encode_body()メソッドを実行する。
_encode_url_methodsはセット型データでRequestMethods(object)の属性である。_encode_url_methods = {"DELETE", "GET", "HEAD", "OPTIONS"}
https://github.com/urllib3/urllib3/blob/master/src/urllib3/request.py#L39
urllib3.request.RequestMethods(object).request_encode_url()
def request_encode_url(self, method, url, fields=None, headers=None, **urlopen_kw): """ Make a request using :meth:`urlopen` with the ``fields`` encoded in the url. This is useful for request methods like GET, HEAD, DELETE, etc. """ if headers is None: headers = self.headers extra_kw = {"headers": headers} extra_kw.update(urlopen_kw) if fields: url += "?" + urlencode(fields) return self.urlopen(method, url, **extra_kw)
urllib3.poolmanager.PoolManager(RequestMethods).urlopen()
def urlopen(self, method, url, redirect=True, **kw): """ Same as :meth:`urllib3.connectionpool.HTTPConnectionPool.urlopen` with custom cross-host redirect logic and only sends the request-uri portion of the ``url``. The given ``url`` parameter must be absolute, such that an appropriate :class:`urllib3.connectionpool.ConnectionPool` can be chosen for it. """ u = parse_url(url) conn = self.connection_from_host(u.host, port=u.port, scheme=u.scheme) kw["assert_same_host"] = False kw["redirect"] = False if "headers" not in kw: kw["headers"] = self.headers.copy() if self.proxy is not None and u.scheme == "http": response = conn.urlopen(method, url, **kw) else: response = conn.urlopen(method, u.request_uri, **kw) redirect_location = redirect and response.get_redirect_location() if not redirect_location: return response # Support relative URLs for redirecting. redirect_location = urljoin(url, redirect_location) # RFC 7231, Section 6.4.4 if response.status == 303: method = "GET" retries = kw.get("retries") if not isinstance(retries, Retry): retries = Retry.from_int(retries, redirect=redirect) # Strip headers marked as unsafe to forward to the redirected location. # Check remove_headers_on_redirect to avoid a potential network call within # conn.is_same_host() which may use socket.gethostbyname() in the future. if retries.remove_headers_on_redirect and not conn.is_same_host( redirect_location ): headers = list(six.iterkeys(kw["headers"])) for header in headers: if header.lower() in retries.remove_headers_on_redirect: kw["headers"].pop(header, None) try: retries = retries.increment(method, url, response=response, _pool=conn) except MaxRetryError: if retries.raise_on_redirect: raise return response kw["retries"] = retries kw["redirect"] = redirect log.info("Redirecting %s -> %s", url, redirect_location) return self.urlopen(method, redirect_location, **kw)
conn = self.connection_from_host(u.host, port=u.port, scheme=u.scheme)のconnはHTTP(s)ConnectionPoolインスタンスである。
HTTP(s)ConnectionPool.urlopen()を実行するとresponseが得られる。このconnオブジェクトを得るには以下のような複数のメソッドを実行してreturn poolとして得る。
https://github.com/urllib3/urllib3/blob/master/src/urllib3/poolmanager.py#L213
def connection_from_host(self, host, port=None, scheme="http", pool_kwargs=None): """ Get a :class:`ConnectionPool` based on the host, port, and scheme. If ``port`` isn't given, it will be derived from the ``scheme`` using ``urllib3.connectionpool.port_by_scheme``. If ``pool_kwargs`` is provided, it is merged with the instance's ``connection_pool_kw`` variable and used to create the new connection pool, if one is needed. """ if not host: raise LocationValueError("No host specified.") request_context = self._merge_pool_kwargs(pool_kwargs) request_context["scheme"] = scheme or "http" if not port: port = port_by_scheme.get(request_context["scheme"].lower(), 80) request_context["port"] = port request_context["host"] = host return self.connection_from_context(request_context)
https://github.com/urllib3/urllib3/blob/master/src/urllib3/poolmanager.py#L236
def connection_from_context(self, request_context): """ Get a :class:`ConnectionPool` based on the request context. ``request_context`` must at least contain the ``scheme`` key and its value must be a key in ``key_fn_by_scheme`` instance variable. """ scheme = request_context["scheme"].lower() pool_key_constructor = self.key_fn_by_scheme[scheme] pool_key = pool_key_constructor(request_context) return self.connection_from_pool_key(pool_key, request_context=request_context)
https://github.com/urllib3/urllib3/blob/master/src/urllib3/poolmanager.py#L249
def connection_from_pool_key(self, pool_key, request_context=None): """ Get a :class:`ConnectionPool` based on the provided pool key. ``pool_key`` should be a namedtuple that only contains immutable objects. At a minimum it must have the ``scheme``, ``host``, and ``port`` fields. """ with self.pools.lock: # If the scheme, host, or port doesn't match existing open # connections, open a new ConnectionPool. pool = self.pools.get(pool_key) if pool: return pool # Make a fresh ConnectionPool of the desired type scheme = request_context["scheme"] host = request_context["host"] port = request_context["port"] pool = self._new_pool(scheme, host, port, request_context=request_context) self.pools[pool_key] = pool return pool
戻り値はpolであるがこれは、HTTPConnectionPool又はHTTPSConnectionPoolクラスのインスタンスである。poolを新しく生成する場合は# Make a fresh ConnectionPool of the desired type以下の部分で生成される。もっと言えば_new_poolメソッドである。
https://github.com/urllib3/urllib3/blob/master/src/urllib3/poolmanager.py#L177
def _new_pool(self, scheme, host, port, request_context=None): """ Create a new :class:`ConnectionPool` based on host, port, scheme, and any additional pool keyword arguments. If ``request_context`` is provided, it is provided as keyword arguments to the pool class used. This method is used to actually create the connection pools handed out by :meth:`connection_from_url` and companion methods. It is intended to be overridden for customization. """ pool_cls = self.pool_classes_by_scheme[scheme] if request_context is None: request_context = self.connection_pool_kw.copy() # Although the context has everything necessary to create the pool, # this function has historically only used the scheme, host, and port # in the positional args. When an API change is acceptable these can # be removed. for key in ("scheme", "host", "port"): request_context.pop(key, None) if scheme == "http": for kw in SSL_KEYWORDS: request_context.pop(kw, None) return pool_cls(host, port, **request_context)
requestsを見てみる
そもそも
requestsはurllib3をもとに作られている。requests.__init__.pyを見ればわかる。それでHTTPリクエストを送る関係のライブラリがpythonには複数ある。
urllib2, urllib(.request), urllib3. requests。これらについて理解するのも後々役に立つだろう。
- urllib2...python2で使われていたもの。python3では使えない。
- urllib(.request)...python3でurllib2の関数を編成して新しい単位にした。
- urllib3...上記とは違い非標準ライブラリ、つまり3rdパーティ製ライブラリ。
- requests... urllib3をもとにさらに使いやすくしたもの。
requestsの中身はどうなっているのか
一つのライブラリを使えるようになるだけなくどのように動いているか調べてみる。
ソース
https://github.com/kennethreitz/requests/tree/master/requests
クラスの整理
requests/models.py
https://github.com/kennethreitz/requests/blob/master/requests/models.py
requests/models.pyでクラスが定義されている。
class RequestEncodingMixin(object) class RequestHooksMixin(object) class Request(RequestHooksMixin) class PreparedRequest(RequestEncodingMixin, RequestHooksMixin) class Response(object)
requests/session.py
class SessionRedirectMixin(object) class Session(SessionRedirectMixin)
requests/adapters.py
class BaseAdapter(object) class HTTPAdapter(BaseAdapter)
requests/cookies.py
class MockRequest(object) class MockResponse(object) class CookieConflictError(RuntimeError) class RequestsCookieJar(cookielib.CookieJar, MutableMapping)
requests.get(URL)に限って言えば、各クラスは以下の関係がある。
再考中。
コードの流れの整理
import requests requests.get(URL)
1. [ import requests ] =======>> [ requests.__init__.pyにてapi.get関数をインポート ] 2. [ requests.get(URL) ] ============ [ get関数を実行 ] 3. [ get関数を実行 ] ===============>> 戻り値[ api.request関数 (の実行) ] 4. [ api.request関数 (の実行) ] =====>> 戻り値[ Sessionインスタンス生成+requestメソッド実行 ] 5. [ Sessionインスタンス生成+requestメソッド実行] ======>> 戻り値[ Responseインスタンス作成 ]
1. [ import requests ] =========>> [ requests.__init__.pyにてapi.get関数をインポート ]
https://github.com/kennethreitz/requests/blob/master/requests/__init__.py#L115
from .api import request, get, head, post, patch, put, delete, options
2. [ requests.get(URL) ] ============ [ get関数を実行 ]
3.[ get関数を実行 ] =========>> 戻り値[ api.request関数 (の実行) ]
def get(url, params=None, **kwargs): kwargs.setdefault('allow_redirects', True) return request('get', url, params=params, **kwargs)
4.[ api.request関数 (の実行) ] ====>> 戻り値[Sessionインスタンス生成+requestメソッド実行]
def request(method, url, **kwargs): with sessions.Session() as session: return session.request(method=method, url=url, **kwargs)
5.[ Sessionインスタンス生成+requestメソッド実行] ======>> 戻り値[ Responseインスタンス作成 ]
(( requestメソッド実行 )) def request(self, method, url, params=None, data=None, headers=None, cookies=None, files=None, auth=None, timeout=None, allow_redirects=True, proxies=None, hooks=None, stream=None, verify=None, cert=None, json=None): # Create the Request. req = Request( method=method.upper(), url=url, headers=headers, files=files, data=data or {}, json=json, params=params or {}, auth=auth, cookies=cookies, hooks=hooks, ) prep = self.prepare_request(req) proxies = proxies or {} settings = self.merge_environment_settings( prep.url, proxies, stream, verify, cert ) # Send the request. send_kwargs = { 'timeout': timeout, 'allow_redirects': allow_redirects, } send_kwargs.update(settings) resp = self.send(prep, **send_kwargs) return resp
Constructs a :class:`Request <Request>`, prepares it and sends it.Returns :class:`Response <Response>` object.
まず最初にRequestインスタンスを作成し、RequestインスタンスからPreparedRequestインスタンスに変換し、それを送信する。その結果Responseインスタンスを得る。
Responseインスタンスを得るのはSession.send()メソッドの実行によってであり、このsend()メソッドはさらに複数のクラスメソッド、関数から構成されている。
requestメソッドに含まれるメソッド、関数を軽く説明
- Session.prepare_request()はRequestインスタンス(req)を引数にPreparedRequestインスタンスを生成する。
https://github.com/kennethreitz/requests/blob/master/requests/sessions.py#L426 - Session.merge_environment_settings()
- DICT.update()
- Session.send()
Session.send()
Session.send()
def send(self, request, **kwargs): """Send a given PreparedRequest. :rtype: requests.Response """ # Set defaults that the hooks can utilize to ensure they always have # the correct parameters to reproduce the previous request. kwargs.setdefault('stream', self.stream) kwargs.setdefault('verify', self.verify) kwargs.setdefault('cert', self.cert) kwargs.setdefault('proxies', self.proxies) # It's possible that users might accidentally send a Request object. # Guard against that specific failure case. if isinstance(request, Request): raise ValueError('You can only send PreparedRequests.') # Set up variables needed for resolve_redirects and dispatching of hooks allow_redirects = kwargs.pop('allow_redirects', True) stream = kwargs.get('stream') hooks = request.hooks # Get the appropriate adapter to use adapter = self.get_adapter(url=request.url) # Start time (approximately) of the request start = preferred_clock() # Send the request r = adapter.send(request, **kwargs) # Total elapsed time of the request (approximately) elapsed = preferred_clock() - start r.elapsed = timedelta(seconds=elapsed) # Response manipulation hooks r = dispatch_hook('response', hooks, r, **kwargs) # Persist cookies if r.history: # If the hooks create history then we want those cookies too for resp in r.history: extract_cookies_to_jar(self.cookies, resp.request, resp.raw) extract_cookies_to_jar(self.cookies, request, r.raw) # Redirect resolving generator. gen = self.resolve_redirects(r, request, **kwargs) # Resolve redirects if allowed. history = [resp for resp in gen] if allow_redirects else [] # Shuffle things around if there's history. if history: # Insert the first (original) request at the start history.insert(0, r) # Get the last request made r = history.pop() r.history = history # If redirects aren't being followed, store the response on the Request for Response.next(). if not allow_redirects: try: r._next = next(self.resolve_redirects(r, request, yield_requests=True, **kwargs)) except StopIteration: pass if not stream: r.content return r
# Get the appropriate adapter to use adapter = self.get_adapter(url=request.url)の部分ではprefixがhttps://なのかhttp://一つに絞っている。
Session.send()メソッド
def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None): """Sends PreparedRequest object. Returns Response object. :param request: The :class:`PreparedRequest` being sent. :param stream: (optional) Whether to stream the request content. :param timeout: (optional) How long to wait for the server to send data before giving up, as a float, or a :ref:`(connect timeout, read timeout) ` tuple. :type timeout: float or tuple or urllib3 Timeout object :param verify: (optional) Either a boolean, in which case it controls whether we verify the server's TLS certificate, or a string, in which case it must be a path to a CA bundle to use :param cert: (optional) Any user-provided SSL certificate to be trusted. :param proxies: (optional) The proxies dictionary to apply to the request. :rtype: requests.Response """ try: conn = self.get_connection(request.url, proxies) except LocationValueError as e: raise InvalidURL(e, request=request) self.cert_verify(conn, request.url, verify, cert) url = self.request_url(request, proxies) self.add_headers(request, stream=stream, timeout=timeout, verify=verify, cert=cert, proxies=proxies) chunked = not (request.body is None or 'Content-Length' in request.headers) if isinstance(timeout, tuple): try: connect, read = timeout timeout = TimeoutSauce(connect=connect, read=read) except ValueError as e: # this may raise a string formatting error. err = ("Invalid timeout {}. Pass a (connect, read) " "timeout tuple, or a single float to set " "both timeouts to the same value".format(timeout)) raise ValueError(err) elif isinstance(timeout, TimeoutSauce): pass else: timeout = TimeoutSauce(connect=timeout, read=timeout) try: if not chunked: resp = conn.urlopen( method=request.method, url=url, body=request.body, headers=request.headers, redirect=False, assert_same_host=False, preload_content=False, decode_content=False, retries=self.max_retries, timeout=timeout ) # Send the request. else: if hasattr(conn, 'proxy_pool'): conn = conn.proxy_pool low_conn = conn._get_conn(timeout=DEFAULT_POOL_TIMEOUT) try: low_conn.putrequest(request.method, url, skip_accept_encoding=True) for header, value in request.headers.items(): low_conn.putheader(header, value) low_conn.endheaders() for i in request.body: low_conn.send(hex(len(i))[2:].encode('utf-8')) low_conn.send(b'\r\n') low_conn.send(i) low_conn.send(b'\r\n') low_conn.send(b'0\r\n\r\n') # Receive the response from the server try: # For Python 2.7, use buffering of HTTP responses r = low_conn.getresponse(buffering=True) except TypeError: # For compatibility with Python 3.3+ r = low_conn.getresponse() resp = HTTPResponse.from_httplib( r, pool=conn, connection=low_conn, preload_content=False, decode_content=False ) except: # If we hit any problems here, clean up the connection. # Then, reraise so that we can handle the actual exception. low_conn.close() raise except (ProtocolError, socket.error) as err: raise ConnectionError(err, request=request) except MaxRetryError as e: if isinstance(e.reason, ConnectTimeoutError): # TODO: Remove this in 3.0.0: see #2811 if not isinstance(e.reason, NewConnectionError): raise ConnectTimeout(e, request=request) if isinstance(e.reason, ResponseError): raise RetryError(e, request=request) if isinstance(e.reason, _ProxyError): raise ProxyError(e, request=request) if isinstance(e.reason, _SSLError): # This branch is for urllib3 v1.22 and later. raise SSLError(e, request=request) raise ConnectionError(e, request=request) except ClosedPoolError as e: raise ConnectionError(e, request=request) except _ProxyError as e: raise ProxyError(e) except (_SSLError, _HTTPError) as e: if isinstance(e, _SSLError): # This branch is for urllib3 versions earlier than v1.22 raise SSLError(e, request=request) elif isinstance(e, ReadTimeoutError): raise ReadTimeout(e, request=request) else: raise return self.build_response(request, resp)
エラー:django.core.exceptions.ImproperlyConfigured: The included URLconf '***.urls' does not appear to have any patterns in it. If you see valid patterns in the file then the issue is probably caused by a circular import.
django.core.exceptions.ImproperlyConfigured: The included URLconf '***.urls' does not appear to have any patterns in it. If you see valid patterns in the file then the issue is probably caused by a circular import.
djangoのエラーでa circular import.と表示されることがよくある。このエラーパターンの原因をメモしておく。
大抵このエラーはurls.pyに不適切なコードが書かれていると表示される。
urlpatterns = [ url('', TemplateView.as_view(template_name="diary/home.html"), name="home"), ]
上記のようにurlpatternsに含める要素はpath(***)である。しかしurl(***)と書いてしまうとエラーが出る。これはconfig.urls.pyよりもアプリケーション側で起こすことが多い。(なぜならconfig側ならadminの例があるのでpathと書く動機になるが、アプリ側はurls.pyをゼロから作り自分で書いてるとミスを起こす確率が高いから)
...apps/views.pyにて from django.core.pagenator import Paginator, Page, PageNotAnInteger
ずっとurls.pyにエラーが有るはずだと考えるとこのミスに気づけない。
上記の例はページネーション関係のクラスをimportする際に記述ミスしている。from django.core.paginator にしないとエラーが出てしまう。