DJango_info
の編集
index.php?DJango_info
[
トップ
] [
編集
|
差分
|
履歴
|
添付
|
リロード
] [
新規
|
一覧
|
検索
|
最終更新
|
ヘルプ
]
-- 雛形とするページ --
(no template pages)
#contents ** インストールとプロジェクトの初期化、アプリの初期化 [#v89c1905] *** インストール [#l192eb6c] [local] munakata:~/source/python/vlman$ pip3 install django [local] munakata:~/source/python/vlman$ python3 -m django --version 2.2.2 *** PYTHONPATH の指定[#lb4d603c] - vscode 上の lint が Django のパッケージが見つけられないというワーニングを出すことを抑止するために明示的に指定 - PYTHONPATH には pip show django を実行して location で指定されたパスを設定する - PYTHONPATH 自体は vscode の設定ファイル (プロジェクトディレクトリ)/.vscode/settings.json 内に記載するのが良さそう *** プロジェクト と アプリ の初期化 [#t5ca0f8e] [local] munakata:~/source/python/vlman$ django-admin startproject vlmanui [local] munakata:~/source/python/vlman/vlmanui$ django-admin startapp file_viewer *** 管理用ユーザーの登録 [#p6099483] [local] munakata:~/source/python/vlman/vlmanui$ python3 manage.py createsuperuser Username (leave blank to use 'munakata'): Email address: magu775@gmail.com Password: Password (again): Superuser created successfully. *** timezone とロケールの設定 (settings.py) [#l8c0ab8b] LANGUAGE_CODE = 'ja' TIME_ZONE = 'Asia/Tokyo' *** インストール完了時のディレクトリー構造 [#n21cb74e] - プロジェクトの下にアプリケーションを追加していく構造 [local] munakata:~/source/python/vlman$ tree . ├── video_top │ ├── sample_A.mp4 ├── vlman.py ├── vlmandb.sqbpro ├── vlmandb.sqlite3 ├── file_viewer <--------- application definition │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ └── __init__.py │ ├── models.py <----------- database access definition │ ├── tests.py │ └── views.py <--------- look & feel definition └── vlmanui <------------ total system settings ├── manage.py └── vlmanui ├── __init__.py ├── settings.py <---------- application directory ├── urls.py <------------ URL entry point definition └── wsgi.py ** django ビルトイン管理ツール(= manage.py) [#i85b1ed8] - ビルトイン Web サーバーの起動 [local] munakata:~/source/python/vlman/vlmanui$ python3 manage.py runserver 8000 *** カスタムコマンドの追加 [#q3ed478e] - BaseCommand クラスもしくはその サブクラス の一つを継承した Command クラスを定義する必要がある - python プログラム名がコマンド名になる(但し実行時に .py は不要) - プログラムを配置する場所は決まっている ( アプリ/management/command/(カスタムコマンド).py ) - プログラム引数は def add_arguments(self, parser): という関数で評価する - プログラム実体は def handle(self, *args, **kwargs): という関数の中に書く - 参考 URL -- [[カスタム django-admin コマンドの実装 (Django 公式マニュアル):https://docs.djangoproject.com/ja/2.2/howto/custom-management-commands/]] -- [[argparse --- コマンドラインオプション、引数、サブコマンドのパーサー (python 公式マニュアル):https://docs.python.org/ja/3/howto/argparse.html#id1]] *** python argument parser (argpaser) [#ebe69f6f] - [[Argparse チュートリアル (python 公式チュートリアル):https://docs.python.org/ja/3/howto/argparse.html#id1]] - [[argparseの引数と設定例について調べてみた:https://kamiya555.github.io/2015/09/26/argparse-args/]] -- [[argparseでコマンドライン引数を使ってみよう!:https://www.sejuku.net/blog/23647]] -- ArgumentParser()の引数 ArgumentParser([description][, epilog][, prog][, usage][, add_help][, argument_default][, parents][, prefix_chars][, conflict_handler][, formatter_class]) |引数|デフォルト値|概要|h |prog|sys.argv[0]|プログラム名 (デフォルト: sys.argv[0])| |usage|パーサーに追加された引数から生成される|プログラムの利用方法を記述する文字列| |description|None|引数のヘルプの前に表示されるテキスト| |epilog|None|引数のヘルプの後で表示されるテキスト| |parents|???|ArgumentParser オブジェクトのリストで、このオブジェクトの引数が追加される| |formatter_class|???|ヘルプ出力をカスタマイズするためのクラス| |prefix_chars|‘-‘|オプションの引数の prefix になる文字集合 (デフォルト: ‘-‘)| |fromfile_prefix_chars|None|追加の引数を読み込むファイルの prefix になる文字集合| |argument_default|None|引数のグローバルなデフォルト値| |conflict_handler|???|衝突するオプションを解決する方法 (通常は不要)| |add_help|True|-h/–help オプションをパーサーに追加する| -- add_argument()の引数 add_argument(name or flags...[, action][, nargs][, const][, default][, type][, choices][, required][, help][, metavar][, dest]) |引数|概要|h |name or flags|名前か、あるいはオプション文字列のリスト (例: foo や -f, –foo)| |action|コマンドラインにこの引数があった時のアクション| |nargs|消費すべきコマンドライン引数の数| |const|一部の action と nargs の組み合わせで利用される定数| |default|コマンドラインに引数がなかった場合に生成される値| |type|コマンドライン引数が変換されるべき型| |choices|引数として許される値のコンテナー| |required|コマンドラインオプションが省略可能かどうか (オプション引数のみ)| |help|引数が何なのかを示す簡潔な説明| |metavar|使用法メッセージの中で使われる引数の名前| |dest|parse_args() が返すオブジェクトに追加される属性名| -- nargsに設定できる値 |設定値|概要|h |N|N 個の引数がコマンドラインから集められ、リストに格納されます| |?|コマンドライン引数が存在しない場合、 default の値が生成されます| |*|全てのコマンドライン引数がリストに集められます| |+|全てのコマンドライン引数がリストに集められます| -- [[agrparse (関数の引数のパーシング関数):https://docs.python.org/ja/3/library/argparse.html]] -- [[ArgumentParserの使い方を簡単にまとめた:https://qiita.com/kzkadc/items/e4fc7bc9c003de1eb6d0]] -- [[Python argparseの使い方:https://qiita.com/dodo5522/items/6ec2b6d83287add6c185]] -- [[ちょっとしたコマンドラインツールをpythonで作る時に便利なargparseの使い方:http://tech.uribou.tokyo/python-argparsenoshi-ifang/]] -- [[Djangoカスタムコマンドを作成し、引数を送る(type にカスタムバリデータ関数を指定する方法):https://shotanuki.com/django%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%82%92%E4%BD%9C%E6%88%90%E3%81%97%E3%80%81%E5%BC%95%E6%95%B0%E3%82%92%E9%80%81%E3%82%8B/]] ** URL ディスパッチャー(パスコンバーターによるリインテリジェントな URL ルーティング機構) [#vfff4005] path('abc/', include('(プロジェクト名).urls', name = 'reverse serch name') -- http://aaa.com/abc/ の場合 abc/ の部分だけ記載する -- include で各アプリの urls.py を読み込ませる rpath(r'^abc*$', include('application name.urls'), name='reverse name') -- 正規表現を使ってパス変換する場合には rpath を使うと python の正規表現(re.)の構文が使える -- name で逆引き( URL をハードコードしないで済むように名前で参照させるために必要) - (プロジェクトルート)/(プロジェクト名)/urls.py <----- 自動で作成される -- プロジェクト全体のルーティング設定を書く - (プロジェクトルート)/(アプリケーションルート)/urls.py <----- 手動で作成する必要あり -- 各アプリケーション毎のルーティング(=view との対応関係) ** Model (データー構造の定義) [#xb793560] *** Django における DB の扱い方(SQL 文は一切使わなくても良い) [#ff0e3b62] - DjangoではORM(オブジェクト関係マッピング)を使ってデータベースを操作 - DjangoのORMの実装は [[ActiveRecord:https://qiita.com/kimioka0/items/8c10e01def23fdbf3aa6]] パターンを採用 -- モデルを設計すればマイグレーション機能でDB定義を自動作成する。 -- SQLの記述は不要。モデルマネージャ経由でクエリを実行するとレコードをモデルのインスタンスとして扱える。 -- クエリは人間に読みやすく命名されたマネージャメソッドの連鎖として表現する - [[Django データベース操作 についてのまとめ:https://qiita.com/okoppe8/items/66a8747cf179a538355b]] |大分類|小分類|メソッド|h |全件取得する||all()| |検索条件を指定|肯定|filter(**kwargs)| |~|NOT条件|exclude(**kwargs)| |~|IN条件の中身をリストで指定する|in_bulk(id_list=None, field_name='pk')| |並び順を指定|昇順|order_by(*fields)| ||降順|reverse()| |クエリセットからインスタンスを取得する|検索に一致したもの|get(**kwargs)| |~|指定した日付順で最新のもの|latest(*fields)| |~|指定した日付順で最古のもの|earliest(*fields)| |~|クエリセットの先頭|first()| |~|クエリセットの最後|last()| |レコードを新規登録する|新規登録|create(**kwargs)| |~|条件に一致するものを取得、無い場合は新規登録して取得|get_or_create(defaults=None, **kwargs)| |~|条件に一致するものを更新、無い場合は新規登録|update_or_create(defaults=None, **kwargs)| |一括登録する||bulk_create(objs, batch_size=None)| |一括更新する||update(**kwargs)| |一括削除する|| delete()| |特定のフィールドを集計する|テーブル内の集計|aggregate(*args, **kwargs)| |参照先テーブルの集計||annotate(*args, **kwargs)| |該当のレコード件数||count()| |抽出したレコードから重複の無い日付配列を作る||dates(field, kind, order='ASC')| |抽出したレコードから重複の無い時刻配列を作る||datetimes(field_name, kind, order='ASC', tzinfo=None)| |クエリセットの存在チェックをする||exists()| |インスタンスの一部だけを取得|結果を辞書のリストで取得する|values(*fields, **expressions)| |結果をタプルのリストで取得する||values_list(*fields, flat=False, named=False)| |特定のフィールドの重複無しリストを作る||distinct(*fields)| |SQLを直接実行する|SQLを直に設定する(一部)|extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)| |~|SQLを直に設定する(全部)|raw(raw_query, params=None, translations=None)| |クエリセット同士の集合演算をする|和集合|union(*other_qs, all=False)| |~|積集合|intersection(*other_qs)| |~|差集合|difference(*other_qs)| |パフォーマンスチューニング キャッシング|SQLのJOIN句を使って関係先を同時に取得しキャッシュする。1-1,N-1で有効|select_related(*fields)| |~|関係先を先行取得してキャッシュする。1-N、N-Nで有効。|prefetch_related(*lookups)| |パフォーマンスチューニング 重いカラムを取得しない|指定カラムだけ取得しない|defer(*fields)| ||指定カラムだけ取得する|only(*fields)| |パフォーマンスチューニング 巨大なテーブルをループで扱う||iterator(chunk_size=2000)| |データベースのレコードロックを使う||select_for_update(nowait=False, skip_locked=False, of=())| |デフォルト以外のデータベースを使う||using(alias)| |空のクエリセットを作る||none()| *** Django 外で作成した DB を利用するには制約がある場合も [#u820f8f6] - &color(red){DJANGO で利用するデーターベースは model の中で予め紐付けて定義する必要がある}; - [[モデルフィールド 設定テンプレート:https://qiita.com/okoppe8/items/a1149b2be54441951de1]] - &color(red){DJANGO からデーターベースを操作する場合には、専用に用意された記述を用いる。ほぼ SQL 文を書くことは無い}; -- [[データベース操作まとめ:https://e-tec-memo.herokuapp.com/article/10/]] *** model と DB の同期処理 (=migration) [#z77f6ca2] - &color(red){モデル変更時には以下のコマンドでデータベースに更新を反映する}; -- python3 manage.py makemigrations (アプリケーション名) -- python3 manage.py migrate [local] munakata:~/source/python/vlman/vlmanui$ python3 manage.py makemigrations Migrations for 'fview': fview/migrations/0001_initial.py - Create model Filedir [local] munakata:~/source/python/vlman/vlmanui$ python3 manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, fview, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying fview.0001_initial... OK Applying sessions.0001_initial... OK - migration のリセット -- アプリケーションディレクトリーにある migrations というフォルダーを削除 -- キャッシュデータのクリア (vman) munakata@muna-E450:~/code/python/vman/vmanhmi$ python manage.py shell Python 3.6.8 (default, Jan 14 2019, 11:02:34) [GCC 8.0.1 20180414 (experimental) [trunk revision 259383]] on linux Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from django.core.cache import cache >>> cache.clear() -- データーベースの中の django_migrations というテーブルから、該当アプリケーションのレコードを削除する sqlite> select * from django_migrations; <------- 該当アプリの変更履歴が記録されている sqlite> delete from django_migrations where app='fview (=アプリ名)'; sqlite>.exit (vman) munakata@muna-E450:~/code/python/vman/vmanhmi$ python manage.py makemigrations (vman) munakata@muna-E450:~/code/python/vman/vmanhmi$ python manage.py migrate -- manage.py showmigrations で該当アプリのマイグレーション履歴が消えている事を確認 -- manage.py makemigrations (アプリ名) で明示的にアプリ名を指定して migrations ファイルを生成 (vman) munakata@muna-E450:~/code/python/vman/vmanhmi$ python manage.py makemigrations fview -- それでも migrate 処理がエラーになる場合には fake migrate を実行する (vman) munakata@muna-E450:~/code/python/vman/vmanhmi$ python manage.py migrate --fake fview zero (vman) munakata@muna-E450:~/code/python/vman/vmanhmi$ python manage.py migrate fview -- [[(参考) manage.py migrateをおかしな感じにやってしまった:https://qiita.com/ajitama/items/130b2f31abd855937a93]] *** pylint の Class '(クラス名)' has no 'objects' member エラー対策 [#m1fad130] - pip install pylint-django - vscode の 設定 → ユーザー設定 (setting.json) に以下を記述 "python.linting.pylintArgs": [ "--load-plugins=pylint_django" ], *** Django キャッシュクリア (データーベース更新時など) [#t89f0cf5] -- from django.core.cache import cache -- cache.clear() munakata@muna-E450:~/code/python/vman/vmanhmi$ python manage.py shell Python 3.6.8 (default, Jan 14 2019, 11:02:34) [GCC 8.0.1 20180414 (experimental) [trunk revision 259383]] on linux Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from django.core.cache import cache >>> cache.clear() ** View(ビジネスロジック) [#we18c449] - Djangoのビューは以下の条件を満たす必要がある -- request オブジェクトを(第一引数として)受け取る -- callable であること -- response オブジェクトを返す - view には各アプリケーション内でデーターベース内のデータの操作(CRUD=新規作成、リード、更新、削除)を行うためのロジック(=関数)を規定する - python の関数として定義する方法 - python の Class として定義し、(クラス名).as_view() でクラスをビュー関数化する方法 <--- こちらが推奨 ** Template(Look & Feel の規定) [#xbe65980] *** bootstrap4 の導入 [#w7165802] - 1) [[bootstrap:https://getbootstrap.com/]] - 2) [[jquery:http://jquery.com/]] munakata@muna-E450:~/code/python/vman/vmanhmi$ tree (project root) ├── fview (application root) │ (snip) │ ├── static <------ 各アプリの下に専用の css/js を配置する │ │ └── fview │ │ ├── css │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ └── js │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.bundle.min.js.map │ │ ├── jquery-3.4.1.min.js │ │ └── jquery-3.4.1.min.map - 3) [[django-bootstrap4:https://pypi.org/project/django-bootstrap4/]] のインストール pip3 install django-bootstrap4 munakata@muna-E450:~/code/python/vman/vmanhmi$ pip install django-bootstrap4 Collecting django-bootstrap4 Downloading https://files.pythonhosted.org/packages/02/5a/485d61f6dafa4e4d001a7880b04f40f04fe485a54b2756b0536ed2052342/django-bootstrap4- 0.0.8.tar.gz Building wheels for collected packages: django-bootstrap4 Running setup.py bdist_wheel for django-bootstrap4 ... done Stored in directory: /home/munakata/.cache/pip/wheels/f6/58/7f/fcdbcc8c631cc8f775cb8b9349ded99aff7a3f5b28e0f40ef3 Successfully built django-bootstrap4 Installing collected packages: django-bootstrap4 Successfully installed django-bootstrap4-0.0.8 - bootstrap4 ボタン [#tc3adc6f] -- &ref(button1.jpg); -- &ref(button2.jpg); -- [[Bootstrap4に用意されているクラス【ボタン編】:https://webnetamemo.com/coding/bootstrap4/201711096011]] *** [[font-awesome:https://fontawesome.com/]] [#q83736bd] - e-mail = g-mail - pass = frex7785 - my kit ID = [[f70a45d16f:https://fontawesome.com/kits/f70a45d16f/use?welcome=yes]] <script src="https://kit.fontawesome.com/f70a45d16f.js"></script> - インストール munakata@muna-E450:~/code/python/vman/vmanhmi$ pip install pipenv munakata@muna-E450:~/code/python/vman/vmanhmi$ pipenv install django-fontawesome-5 - [[(参考URL) django-fontawesome-5 1.0.16:https://pypi.org/project/django-fontawesome-5/]] *** choice に対応した専用フォーム(ラジオボタン、ドロップダウンリストなど) [#z5105dcc] - [[Djangoフォームを使ってみよう:https://eiry.bitbucket.io/tutorials/tutorial/forms.html]] *** ページネーション [#ub08d1af] - [[Django 1.0 Documents ペジネータ (paginator):https://djangoproject.jp/doc/ja/1.0/topics/pagination.html]] - [[Django、ページング処理まとめ:https://narito.ninja/blog/detail/89/]] - [[Djangoで、前のデータ・次のデータを取得する:https://narito.ninja/blog/detail/79/]] *** url タグ(別画面へのリンク)= {% url '{namespace}:{name}' %} [#adbecea4] - namespace = プロジェクト全体の urls.py に指定された名前 - name = アプリケーションの urls.py に指定された名前 <a href="{% url 'fview:list_view' %}" class="btn btn-primary btn-sm my-3">ファイル一覧に戻る</a> *** url タグでページ遷移する時に引数を渡す方法 [#p2504b5f] - 第三引数に引数名、外側に該当する変数名を書く <a href={% url 'app:table' table.id %}>{{ table.name }}</a> -[[Django - URLに引数を指定してページ遷移する:https://hiroronn.hatenablog.jp/entry/20190305/1551742344]] *** <a タグを使って別ページへのリンクを指定するときには view で規定したメソッド名を使う [#j3b02dc4] - 下記の例では fview:detail_view は view 内でメソッド関数として定義した detail_view を呼び出している <a scope="row" href="{% url 'fview:detail_view' fid=vfile.file_ID %}" class="btn btn-outline-primary btn-sm">{{ vfile.file_ID }}</a> - アプリケーションの urls.py で name= で規定した名前が URL の逆引きに利用される - &color(red){ここでは urls.py 内で規定した name と view 内のメソッド名が同じ、どちらを参照しているのかわからない! 駄目実装だな}; *** 画面遷移を伴わない イベントによるデータ操作 [#heb74024] - template に POST 処理を書いて view で受け取る <form action="" method="POST"> {% csrf_token %} <button type="submit" class="btn btn-outline-success btn-sm">お気に入りに登録</button> </form> - [[Djangoで、フォームの内容を保持する(セッションを使う方法):https://narito.ninja/blog/detail/46/]] *** {% csrf_token %} = クロスサイトリクエストフォージェリ(CSRF) 対策 [#u2e93832] - POST 処理がエラーとして誤検出されないようにする目的で追加する必要がある *** 画像の取扱い [#za579f2d] - DJANGO は Instagram に使われているくらいなので写真のデーターベース検索に関する機能が充実しているが、ポイントは&color(red){ データベースには実際の写真データは登録しないが DJANGO の操作としては DB に登録するように見せて実際にはファイル名、Media_Root からのパス名だけを DB に入れる仕組みを使っていること。このため DJANGO の view を使って写真を登録しないと写真が認識(=検索)出来ない}; - 画像を扱うには pillow というライブラリが必要 munakata@muna-E450:~/code/python/vmanhmi/fview/templates/fview$ pip install pillow Collecting pillow Using cached https://files.pythonhosted.org/packages/d2/c2/f84b1e57416755e967236468dcfb0fad7fd911f707185efc4ba8834a1a94/Pillow-6.0.0-cp36-cp36m-manylinux1_x86_64.whl Installing collected packages: pillow Successfully installed pillow-6.0.0 -- プロジェクトルートディレクトリに media ディレクトリーを作成 -- settings.py に以下の行を追加 # media file for "pillow" MEDIA_ROOT = os.path.join(BASE_DIR, 'media') MEDIA_URL = '/media/' -- ルート URL ルーティングファイル (urls.py) に以下の行を追加 # To support media directory path finding from . import settings from django.contrib.staticfiles.urls import static from django.contrib.staticfiles.urls import staticfiles_urlpatterns urlpatterns += staticfiles_urlpatterns() urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) -- model に画像を扱うための設定を追加(upload_toを’documents’にしておくことで、/media/documentsに画像が保存される) class Document(models.Model): description = models.CharField(max_length=255, blank=True) photo = models.ImageField(upload_to='documents/', default='defo') uploaded_at = models.DateTimeField(auto_now_add=True) ImageField 型を宣言することが重要な意味を持つ --- データが画像データであるかの validity check が行われる --- 更に実際の画像データ(バイナリー)はデーターベースには登録せず、実際のファイルの場所へのリンク情報を登録する - 参考 URL -- [[Display Image in a Django Template (using ImageField):https://matthiasomisore.com/web-programming/display-image-in-a-django-template-using-imagefield/]] -- [[Djangoで画像アップローダを作る:https://hombre-nuevo.com/python/python0065/]] -- [[画像をアップロードして表示する:https://ymgsapo.com/2018/11/05/show-image-imagefield/]] *** 画面分割 = boostrap4 のグリッドシステム [#u1287e3b] - container 領域内を水平方向に 12分割 された領域に画面を分解可能 (例. 8 + 4 = 12) - &ref(twelve-sectors2.jpg); <div class="container"> <div class="row"> <div class="col-lg-8"> <div class="row"> <div class="col-xs-6"></div> <div class="col-xs-6"></div> <div class="col-xs-6"></div> <div class="col-xs-6"></div> </div> </div> <div class="col-lg-4"></div> </div> </div> - &ref(layout-example2.jpg); - [[Bootstrapのグリッドシステムの使い方を初心者に向けておさらいする:http://websae.net/twitter-bootstrap-grid-system-21060224/]] ** [[django/channels (非同期処理拡張):https://github.com/django/channels]] [#c7dd2bb2] - [[公式ドキュメント(英語):https://channels.readthedocs.io/en/latest/]] *** [[コンセプト (Django ChannelsでできるリアルタイムWeb 引用):https://qiita.com/massa142/items/cbd508efe0c45b618b34]] [#k7343b96] -- &ref(ASGI_0.jpg); -- &ref(ASGI_1.jpg); *** インストール [#i436d49e] (vman) munakata@muna-E450:~/code/python/vman$ pip3 install channels channels_redis asgi_redis - [[radis をインストール:https://symfoware.blog.fc2.com/blog-entry-2175.html]]( Linux マシンのパッケージとして) munakata@mvc:~$ sudo apt install redis munakata@mvc:~$ redis-server -v Redis server v=4.0.9 sha=00000000:0 malloc=jemalloc-3.6.0 bits=64 build=9435c3c2879311f3 munakata@mvc:~$ ss -nlt State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 0.0.0.0:111 0.0.0.0:* (snip) LISTEN 0 128 127.0.0.1:6379 0.0.0.0:* <------ Radis はデフォルトで 127.0.0.1 だけを受け付ける設定 -- [[Redisのインストール・セットアップ (自動起動の設定など) :https://qiita.com/KurosawaTsuyoshi/items/f8719bf7c3a10d22a921]] - routing.py をプロジェクト直下(アプリの下ではなく)に作成 from channels.routing import ProtocolTypeRouter application = ProtocolTypeRouter({ # (http->django views is added by default) }) - setting.py の installed apps に channels を追加 INSTALLED_APPS = [ 'channels', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'fview', 'bootstrap4', 'fontawesome_5', ] - channels 2.0 では (参考にした記事で使っている) delay server は無くなり、以下の機能を利用するようになったようだ [[リンク:https://channels.readthedocs.io/en/latest/one-to-two.html?highlight=delay#delay-server]] Delay server If you used the delay server before to put things on hold for a few seconds, you can now instead use an AsyncConsumer and asyncio.sleep: class PingConsumer(AsyncConsumer): async def websocket_receive(self, message): await asyncio.sleep(1) await self.send({ "type": "websocket.send", "text": "pong", }) - setting.py に ASGI (=Asynchronous Server Gateway Interface) の起点を登録する。 # websocket (channels) のために ASGIの起点を指定 ASGI_APPLICATION = 'vmanhmi.routing.application' - runserver 時にASGI が有効になっている事を確認する System check identified no issues (0 silenced). July 20, 2019 - 15:43:38 Django version 2.2.2, using settings 'vmanhmi.settings' Starting ASGI/Channels version 2.2.0 development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C. - ASGI サーバーが起動するようになって以降、web サーバーを停止してもバックグラウンドで 127.0.0.1:8000 を Listen しているプロセスが残るようになり、サーバー再起動時に以下のエラーを吐くようになった Listen failure: Couldn't listen on 127.0.0.1:8000: [Errno 98] Address already in use. -- lsof -i:(ポート番号) で 8000番ポートを使っているプロセスを特定しマニュアルでプロセスを kill する munakata@muna-E450:~/code/python/vman/vmanhmi$ lsof -i:8000 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME chrome 3143 munakata 57u IPv4 57585 0t0 TCP localhost:56236->localhost:8000 (ESTABLISHED) python3 4055 munakata 26u IPv4 57446 0t0 TCP localhost:8000 (LISTEN) python3 4055 munakata 29u IPv4 57621 0t0 TCP localhost:8000->localhost:56242 (CLOSE_WAIT) python3 4055 munakata 30u IPv4 55067 0t0 TCP localhost:8000->localhost:56236 (ESTABLISHED) python3 4055 munakata 31u IPv4 58463 0t0 TCP localhost:8000->localhost:56246 (CLOSE_WAIT) python3 4055 munakata 32u IPv4 57175 0t0 TCP localhost:8000->localhost:56244 (CLOSE_WAIT) munakata@muna-E450:~/code/python/vman/vmanhmi$ sudo kill 3143 4055 munakata@muna-E450:~/code/python/vman/vmanhmi$ lsof -i:8000 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME python3 4055 munakata 26u IPv4 57446 0t0 TCP localhost:8000 (LISTEN) python3 4055 munakata 29u IPv4 57621 0t0 TCP localhost:8000->localhost:56242 (CLOSE_WAIT) python3 4055 munakata 30u IPv4 55067 0t0 TCP localhost:8000->localhost:56236 (CLOSE_WAIT) python3 4055 munakata 31u IPv4 58463 0t0 TCP localhost:8000->localhost:56246 (CLOSE_WAIT) python3 4055 munakata 32u IPv4 57175 0t0 TCP localhost:8000->localhost:56244 (CLOSE_WAIT) -- が、LISTREN ポートのプロセスが死なない。 &color(red){kill -s QUIT}; で強制停止できた munakata@muna-E450:~/code/python/vman/vmanhmi$ kill -s QUIT 4055 munakata@muna-E450:~/code/python/vman/vmanhmi$ lsof -i:8000 *** [[websocket の基本:http://yagisuke.hatenadiary.com/entry/2018/01/28/181954]] [#x5cfe4eb] - WebSocketはフレーム探知で送受信しますが、相手が決まっているために送信先の情報などはもちません(一対一の通信) - WebSocketが他のHTTPベースのプロトコル異なるのは「ステートフルな通信である」という点 - 通信はサーバーが受信を受けている状態で、必ずクライアントから接続 (クライアント起点の通信) *** python プログラムから Websocket を利用する方法 [#w401aec6] - Websocket は通常は Java Script の中で送受信するが、今回は素の python プログラム(実際には Django Managment.py 配下のプログラム)として動作させるワーカープログラムの中から websocket で Django Channel の ASGI Consumer と通信させたい。 - python で websocket を使うためのライブラリーは2種類 (websocket-client, websockets) ある - [[python websockets API1 (websocket-client):https://pypi.org/project/websocket_client/]] [#j55b2e59] - [[python websockets API2 (websockets):https://pypi.org/project/websockets/]] [#n0adc042] -- pip install websockets --- Here’s how a client sends and receives messages: #!/usr/bin/env python import asyncio import websockets async def hello(uri): async with websockets.connect(uri) as websocket: await websocket.send("Hello world!") await websocket.recv() asyncio.get_event_loop().run_until_complete( hello('ws://localhost:8765')) --- And here’s an echo server: #!/usr/bin/env python import asyncio import websockets async def echo(websocket, path): async for message in websocket: await websocket.send(message) asyncio.get_event_loop().run_until_complete( websockets.serve(echo, 'localhost', 8765)) asyncio.get_event_loop().run_forever() *** Channels (ワーカーと consumer 間の通信のは channel を使う、websocket は使えない) [#k2e4470c] - backscan_worker.py (← manage.py のカスタムコマンド)を worker として動かし consumer と通信させる。 - そのままでは &color(red){backscan_worker は Radis バックエンドと繋がっていない}; のでエラーになる munakata@muna-E450:~/code/python/vman/vmanhmi$ python3 manage.py runworker prog-sync [16/Aug/2019 16:05:22] INFO [django.channels.worker:40] Running worker for channels ['prog-sync'] (snip) File "/home/munakata/code/python/vman/lib/python3.6/site-packages/channels/routing.py", line 61, in __call__ "No application configured for scope type %r" % scope["type"] ValueError: No application configured for scope type 'channel' - そんな事はない。1つの websocket 接続で複数の通信をハンドルすることができる -- [[How to use multiple websocket connections using Django Channels?:https://stackoverflow.com/questions/52446540/how-to-use-multiple-websocket-connections-using-django-channels]] from django.conf.urls import url from channels.routing import ProtocolTypeRouter, URLRouter from channels.auth import AuthMiddlewareStack application = ProtocolTypeRouter({ # WebSocket chat handler "websocket": AuthMiddlewareStack( URLRouter([ url(r"^first_application/stream/$", app_2_consumers.AsyncApp1Consumer), url(r"^second_application/stream/$", app_2_consumers.AsyncApp2Consumer), ]) ), }) - 参考 URL -- [[Django Channels - Cannot send event to a consumer Ask Question:https://stackoverflow.com/questions/52487290/django-channels-cannot-send-event-to-a-consumer]] -- [[How to use ChannelNameRouter to communicate between Worker and Websocket (Django and Channels2.x)?:https://stackoverflow.com/questions/50199118/how-to-use-channelnamerouter-to-communicate-between-worker-and-websocket-django]] *** channels group_send を使った同期(イベント一斉送信) [#c182b297] - backscan_worker (Django custom command, standalone python program) と channels を使ってメッセージ送受信をする場合には、worker プログラム側も Redis に接続する必要がある。 - manage.py runworker backscan_worker prog-sync (=グループ名)で常駐化ができるが、この場合 channel からのメッセージを受け取って処理をする形になるので、コマンドラインからの単独実行と両立できない(まぁプログラムを分割すれば良い話ではあるが) - 結局 ブラウザー(=JavaScript) ⇔ Consumer 間とは別に ワーカー ⇔ Consumer 間に別の WebSocket による接続を行う - WebSocket にはアドレス情報を付加できない(組み込まれている)ので、ワーカー ⇒ ブラウザー に直接メッセージを送ることは出来ない - Consumer の中で別々のハンドラーを用意し、その間を group_send メッセージでイベントをブロードキャストする方法を採用した - group_send は非同期処理になるので async_to_sync で命令を wrapping する必要がある - それぞれのハンドラーに対して、明示的にグループへの登録、削除を行う必要がある -- グループへの参加 def connect(self): #print("websocket [browser] connected >>>>>") self.accept() async_to_sync(self.channel_layer.group_add)("fview_sync", self.channel_name) -- グループからの離脱 def disconnect(self, close_code): print("Web socket [browser] disconnect code = [" + str(close_code) + "]") async_to_sync(self.channel_layer.group_discard)("fview_sync", self.channel_name) self.close() - group_send 時には グループ名、type、メッセージ本体を指定する必要がある -- type は アプリ名.タイプ名 の形を指定する -- アプリ名_タイプ名 の名前の ハンドラーを別に用意する (. を _ に置き換える) --- https://github.com/andrewgodwin/channels-examples/blob/master/multichat/chat/consumers.py *** 参考 URL [#mba8e51e] -- [[Django 2.0 + Channels 2.xを使ってWebSocketを扱う(その1):http://www.denzow.me/entry/2018/03/25/235952]] -- [[Django 2.0 + Channels 2.xを使ってWebSocketを扱う(その2):http://www.denzow.me/entry/2018/03/27/002350]] -- [[Django 2.0 + Channels 2.xを使ってWebSocketを扱う(その3):http://www.denzow.me/entry/2018/04/03/002351]] -- [[Django 2.0 + Channels 2.1 でチュートリアルやっていく(1):https://poyo.hatenablog.jp/entry/2018/05/17/224247]] -- [[Django 2.0 + Channels 2.1 でチュートリアルやっていく(2):https://poyo.hatenablog.jp/entry/2018/05/17/233000]] -- [[Django 2.0 + Channels 2.1 でチュートリアルやっていく(3):https://poyo.hatenablog.jp/entry/2018/05/18/003000]] -- [[Django を WebSocket サーバにする:https://qiita.com/ekzemplaro/items/a6b81bd1d181fdd0cc24]] -- [[REAL-TIME PROGRESS UPDATES WITH DJANGO CHANNELS:http://thomasruble.com/blog/real-time-progress-updates-with-django-channels/]] --- &ref(The Fool - real-time progress updates with django channels.pdf); -- [[Django, Background Processes, and Keeping Users in the Loop:https://www.oddbird.net/2017/04/17/async-notifications/]] -- [[Finally, Real-Time Django Is Here: Get Started with Django Channels:https://blog.heroku.com/in_deep_with_django_channels_the_future_of_real_time_apps_in_django]] -- [[Getting Started with Django Channels:https://realpython.com/getting-started-with-django-channels/]] -- [[Django Channels — Talking to Channels from my non-Django application:https://medium.com/@johngrant/django-channels-talking-to-channels-from-my-non-django-application-7dec7ceb80a8]] -- [[Sending messages to groups in Django Channels 2:https://stackoverflow.com/questions/48855417/sending-messages-to-groups-in-django-channels-2]] ** Java Script から view を呼び出す [#u0378c2c] *** 参考 URL [#g0a51c67] - [[Djangoでリダイレクトのレスポンスを返してJavaScript(JS)でリダイレクトさせたい:https://awesome-linus.com/2019/04/03/django-return-redirect-and-redirect-js/]] ** [[subprocess.Popenによる詳細な外部プログラムの実行:https://pknight.hatenablog.com/entry/20170414/1492152828]] [#gf2b420d] 通常はcheck_allやcheck_output(およびrun)などを使えばよいのですが、子プロセスの終了をまたずに何か処理をしたいとかそういうことをしたい場合にはPopenを使います。 # -- test.py -- from time import sleep print('child start!') sleep(1) print('child end!') # -- main.py -- import subprocess print('parent start!') ps = subprocess.Popen(['python','test.py']) print('parent end!') $ py main.py parent start! parent end! child start! child end! ** [[Django アプリの中から 重い処理を別の実行プロセスに登録する:https://dot-blog.jp/news/django-async-celery-redis-mac/]] [#afd2b790] - Celery は非同期処理の登録の方法として使えるが(理解が正しければ)処理結果が出た時点で結果を返すことはできる - が処理の途中経過を通知する(=同期する)手段は提供されないと理解。 なので、この記事では別プロセスへの登録の部分だけが参考になると想像 -&ref(cerely_60.jpg); -- Task = 非同期処理をひとかたまりにまとめたもの。ブラウザからのリクエストによって必要となった実行処理する関数やその引数などが記録される -- Queue = タスクを格納する入れ物です。構造が先入れ先出しになっているものが Queue、後入れ先出しが FIFO -- Producer = タスクを作成してブローカーに渡す役割を持ったもの。『Celery Client』を利用 -- Broker = タスクをキューに登録したり、登録されたタスクをワーカーに渡したりするもの。『Redis』を利用 -- Woker = ブローカーによってキューから取り出されたタスクを実際に処理。『Celery Woker』を利用 -&ref(cerely2_60.jpg); *** [[やりたい実装 例:https://stackoverflow.com/questions/50130851/django-channels-background-worker-instances]] [#ne22e1d3] - &ref(django_woker.JPG); *** wscat (websocket テストツール) [#ucf2e2ee] - インストール(node.js のツールなので npm でインストール) munakata@muna-E450:~/code/python/vman/vmanhmi$ sudo npm install -g ws /usr/local/lib └── ws@7.1.1 - サーバーに接続 *** python プログラム内で別の python プログラムを起動する方法 [#uc2cb56c] - 実現方法 -- os.fork import os if os.fork()==0: long_process() else: return HttpResponse() -- subprocess import subprocess p = subprocess.Popen([sys.executable, '/path/to/script.py'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) (where the script is likely to be a manage.py command) -- threads import threading t = threading.Thread(target=long_process, args=args, kwargs=kwargs) t.setDaemon(True) t.start() return HttpResponse() --- &color(red){Due to the Global Interpreter Lock, in CPython only one thread can execute Python code at once}; (even though certain performance-oriented libraries might overcome this limitation). If you want your application to make better of use of the computational resources of multi-core machines, you are advised to use multiprocessing. However, threading is still an appropriate model if you want to run multiple I/O-bound tasks simultaneously. -- multiprocessing from multiprocessing import Process p = Process(target=_bulk_action,args=(action,objs)) p.start() return HttpResponse() - python interpreter には thread scheduler が無い。 故に multi-thread にしても並列化されない。 - これを GIL (Global Interpreter Lock) 問題とよぶ - python で並列化を実現するにはプロセス単位で実行を分ける(= thread scheduler を二重化する)のが良い - 参考 URL -- [[Django long running asynchronous tasks with threads/processing:https://stackoverflow.com/questions/8068945/django-long-running-asynchronous-tasks-with-threads-processing]] --- &ref(django-threads.pdf); -- [[PythonのGILについて簡単に調べてみました:https://blog.karky7.com/2014/12/pythongil.html?m=1]] -- [[[Python] multiprocessing 備忘録:http://note.crohaco.net/2017/python-multiprocessing/]] *** [[Integrating a data producer as worker to Django Channels 2.x:https://stackoverflow.com/questions/51083910/integrating-a-data-producer-as-worker-to-django-channels-2-x]] [#f4f18d73] *** [[(WebSocket を使わない方法) Djangoで、進捗表示する:https://torina.top/detail/403/]] [#b71879c8] ** Ajax (Javascript 内でクライアント内での動的ページ書き換えを記述する) [#y3ebeddc] *** Ajax の概念 [#kb87dae9] - Ajax = Asynchronous JavaScript + XML、&color(red){XMLHttpRequest}; による非同期通信を利用 -- Ajaxを利用すれば、サーバーから必要な情報だけを取得えきるので、ページ全体をリロードせず必要部分だけ更新できる(応答性の大幅な向上) -- [[AJAX in a nutshell の説明:https://hackernoon.com/ajax-in-a-nutshell-4770da7b16f3]] --- &ref(ajax_dialog2.jpg); -- [[django#AJAX の説明:https://qiita.com/soup01/items/f356d6ee09534007f76d]] --- まずイベントがWEB PAGEの中に発生。(例えばPAGEがLOADされた、ボタン押し) --- JavascriptはXMLHttpRequestオブジェクトを生成。 --- XMLHttpRequestはWEB Serverに要求を送る。 --- サーバー側が要求を処理する。 --- サーバー側が返事する。 --- Javascriptが返事内容をEncodeする。 --- 画面が変わる。 -- という事は、実際には多くの Web アプリケーションで Ajax 処理の活用がポイントとなる -- Ajaxアプリケーションでは &color(red){動的にページの一部が書き換えられる}; 為、デザインとコードが単純に分離できないという開発上の問題点が顕在化 -- こうした問題を解決するために Jquerry 等のアプリケーションフレームワーク(JavaScriptライブラリ)が開発された --- [[jQuery:https://jquery.com/]] --- [[Google Web Toolkit:http://www.gwtproject.org/]] --- [[Prototype JavaScript Framework:http://prototypejs.org/]] - Javascript 内の script セクションに Ajax 処理を追記する - Jquerry を使った簡易記述を利用することができる(一般的である) - Django の場合、実際のページ書き換え(クライアント内でのリロード) *** jQuerry (= Javascript Library) [#h27e77c4] - jQueryでスクリプトを書くときには &color(red){まずready関数を記述、}; その内側に実際の命令を記述します。 - jQueryを利用したスクリプトでは、 -- &color(red){(1)$("...")の内側にどの(X)HTMLの要素を操作するかをセレクターで指定}; -- &color(red){(2)その後ろに .(ドット) で処理の内容 (=jQueryの命令) を書く}; $(function(){ $("セレクター").jQueryの命令 }) - jQuerry で Ajax を使う場合の記述方法は異なる -- ajax()にはセレクターの指定はない -- $. で始まり、括弧内の {...} に設定(オプション)を記述 -- 設定は左側に項目名を、右側には項目に対応する値を記述し、左右は:(コロン)で区切ります。 -- 複数の設定は ,(カンマ)区切りでつなげていきます。 -- データーの取得に成功すると、取得内容は function(data){...} の「data」に入る -- success にはデータ取得成功時に実行する処理を function(data){...} の内側に記述する $.ajax({ url: '取得するXMLファイル', dataType: 'xml', success : function(data){ //取得したファイルに対する処理 } }) - セレクター 一覧 |名称|書式|指定対象|h |>|>|BGCOLOR(#5f9ef0):CSSでよく利用されるセレクター| |要素セレクター|$("要素名")|特定のhtml要素| |IDセレクター|$("#ID名")|特定のid属性の値を持つ要素| |クラスセレクター|$(".クラス名")|特定のclass属性の値を持つ要素| |子孫セレクター|$("要素1 要素2")|特定の要素の内側にある要素| |ユニバーサルセレクター|$("*")|すべての要素| |>|>|BGCOLOR(#5f9ef0):CSS2のセレクター| |グループセレクター|$("セレクター1,セレクター2,...")|複数のセレクター| |子セレクター|$("親要素名 > 子要素名")|特定の要素の直下の子要素| |隣接セレクター|$("要素1 + 要素2")|特定の要素の次の要素| |first-child擬似クラス|$("要素:first-child")|特定の要素内の最初の要素| |>|>|BGCOLOR(#5f9ef0):CSS3のセレクター| |間接セレクター|$("要素1 ~ 要素2")|特定の要素の後に出現する要素| |否定擬似クラス|$("要素1:not(要素2)")|特定の要素内の指定した要素以外の要素| |empty擬似クラス|$("要素:empty")|子要素やテキストを含まない要素| |nth-child擬似クラス|$("要素:nth-child(番号)")|特定の要素内の指定した番号の要素| |last-child擬似クラス|$("要素:last-child")|特定の要素内の最後の要素| |only-child擬似クラス|$("要素:only-child")|指定した要素が1つだけ含まれる特定の要素| |>|>|BGCOLOR(#5f9ef0):CSSの属性セレクター| |[attribute]|$("[属性名]")|特定の属性を持つ要素| |[attribute='value']|$("[属性名='値']")|特定の属性が指定した値を持つ要素| |[attribute!='value']|$("要素名[属性名!='値']")|特定の属性が指定した値を持たない要素| |[attribute^='value']|$("[属性名^='値']")|特定の属性が特定した値で始まっている要素| |[attribute$='value']|$("[属性名$='値']")|特定の属性が特定した値で終わっている要素| |[attribute*='value']|$("[属性名*='値']")|特定の属性が特定した値を含んでいる要素| |[attributeFilter1][attributeFilter2]|$("[属性セレクター1][属性セレクター2]")|複数の属性セレクターに該当する要素| |>|>|BGCOLOR(#5f9ef0):jQueryの独自フィルター| |firstフィルター|$("要素:first")|指定した要素の最初の要素| |lastフィルター|$("要素:last")|指定した要素の最後の要素| |evenフィルター|$("要素:even")|指定した要素の偶数番目の要素| |oddフィルター|$("要素:odd")|指定した要素の奇数番目の要素| |eqフィルター|$("要素:eq(番号)")|指定した番号の要素(番号は0から数える)| |gtフィルター|$("要素:gt(番号)")|指定した番号より後の要素(番号は0から数える)| |ltフィルター|$("要素:lt(番号)")|指定した番号より前の要素(番号は0から数える)| |headerフィルター|$("要素:header")|h1~h6までのheader要素| |containsフィルター|$("要素:contains(文字列)")|特定の文字列が含まれている要素| |hasフィルター|$("要素1:had(要素2)")|特定の要素が含まれている要素| |parentフィルター|$("要素:parent")|子要素やテキストを含む要素| *** jQuerry UI [#vd5f8349] - jQuery UIは、jQuery のユーザー・インターフェイス(UI)に関わる機能をを拡張する公式ライブラリ(プラグイン) - [[jQuery UIを利用するには?:https://www.buildinsider.net/web/jqueryuiref/0001]] - jQuery UI project は &color(red){2016年9月の [[ver.1.12.1:https://blog.jqueryui.com/category/releases/]] 以降更新が止まって}; いた。 - jQuery UI 1.12.1 は &color(red){jQuery 1.7.0 – 3.1.0}; に対応している - 今回は Ajax + jQuery 環境で jQuery UI が提供する progressbar を利用したいので、この環境に合わせた - jQuery UIで提供されているコンポーネント |分類|コンポーネント名|概要|h |Interactions|Draggable|ドラッグ可能な要素を定義| |~|Droppable|ドロップ可能な要素を定義| |~|Resizable|サイズ変更可能な要素を定義| |~|Selectable|マウス操作で選択可能な要素を定義| |~|Sortable|並べ替え可能な要素を定義| |Widgets|Accordion|アコーディオン・パネル| |~|Autocomplete|オート・コンプリート機能付きのテキストボックスを生成| |~|Button|ボタンやリンク、ラジオボタンなどからボタンを生成| |~|Datepicker|日付入力ボックスを生成| |~|Dialog|汎用的なダイアログを生成| |~|Menu|展開可能なリッチ・メニューを生成| |~|Progressbar|進捗(しんちょく)バーを生成| |~|Slider|スライダーを生成| |~|Spinner|アップダウン・ボタンを伴う数値入力ボックスを生成| |~|Tabs|タブ・パネルを生成| |~|Tooltip|ツールチップを生成| |Effects|Effect|基本的なエフェクトを提供| |~|Show/Hide/Toggle|要素の表示/非表示にエフェクトを適用| |~|Add/Remove/Toggle/Switch Class|スタイル・クラスの適用/解除にエフェクトを適用| |~|Color Animation|色を徐々に変化させるエフェクトを提供| - [[jQuery UI Progressbar 仕様:https://api.jqueryui.com/progressbar/]] - 参考 URL -- [[ASCII 初めてのjQuery:セレクターAPIを一挙解説(前編):https://ascii.jp/elem/000/000/439/439414/index-2.html]] -- [[ASCII 初めてのjQuery:セレクターAPIを一挙解説(後編):https://ascii.jp/elem/000/000/440/440882/index-5.html]] -- [[ASCII jQueryによるフォームのデザインの基礎(前編):https://ascii.jp/elem/000/000/452/452106/]] -- [[ASCII jQueryによるフォームのデザインの基礎(後編):https://ascii.jp/elem/000/000/456/456040/]] -- [[ASCII jQueryでAjaxを利用する基本チュートリアル:https://ascii.jp/elem/000/000/458/458236/]] -- [[:]] *** 参考 URL [#i5d1b223] - [[:]] - [[:]] - [[:]] - [[jQuery日本語リファレンス(Ajax/API/jQuery):http://semooh.jp/jquery/api/ajax/jQuery.ajax/options/]] - [[Django クロスサイトリクエストフォージェリ (CSRF) 対策 [Ajax]:https://docs.djangoproject.com/ja/2.2/ref/csrf/#ajax]] - [[s-cookie/js-cookie:https://github.com/js-cookie/js-cookie/]] - [[:]] - [[:]] ** vscode を使ったデバッグ [#x9aada46] - vscode 全般の説明は [[VsCode]] にまとめている - vscode を使ってdjango のデバッグができる。 -- vscode のデバッガー設定で python django を選択する -- デバッガーの RUN コマンド(緑色の矢印ボタン)でサーバーを起動する (別のターミナルで起動しない) -- ブラウザーでアプリを起動してブレークポイントにヒットすると vscode 画面に切り替わり、変数の内容などが確認できる ** 参考 URL [#u00645c3] -- [[Python Django入門 (1) Django イントロ:https://qiita.com/kaki_k/items/511611cadac1d0c69c54]] -- [[Python Django入門 (2) install mac編:https://qiita.com/kaki_k/items/e824cfcf089e75d43551]] -- [[Python Django入門 (3) Django 導入~Admin ツール:https://qiita.com/kaki_k/items/7b178ad39394a031b50d]] -- [[Python Django入門 (4) bootstrap による Web 画面作成:https://qiita.com/kaki_k/items/6e17597804437ef170ae]] -- [[Djangoチュートリアル - 汎用業務Webアプリを最速で作る:https://qiita.com/okoppe8/items/54eb105c9c94c0960f14]] -- [[IT技術相互 Python Django の使い方:https://jpn.itlibra.com/article?id=10796]] -- [[Djangoを最速でマスターする part1:https://qiita.com/gragragrao/items/373057783ba8856124f3]] -- [[Djangoを最速でマスターする part2:https://qiita.com/gragragrao/items/9a85a372a9c3eec06243]] -- [[Django入門編 (paiza トレーニング):https://paiza.jp/works/django/primer]]
タイムスタンプを変更しない
#contents ** インストールとプロジェクトの初期化、アプリの初期化 [#v89c1905] *** インストール [#l192eb6c] [local] munakata:~/source/python/vlman$ pip3 install django [local] munakata:~/source/python/vlman$ python3 -m django --version 2.2.2 *** PYTHONPATH の指定[#lb4d603c] - vscode 上の lint が Django のパッケージが見つけられないというワーニングを出すことを抑止するために明示的に指定 - PYTHONPATH には pip show django を実行して location で指定されたパスを設定する - PYTHONPATH 自体は vscode の設定ファイル (プロジェクトディレクトリ)/.vscode/settings.json 内に記載するのが良さそう *** プロジェクト と アプリ の初期化 [#t5ca0f8e] [local] munakata:~/source/python/vlman$ django-admin startproject vlmanui [local] munakata:~/source/python/vlman/vlmanui$ django-admin startapp file_viewer *** 管理用ユーザーの登録 [#p6099483] [local] munakata:~/source/python/vlman/vlmanui$ python3 manage.py createsuperuser Username (leave blank to use 'munakata'): Email address: magu775@gmail.com Password: Password (again): Superuser created successfully. *** timezone とロケールの設定 (settings.py) [#l8c0ab8b] LANGUAGE_CODE = 'ja' TIME_ZONE = 'Asia/Tokyo' *** インストール完了時のディレクトリー構造 [#n21cb74e] - プロジェクトの下にアプリケーションを追加していく構造 [local] munakata:~/source/python/vlman$ tree . ├── video_top │ ├── sample_A.mp4 ├── vlman.py ├── vlmandb.sqbpro ├── vlmandb.sqlite3 ├── file_viewer <--------- application definition │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ └── __init__.py │ ├── models.py <----------- database access definition │ ├── tests.py │ └── views.py <--------- look & feel definition └── vlmanui <------------ total system settings ├── manage.py └── vlmanui ├── __init__.py ├── settings.py <---------- application directory ├── urls.py <------------ URL entry point definition └── wsgi.py ** django ビルトイン管理ツール(= manage.py) [#i85b1ed8] - ビルトイン Web サーバーの起動 [local] munakata:~/source/python/vlman/vlmanui$ python3 manage.py runserver 8000 *** カスタムコマンドの追加 [#q3ed478e] - BaseCommand クラスもしくはその サブクラス の一つを継承した Command クラスを定義する必要がある - python プログラム名がコマンド名になる(但し実行時に .py は不要) - プログラムを配置する場所は決まっている ( アプリ/management/command/(カスタムコマンド).py ) - プログラム引数は def add_arguments(self, parser): という関数で評価する - プログラム実体は def handle(self, *args, **kwargs): という関数の中に書く - 参考 URL -- [[カスタム django-admin コマンドの実装 (Django 公式マニュアル):https://docs.djangoproject.com/ja/2.2/howto/custom-management-commands/]] -- [[argparse --- コマンドラインオプション、引数、サブコマンドのパーサー (python 公式マニュアル):https://docs.python.org/ja/3/howto/argparse.html#id1]] *** python argument parser (argpaser) [#ebe69f6f] - [[Argparse チュートリアル (python 公式チュートリアル):https://docs.python.org/ja/3/howto/argparse.html#id1]] - [[argparseの引数と設定例について調べてみた:https://kamiya555.github.io/2015/09/26/argparse-args/]] -- [[argparseでコマンドライン引数を使ってみよう!:https://www.sejuku.net/blog/23647]] -- ArgumentParser()の引数 ArgumentParser([description][, epilog][, prog][, usage][, add_help][, argument_default][, parents][, prefix_chars][, conflict_handler][, formatter_class]) |引数|デフォルト値|概要|h |prog|sys.argv[0]|プログラム名 (デフォルト: sys.argv[0])| |usage|パーサーに追加された引数から生成される|プログラムの利用方法を記述する文字列| |description|None|引数のヘルプの前に表示されるテキスト| |epilog|None|引数のヘルプの後で表示されるテキスト| |parents|???|ArgumentParser オブジェクトのリストで、このオブジェクトの引数が追加される| |formatter_class|???|ヘルプ出力をカスタマイズするためのクラス| |prefix_chars|‘-‘|オプションの引数の prefix になる文字集合 (デフォルト: ‘-‘)| |fromfile_prefix_chars|None|追加の引数を読み込むファイルの prefix になる文字集合| |argument_default|None|引数のグローバルなデフォルト値| |conflict_handler|???|衝突するオプションを解決する方法 (通常は不要)| |add_help|True|-h/–help オプションをパーサーに追加する| -- add_argument()の引数 add_argument(name or flags...[, action][, nargs][, const][, default][, type][, choices][, required][, help][, metavar][, dest]) |引数|概要|h |name or flags|名前か、あるいはオプション文字列のリスト (例: foo や -f, –foo)| |action|コマンドラインにこの引数があった時のアクション| |nargs|消費すべきコマンドライン引数の数| |const|一部の action と nargs の組み合わせで利用される定数| |default|コマンドラインに引数がなかった場合に生成される値| |type|コマンドライン引数が変換されるべき型| |choices|引数として許される値のコンテナー| |required|コマンドラインオプションが省略可能かどうか (オプション引数のみ)| |help|引数が何なのかを示す簡潔な説明| |metavar|使用法メッセージの中で使われる引数の名前| |dest|parse_args() が返すオブジェクトに追加される属性名| -- nargsに設定できる値 |設定値|概要|h |N|N 個の引数がコマンドラインから集められ、リストに格納されます| |?|コマンドライン引数が存在しない場合、 default の値が生成されます| |*|全てのコマンドライン引数がリストに集められます| |+|全てのコマンドライン引数がリストに集められます| -- [[agrparse (関数の引数のパーシング関数):https://docs.python.org/ja/3/library/argparse.html]] -- [[ArgumentParserの使い方を簡単にまとめた:https://qiita.com/kzkadc/items/e4fc7bc9c003de1eb6d0]] -- [[Python argparseの使い方:https://qiita.com/dodo5522/items/6ec2b6d83287add6c185]] -- [[ちょっとしたコマンドラインツールをpythonで作る時に便利なargparseの使い方:http://tech.uribou.tokyo/python-argparsenoshi-ifang/]] -- [[Djangoカスタムコマンドを作成し、引数を送る(type にカスタムバリデータ関数を指定する方法):https://shotanuki.com/django%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%82%92%E4%BD%9C%E6%88%90%E3%81%97%E3%80%81%E5%BC%95%E6%95%B0%E3%82%92%E9%80%81%E3%82%8B/]] ** URL ディスパッチャー(パスコンバーターによるリインテリジェントな URL ルーティング機構) [#vfff4005] path('abc/', include('(プロジェクト名).urls', name = 'reverse serch name') -- http://aaa.com/abc/ の場合 abc/ の部分だけ記載する -- include で各アプリの urls.py を読み込ませる rpath(r'^abc*$', include('application name.urls'), name='reverse name') -- 正規表現を使ってパス変換する場合には rpath を使うと python の正規表現(re.)の構文が使える -- name で逆引き( URL をハードコードしないで済むように名前で参照させるために必要) - (プロジェクトルート)/(プロジェクト名)/urls.py <----- 自動で作成される -- プロジェクト全体のルーティング設定を書く - (プロジェクトルート)/(アプリケーションルート)/urls.py <----- 手動で作成する必要あり -- 各アプリケーション毎のルーティング(=view との対応関係) ** Model (データー構造の定義) [#xb793560] *** Django における DB の扱い方(SQL 文は一切使わなくても良い) [#ff0e3b62] - DjangoではORM(オブジェクト関係マッピング)を使ってデータベースを操作 - DjangoのORMの実装は [[ActiveRecord:https://qiita.com/kimioka0/items/8c10e01def23fdbf3aa6]] パターンを採用 -- モデルを設計すればマイグレーション機能でDB定義を自動作成する。 -- SQLの記述は不要。モデルマネージャ経由でクエリを実行するとレコードをモデルのインスタンスとして扱える。 -- クエリは人間に読みやすく命名されたマネージャメソッドの連鎖として表現する - [[Django データベース操作 についてのまとめ:https://qiita.com/okoppe8/items/66a8747cf179a538355b]] |大分類|小分類|メソッド|h |全件取得する||all()| |検索条件を指定|肯定|filter(**kwargs)| |~|NOT条件|exclude(**kwargs)| |~|IN条件の中身をリストで指定する|in_bulk(id_list=None, field_name='pk')| |並び順を指定|昇順|order_by(*fields)| ||降順|reverse()| |クエリセットからインスタンスを取得する|検索に一致したもの|get(**kwargs)| |~|指定した日付順で最新のもの|latest(*fields)| |~|指定した日付順で最古のもの|earliest(*fields)| |~|クエリセットの先頭|first()| |~|クエリセットの最後|last()| |レコードを新規登録する|新規登録|create(**kwargs)| |~|条件に一致するものを取得、無い場合は新規登録して取得|get_or_create(defaults=None, **kwargs)| |~|条件に一致するものを更新、無い場合は新規登録|update_or_create(defaults=None, **kwargs)| |一括登録する||bulk_create(objs, batch_size=None)| |一括更新する||update(**kwargs)| |一括削除する|| delete()| |特定のフィールドを集計する|テーブル内の集計|aggregate(*args, **kwargs)| |参照先テーブルの集計||annotate(*args, **kwargs)| |該当のレコード件数||count()| |抽出したレコードから重複の無い日付配列を作る||dates(field, kind, order='ASC')| |抽出したレコードから重複の無い時刻配列を作る||datetimes(field_name, kind, order='ASC', tzinfo=None)| |クエリセットの存在チェックをする||exists()| |インスタンスの一部だけを取得|結果を辞書のリストで取得する|values(*fields, **expressions)| |結果をタプルのリストで取得する||values_list(*fields, flat=False, named=False)| |特定のフィールドの重複無しリストを作る||distinct(*fields)| |SQLを直接実行する|SQLを直に設定する(一部)|extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)| |~|SQLを直に設定する(全部)|raw(raw_query, params=None, translations=None)| |クエリセット同士の集合演算をする|和集合|union(*other_qs, all=False)| |~|積集合|intersection(*other_qs)| |~|差集合|difference(*other_qs)| |パフォーマンスチューニング キャッシング|SQLのJOIN句を使って関係先を同時に取得しキャッシュする。1-1,N-1で有効|select_related(*fields)| |~|関係先を先行取得してキャッシュする。1-N、N-Nで有効。|prefetch_related(*lookups)| |パフォーマンスチューニング 重いカラムを取得しない|指定カラムだけ取得しない|defer(*fields)| ||指定カラムだけ取得する|only(*fields)| |パフォーマンスチューニング 巨大なテーブルをループで扱う||iterator(chunk_size=2000)| |データベースのレコードロックを使う||select_for_update(nowait=False, skip_locked=False, of=())| |デフォルト以外のデータベースを使う||using(alias)| |空のクエリセットを作る||none()| *** Django 外で作成した DB を利用するには制約がある場合も [#u820f8f6] - &color(red){DJANGO で利用するデーターベースは model の中で予め紐付けて定義する必要がある}; - [[モデルフィールド 設定テンプレート:https://qiita.com/okoppe8/items/a1149b2be54441951de1]] - &color(red){DJANGO からデーターベースを操作する場合には、専用に用意された記述を用いる。ほぼ SQL 文を書くことは無い}; -- [[データベース操作まとめ:https://e-tec-memo.herokuapp.com/article/10/]] *** model と DB の同期処理 (=migration) [#z77f6ca2] - &color(red){モデル変更時には以下のコマンドでデータベースに更新を反映する}; -- python3 manage.py makemigrations (アプリケーション名) -- python3 manage.py migrate [local] munakata:~/source/python/vlman/vlmanui$ python3 manage.py makemigrations Migrations for 'fview': fview/migrations/0001_initial.py - Create model Filedir [local] munakata:~/source/python/vlman/vlmanui$ python3 manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, fview, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying fview.0001_initial... OK Applying sessions.0001_initial... OK - migration のリセット -- アプリケーションディレクトリーにある migrations というフォルダーを削除 -- キャッシュデータのクリア (vman) munakata@muna-E450:~/code/python/vman/vmanhmi$ python manage.py shell Python 3.6.8 (default, Jan 14 2019, 11:02:34) [GCC 8.0.1 20180414 (experimental) [trunk revision 259383]] on linux Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from django.core.cache import cache >>> cache.clear() -- データーベースの中の django_migrations というテーブルから、該当アプリケーションのレコードを削除する sqlite> select * from django_migrations; <------- 該当アプリの変更履歴が記録されている sqlite> delete from django_migrations where app='fview (=アプリ名)'; sqlite>.exit (vman) munakata@muna-E450:~/code/python/vman/vmanhmi$ python manage.py makemigrations (vman) munakata@muna-E450:~/code/python/vman/vmanhmi$ python manage.py migrate -- manage.py showmigrations で該当アプリのマイグレーション履歴が消えている事を確認 -- manage.py makemigrations (アプリ名) で明示的にアプリ名を指定して migrations ファイルを生成 (vman) munakata@muna-E450:~/code/python/vman/vmanhmi$ python manage.py makemigrations fview -- それでも migrate 処理がエラーになる場合には fake migrate を実行する (vman) munakata@muna-E450:~/code/python/vman/vmanhmi$ python manage.py migrate --fake fview zero (vman) munakata@muna-E450:~/code/python/vman/vmanhmi$ python manage.py migrate fview -- [[(参考) manage.py migrateをおかしな感じにやってしまった:https://qiita.com/ajitama/items/130b2f31abd855937a93]] *** pylint の Class '(クラス名)' has no 'objects' member エラー対策 [#m1fad130] - pip install pylint-django - vscode の 設定 → ユーザー設定 (setting.json) に以下を記述 "python.linting.pylintArgs": [ "--load-plugins=pylint_django" ], *** Django キャッシュクリア (データーベース更新時など) [#t89f0cf5] -- from django.core.cache import cache -- cache.clear() munakata@muna-E450:~/code/python/vman/vmanhmi$ python manage.py shell Python 3.6.8 (default, Jan 14 2019, 11:02:34) [GCC 8.0.1 20180414 (experimental) [trunk revision 259383]] on linux Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from django.core.cache import cache >>> cache.clear() ** View(ビジネスロジック) [#we18c449] - Djangoのビューは以下の条件を満たす必要がある -- request オブジェクトを(第一引数として)受け取る -- callable であること -- response オブジェクトを返す - view には各アプリケーション内でデーターベース内のデータの操作(CRUD=新規作成、リード、更新、削除)を行うためのロジック(=関数)を規定する - python の関数として定義する方法 - python の Class として定義し、(クラス名).as_view() でクラスをビュー関数化する方法 <--- こちらが推奨 ** Template(Look & Feel の規定) [#xbe65980] *** bootstrap4 の導入 [#w7165802] - 1) [[bootstrap:https://getbootstrap.com/]] - 2) [[jquery:http://jquery.com/]] munakata@muna-E450:~/code/python/vman/vmanhmi$ tree (project root) ├── fview (application root) │ (snip) │ ├── static <------ 各アプリの下に専用の css/js を配置する │ │ └── fview │ │ ├── css │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ └── js │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.bundle.min.js.map │ │ ├── jquery-3.4.1.min.js │ │ └── jquery-3.4.1.min.map - 3) [[django-bootstrap4:https://pypi.org/project/django-bootstrap4/]] のインストール pip3 install django-bootstrap4 munakata@muna-E450:~/code/python/vman/vmanhmi$ pip install django-bootstrap4 Collecting django-bootstrap4 Downloading https://files.pythonhosted.org/packages/02/5a/485d61f6dafa4e4d001a7880b04f40f04fe485a54b2756b0536ed2052342/django-bootstrap4- 0.0.8.tar.gz Building wheels for collected packages: django-bootstrap4 Running setup.py bdist_wheel for django-bootstrap4 ... done Stored in directory: /home/munakata/.cache/pip/wheels/f6/58/7f/fcdbcc8c631cc8f775cb8b9349ded99aff7a3f5b28e0f40ef3 Successfully built django-bootstrap4 Installing collected packages: django-bootstrap4 Successfully installed django-bootstrap4-0.0.8 - bootstrap4 ボタン [#tc3adc6f] -- &ref(button1.jpg); -- &ref(button2.jpg); -- [[Bootstrap4に用意されているクラス【ボタン編】:https://webnetamemo.com/coding/bootstrap4/201711096011]] *** [[font-awesome:https://fontawesome.com/]] [#q83736bd] - e-mail = g-mail - pass = frex7785 - my kit ID = [[f70a45d16f:https://fontawesome.com/kits/f70a45d16f/use?welcome=yes]] <script src="https://kit.fontawesome.com/f70a45d16f.js"></script> - インストール munakata@muna-E450:~/code/python/vman/vmanhmi$ pip install pipenv munakata@muna-E450:~/code/python/vman/vmanhmi$ pipenv install django-fontawesome-5 - [[(参考URL) django-fontawesome-5 1.0.16:https://pypi.org/project/django-fontawesome-5/]] *** choice に対応した専用フォーム(ラジオボタン、ドロップダウンリストなど) [#z5105dcc] - [[Djangoフォームを使ってみよう:https://eiry.bitbucket.io/tutorials/tutorial/forms.html]] *** ページネーション [#ub08d1af] - [[Django 1.0 Documents ペジネータ (paginator):https://djangoproject.jp/doc/ja/1.0/topics/pagination.html]] - [[Django、ページング処理まとめ:https://narito.ninja/blog/detail/89/]] - [[Djangoで、前のデータ・次のデータを取得する:https://narito.ninja/blog/detail/79/]] *** url タグ(別画面へのリンク)= {% url '{namespace}:{name}' %} [#adbecea4] - namespace = プロジェクト全体の urls.py に指定された名前 - name = アプリケーションの urls.py に指定された名前 <a href="{% url 'fview:list_view' %}" class="btn btn-primary btn-sm my-3">ファイル一覧に戻る</a> *** url タグでページ遷移する時に引数を渡す方法 [#p2504b5f] - 第三引数に引数名、外側に該当する変数名を書く <a href={% url 'app:table' table.id %}>{{ table.name }}</a> -[[Django - URLに引数を指定してページ遷移する:https://hiroronn.hatenablog.jp/entry/20190305/1551742344]] *** <a タグを使って別ページへのリンクを指定するときには view で規定したメソッド名を使う [#j3b02dc4] - 下記の例では fview:detail_view は view 内でメソッド関数として定義した detail_view を呼び出している <a scope="row" href="{% url 'fview:detail_view' fid=vfile.file_ID %}" class="btn btn-outline-primary btn-sm">{{ vfile.file_ID }}</a> - アプリケーションの urls.py で name= で規定した名前が URL の逆引きに利用される - &color(red){ここでは urls.py 内で規定した name と view 内のメソッド名が同じ、どちらを参照しているのかわからない! 駄目実装だな}; *** 画面遷移を伴わない イベントによるデータ操作 [#heb74024] - template に POST 処理を書いて view で受け取る <form action="" method="POST"> {% csrf_token %} <button type="submit" class="btn btn-outline-success btn-sm">お気に入りに登録</button> </form> - [[Djangoで、フォームの内容を保持する(セッションを使う方法):https://narito.ninja/blog/detail/46/]] *** {% csrf_token %} = クロスサイトリクエストフォージェリ(CSRF) 対策 [#u2e93832] - POST 処理がエラーとして誤検出されないようにする目的で追加する必要がある *** 画像の取扱い [#za579f2d] - DJANGO は Instagram に使われているくらいなので写真のデーターベース検索に関する機能が充実しているが、ポイントは&color(red){ データベースには実際の写真データは登録しないが DJANGO の操作としては DB に登録するように見せて実際にはファイル名、Media_Root からのパス名だけを DB に入れる仕組みを使っていること。このため DJANGO の view を使って写真を登録しないと写真が認識(=検索)出来ない}; - 画像を扱うには pillow というライブラリが必要 munakata@muna-E450:~/code/python/vmanhmi/fview/templates/fview$ pip install pillow Collecting pillow Using cached https://files.pythonhosted.org/packages/d2/c2/f84b1e57416755e967236468dcfb0fad7fd911f707185efc4ba8834a1a94/Pillow-6.0.0-cp36-cp36m-manylinux1_x86_64.whl Installing collected packages: pillow Successfully installed pillow-6.0.0 -- プロジェクトルートディレクトリに media ディレクトリーを作成 -- settings.py に以下の行を追加 # media file for "pillow" MEDIA_ROOT = os.path.join(BASE_DIR, 'media') MEDIA_URL = '/media/' -- ルート URL ルーティングファイル (urls.py) に以下の行を追加 # To support media directory path finding from . import settings from django.contrib.staticfiles.urls import static from django.contrib.staticfiles.urls import staticfiles_urlpatterns urlpatterns += staticfiles_urlpatterns() urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) -- model に画像を扱うための設定を追加(upload_toを’documents’にしておくことで、/media/documentsに画像が保存される) class Document(models.Model): description = models.CharField(max_length=255, blank=True) photo = models.ImageField(upload_to='documents/', default='defo') uploaded_at = models.DateTimeField(auto_now_add=True) ImageField 型を宣言することが重要な意味を持つ --- データが画像データであるかの validity check が行われる --- 更に実際の画像データ(バイナリー)はデーターベースには登録せず、実際のファイルの場所へのリンク情報を登録する - 参考 URL -- [[Display Image in a Django Template (using ImageField):https://matthiasomisore.com/web-programming/display-image-in-a-django-template-using-imagefield/]] -- [[Djangoで画像アップローダを作る:https://hombre-nuevo.com/python/python0065/]] -- [[画像をアップロードして表示する:https://ymgsapo.com/2018/11/05/show-image-imagefield/]] *** 画面分割 = boostrap4 のグリッドシステム [#u1287e3b] - container 領域内を水平方向に 12分割 された領域に画面を分解可能 (例. 8 + 4 = 12) - &ref(twelve-sectors2.jpg); <div class="container"> <div class="row"> <div class="col-lg-8"> <div class="row"> <div class="col-xs-6"></div> <div class="col-xs-6"></div> <div class="col-xs-6"></div> <div class="col-xs-6"></div> </div> </div> <div class="col-lg-4"></div> </div> </div> - &ref(layout-example2.jpg); - [[Bootstrapのグリッドシステムの使い方を初心者に向けておさらいする:http://websae.net/twitter-bootstrap-grid-system-21060224/]] ** [[django/channels (非同期処理拡張):https://github.com/django/channels]] [#c7dd2bb2] - [[公式ドキュメント(英語):https://channels.readthedocs.io/en/latest/]] *** [[コンセプト (Django ChannelsでできるリアルタイムWeb 引用):https://qiita.com/massa142/items/cbd508efe0c45b618b34]] [#k7343b96] -- &ref(ASGI_0.jpg); -- &ref(ASGI_1.jpg); *** インストール [#i436d49e] (vman) munakata@muna-E450:~/code/python/vman$ pip3 install channels channels_redis asgi_redis - [[radis をインストール:https://symfoware.blog.fc2.com/blog-entry-2175.html]]( Linux マシンのパッケージとして) munakata@mvc:~$ sudo apt install redis munakata@mvc:~$ redis-server -v Redis server v=4.0.9 sha=00000000:0 malloc=jemalloc-3.6.0 bits=64 build=9435c3c2879311f3 munakata@mvc:~$ ss -nlt State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 0.0.0.0:111 0.0.0.0:* (snip) LISTEN 0 128 127.0.0.1:6379 0.0.0.0:* <------ Radis はデフォルトで 127.0.0.1 だけを受け付ける設定 -- [[Redisのインストール・セットアップ (自動起動の設定など) :https://qiita.com/KurosawaTsuyoshi/items/f8719bf7c3a10d22a921]] - routing.py をプロジェクト直下(アプリの下ではなく)に作成 from channels.routing import ProtocolTypeRouter application = ProtocolTypeRouter({ # (http->django views is added by default) }) - setting.py の installed apps に channels を追加 INSTALLED_APPS = [ 'channels', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'fview', 'bootstrap4', 'fontawesome_5', ] - channels 2.0 では (参考にした記事で使っている) delay server は無くなり、以下の機能を利用するようになったようだ [[リンク:https://channels.readthedocs.io/en/latest/one-to-two.html?highlight=delay#delay-server]] Delay server If you used the delay server before to put things on hold for a few seconds, you can now instead use an AsyncConsumer and asyncio.sleep: class PingConsumer(AsyncConsumer): async def websocket_receive(self, message): await asyncio.sleep(1) await self.send({ "type": "websocket.send", "text": "pong", }) - setting.py に ASGI (=Asynchronous Server Gateway Interface) の起点を登録する。 # websocket (channels) のために ASGIの起点を指定 ASGI_APPLICATION = 'vmanhmi.routing.application' - runserver 時にASGI が有効になっている事を確認する System check identified no issues (0 silenced). July 20, 2019 - 15:43:38 Django version 2.2.2, using settings 'vmanhmi.settings' Starting ASGI/Channels version 2.2.0 development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C. - ASGI サーバーが起動するようになって以降、web サーバーを停止してもバックグラウンドで 127.0.0.1:8000 を Listen しているプロセスが残るようになり、サーバー再起動時に以下のエラーを吐くようになった Listen failure: Couldn't listen on 127.0.0.1:8000: [Errno 98] Address already in use. -- lsof -i:(ポート番号) で 8000番ポートを使っているプロセスを特定しマニュアルでプロセスを kill する munakata@muna-E450:~/code/python/vman/vmanhmi$ lsof -i:8000 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME chrome 3143 munakata 57u IPv4 57585 0t0 TCP localhost:56236->localhost:8000 (ESTABLISHED) python3 4055 munakata 26u IPv4 57446 0t0 TCP localhost:8000 (LISTEN) python3 4055 munakata 29u IPv4 57621 0t0 TCP localhost:8000->localhost:56242 (CLOSE_WAIT) python3 4055 munakata 30u IPv4 55067 0t0 TCP localhost:8000->localhost:56236 (ESTABLISHED) python3 4055 munakata 31u IPv4 58463 0t0 TCP localhost:8000->localhost:56246 (CLOSE_WAIT) python3 4055 munakata 32u IPv4 57175 0t0 TCP localhost:8000->localhost:56244 (CLOSE_WAIT) munakata@muna-E450:~/code/python/vman/vmanhmi$ sudo kill 3143 4055 munakata@muna-E450:~/code/python/vman/vmanhmi$ lsof -i:8000 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME python3 4055 munakata 26u IPv4 57446 0t0 TCP localhost:8000 (LISTEN) python3 4055 munakata 29u IPv4 57621 0t0 TCP localhost:8000->localhost:56242 (CLOSE_WAIT) python3 4055 munakata 30u IPv4 55067 0t0 TCP localhost:8000->localhost:56236 (CLOSE_WAIT) python3 4055 munakata 31u IPv4 58463 0t0 TCP localhost:8000->localhost:56246 (CLOSE_WAIT) python3 4055 munakata 32u IPv4 57175 0t0 TCP localhost:8000->localhost:56244 (CLOSE_WAIT) -- が、LISTREN ポートのプロセスが死なない。 &color(red){kill -s QUIT}; で強制停止できた munakata@muna-E450:~/code/python/vman/vmanhmi$ kill -s QUIT 4055 munakata@muna-E450:~/code/python/vman/vmanhmi$ lsof -i:8000 *** [[websocket の基本:http://yagisuke.hatenadiary.com/entry/2018/01/28/181954]] [#x5cfe4eb] - WebSocketはフレーム探知で送受信しますが、相手が決まっているために送信先の情報などはもちません(一対一の通信) - WebSocketが他のHTTPベースのプロトコル異なるのは「ステートフルな通信である」という点 - 通信はサーバーが受信を受けている状態で、必ずクライアントから接続 (クライアント起点の通信) *** python プログラムから Websocket を利用する方法 [#w401aec6] - Websocket は通常は Java Script の中で送受信するが、今回は素の python プログラム(実際には Django Managment.py 配下のプログラム)として動作させるワーカープログラムの中から websocket で Django Channel の ASGI Consumer と通信させたい。 - python で websocket を使うためのライブラリーは2種類 (websocket-client, websockets) ある - [[python websockets API1 (websocket-client):https://pypi.org/project/websocket_client/]] [#j55b2e59] - [[python websockets API2 (websockets):https://pypi.org/project/websockets/]] [#n0adc042] -- pip install websockets --- Here’s how a client sends and receives messages: #!/usr/bin/env python import asyncio import websockets async def hello(uri): async with websockets.connect(uri) as websocket: await websocket.send("Hello world!") await websocket.recv() asyncio.get_event_loop().run_until_complete( hello('ws://localhost:8765')) --- And here’s an echo server: #!/usr/bin/env python import asyncio import websockets async def echo(websocket, path): async for message in websocket: await websocket.send(message) asyncio.get_event_loop().run_until_complete( websockets.serve(echo, 'localhost', 8765)) asyncio.get_event_loop().run_forever() *** Channels (ワーカーと consumer 間の通信のは channel を使う、websocket は使えない) [#k2e4470c] - backscan_worker.py (← manage.py のカスタムコマンド)を worker として動かし consumer と通信させる。 - そのままでは &color(red){backscan_worker は Radis バックエンドと繋がっていない}; のでエラーになる munakata@muna-E450:~/code/python/vman/vmanhmi$ python3 manage.py runworker prog-sync [16/Aug/2019 16:05:22] INFO [django.channels.worker:40] Running worker for channels ['prog-sync'] (snip) File "/home/munakata/code/python/vman/lib/python3.6/site-packages/channels/routing.py", line 61, in __call__ "No application configured for scope type %r" % scope["type"] ValueError: No application configured for scope type 'channel' - そんな事はない。1つの websocket 接続で複数の通信をハンドルすることができる -- [[How to use multiple websocket connections using Django Channels?:https://stackoverflow.com/questions/52446540/how-to-use-multiple-websocket-connections-using-django-channels]] from django.conf.urls import url from channels.routing import ProtocolTypeRouter, URLRouter from channels.auth import AuthMiddlewareStack application = ProtocolTypeRouter({ # WebSocket chat handler "websocket": AuthMiddlewareStack( URLRouter([ url(r"^first_application/stream/$", app_2_consumers.AsyncApp1Consumer), url(r"^second_application/stream/$", app_2_consumers.AsyncApp2Consumer), ]) ), }) - 参考 URL -- [[Django Channels - Cannot send event to a consumer Ask Question:https://stackoverflow.com/questions/52487290/django-channels-cannot-send-event-to-a-consumer]] -- [[How to use ChannelNameRouter to communicate between Worker and Websocket (Django and Channels2.x)?:https://stackoverflow.com/questions/50199118/how-to-use-channelnamerouter-to-communicate-between-worker-and-websocket-django]] *** channels group_send を使った同期(イベント一斉送信) [#c182b297] - backscan_worker (Django custom command, standalone python program) と channels を使ってメッセージ送受信をする場合には、worker プログラム側も Redis に接続する必要がある。 - manage.py runworker backscan_worker prog-sync (=グループ名)で常駐化ができるが、この場合 channel からのメッセージを受け取って処理をする形になるので、コマンドラインからの単独実行と両立できない(まぁプログラムを分割すれば良い話ではあるが) - 結局 ブラウザー(=JavaScript) ⇔ Consumer 間とは別に ワーカー ⇔ Consumer 間に別の WebSocket による接続を行う - WebSocket にはアドレス情報を付加できない(組み込まれている)ので、ワーカー ⇒ ブラウザー に直接メッセージを送ることは出来ない - Consumer の中で別々のハンドラーを用意し、その間を group_send メッセージでイベントをブロードキャストする方法を採用した - group_send は非同期処理になるので async_to_sync で命令を wrapping する必要がある - それぞれのハンドラーに対して、明示的にグループへの登録、削除を行う必要がある -- グループへの参加 def connect(self): #print("websocket [browser] connected >>>>>") self.accept() async_to_sync(self.channel_layer.group_add)("fview_sync", self.channel_name) -- グループからの離脱 def disconnect(self, close_code): print("Web socket [browser] disconnect code = [" + str(close_code) + "]") async_to_sync(self.channel_layer.group_discard)("fview_sync", self.channel_name) self.close() - group_send 時には グループ名、type、メッセージ本体を指定する必要がある -- type は アプリ名.タイプ名 の形を指定する -- アプリ名_タイプ名 の名前の ハンドラーを別に用意する (. を _ に置き換える) --- https://github.com/andrewgodwin/channels-examples/blob/master/multichat/chat/consumers.py *** 参考 URL [#mba8e51e] -- [[Django 2.0 + Channels 2.xを使ってWebSocketを扱う(その1):http://www.denzow.me/entry/2018/03/25/235952]] -- [[Django 2.0 + Channels 2.xを使ってWebSocketを扱う(その2):http://www.denzow.me/entry/2018/03/27/002350]] -- [[Django 2.0 + Channels 2.xを使ってWebSocketを扱う(その3):http://www.denzow.me/entry/2018/04/03/002351]] -- [[Django 2.0 + Channels 2.1 でチュートリアルやっていく(1):https://poyo.hatenablog.jp/entry/2018/05/17/224247]] -- [[Django 2.0 + Channels 2.1 でチュートリアルやっていく(2):https://poyo.hatenablog.jp/entry/2018/05/17/233000]] -- [[Django 2.0 + Channels 2.1 でチュートリアルやっていく(3):https://poyo.hatenablog.jp/entry/2018/05/18/003000]] -- [[Django を WebSocket サーバにする:https://qiita.com/ekzemplaro/items/a6b81bd1d181fdd0cc24]] -- [[REAL-TIME PROGRESS UPDATES WITH DJANGO CHANNELS:http://thomasruble.com/blog/real-time-progress-updates-with-django-channels/]] --- &ref(The Fool - real-time progress updates with django channels.pdf); -- [[Django, Background Processes, and Keeping Users in the Loop:https://www.oddbird.net/2017/04/17/async-notifications/]] -- [[Finally, Real-Time Django Is Here: Get Started with Django Channels:https://blog.heroku.com/in_deep_with_django_channels_the_future_of_real_time_apps_in_django]] -- [[Getting Started with Django Channels:https://realpython.com/getting-started-with-django-channels/]] -- [[Django Channels — Talking to Channels from my non-Django application:https://medium.com/@johngrant/django-channels-talking-to-channels-from-my-non-django-application-7dec7ceb80a8]] -- [[Sending messages to groups in Django Channels 2:https://stackoverflow.com/questions/48855417/sending-messages-to-groups-in-django-channels-2]] ** Java Script から view を呼び出す [#u0378c2c] *** 参考 URL [#g0a51c67] - [[Djangoでリダイレクトのレスポンスを返してJavaScript(JS)でリダイレクトさせたい:https://awesome-linus.com/2019/04/03/django-return-redirect-and-redirect-js/]] ** [[subprocess.Popenによる詳細な外部プログラムの実行:https://pknight.hatenablog.com/entry/20170414/1492152828]] [#gf2b420d] 通常はcheck_allやcheck_output(およびrun)などを使えばよいのですが、子プロセスの終了をまたずに何か処理をしたいとかそういうことをしたい場合にはPopenを使います。 # -- test.py -- from time import sleep print('child start!') sleep(1) print('child end!') # -- main.py -- import subprocess print('parent start!') ps = subprocess.Popen(['python','test.py']) print('parent end!') $ py main.py parent start! parent end! child start! child end! ** [[Django アプリの中から 重い処理を別の実行プロセスに登録する:https://dot-blog.jp/news/django-async-celery-redis-mac/]] [#afd2b790] - Celery は非同期処理の登録の方法として使えるが(理解が正しければ)処理結果が出た時点で結果を返すことはできる - が処理の途中経過を通知する(=同期する)手段は提供されないと理解。 なので、この記事では別プロセスへの登録の部分だけが参考になると想像 -&ref(cerely_60.jpg); -- Task = 非同期処理をひとかたまりにまとめたもの。ブラウザからのリクエストによって必要となった実行処理する関数やその引数などが記録される -- Queue = タスクを格納する入れ物です。構造が先入れ先出しになっているものが Queue、後入れ先出しが FIFO -- Producer = タスクを作成してブローカーに渡す役割を持ったもの。『Celery Client』を利用 -- Broker = タスクをキューに登録したり、登録されたタスクをワーカーに渡したりするもの。『Redis』を利用 -- Woker = ブローカーによってキューから取り出されたタスクを実際に処理。『Celery Woker』を利用 -&ref(cerely2_60.jpg); *** [[やりたい実装 例:https://stackoverflow.com/questions/50130851/django-channels-background-worker-instances]] [#ne22e1d3] - &ref(django_woker.JPG); *** wscat (websocket テストツール) [#ucf2e2ee] - インストール(node.js のツールなので npm でインストール) munakata@muna-E450:~/code/python/vman/vmanhmi$ sudo npm install -g ws /usr/local/lib └── ws@7.1.1 - サーバーに接続 *** python プログラム内で別の python プログラムを起動する方法 [#uc2cb56c] - 実現方法 -- os.fork import os if os.fork()==0: long_process() else: return HttpResponse() -- subprocess import subprocess p = subprocess.Popen([sys.executable, '/path/to/script.py'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) (where the script is likely to be a manage.py command) -- threads import threading t = threading.Thread(target=long_process, args=args, kwargs=kwargs) t.setDaemon(True) t.start() return HttpResponse() --- &color(red){Due to the Global Interpreter Lock, in CPython only one thread can execute Python code at once}; (even though certain performance-oriented libraries might overcome this limitation). If you want your application to make better of use of the computational resources of multi-core machines, you are advised to use multiprocessing. However, threading is still an appropriate model if you want to run multiple I/O-bound tasks simultaneously. -- multiprocessing from multiprocessing import Process p = Process(target=_bulk_action,args=(action,objs)) p.start() return HttpResponse() - python interpreter には thread scheduler が無い。 故に multi-thread にしても並列化されない。 - これを GIL (Global Interpreter Lock) 問題とよぶ - python で並列化を実現するにはプロセス単位で実行を分ける(= thread scheduler を二重化する)のが良い - 参考 URL -- [[Django long running asynchronous tasks with threads/processing:https://stackoverflow.com/questions/8068945/django-long-running-asynchronous-tasks-with-threads-processing]] --- &ref(django-threads.pdf); -- [[PythonのGILについて簡単に調べてみました:https://blog.karky7.com/2014/12/pythongil.html?m=1]] -- [[[Python] multiprocessing 備忘録:http://note.crohaco.net/2017/python-multiprocessing/]] *** [[Integrating a data producer as worker to Django Channels 2.x:https://stackoverflow.com/questions/51083910/integrating-a-data-producer-as-worker-to-django-channels-2-x]] [#f4f18d73] *** [[(WebSocket を使わない方法) Djangoで、進捗表示する:https://torina.top/detail/403/]] [#b71879c8] ** Ajax (Javascript 内でクライアント内での動的ページ書き換えを記述する) [#y3ebeddc] *** Ajax の概念 [#kb87dae9] - Ajax = Asynchronous JavaScript + XML、&color(red){XMLHttpRequest}; による非同期通信を利用 -- Ajaxを利用すれば、サーバーから必要な情報だけを取得えきるので、ページ全体をリロードせず必要部分だけ更新できる(応答性の大幅な向上) -- [[AJAX in a nutshell の説明:https://hackernoon.com/ajax-in-a-nutshell-4770da7b16f3]] --- &ref(ajax_dialog2.jpg); -- [[django#AJAX の説明:https://qiita.com/soup01/items/f356d6ee09534007f76d]] --- まずイベントがWEB PAGEの中に発生。(例えばPAGEがLOADされた、ボタン押し) --- JavascriptはXMLHttpRequestオブジェクトを生成。 --- XMLHttpRequestはWEB Serverに要求を送る。 --- サーバー側が要求を処理する。 --- サーバー側が返事する。 --- Javascriptが返事内容をEncodeする。 --- 画面が変わる。 -- という事は、実際には多くの Web アプリケーションで Ajax 処理の活用がポイントとなる -- Ajaxアプリケーションでは &color(red){動的にページの一部が書き換えられる}; 為、デザインとコードが単純に分離できないという開発上の問題点が顕在化 -- こうした問題を解決するために Jquerry 等のアプリケーションフレームワーク(JavaScriptライブラリ)が開発された --- [[jQuery:https://jquery.com/]] --- [[Google Web Toolkit:http://www.gwtproject.org/]] --- [[Prototype JavaScript Framework:http://prototypejs.org/]] - Javascript 内の script セクションに Ajax 処理を追記する - Jquerry を使った簡易記述を利用することができる(一般的である) - Django の場合、実際のページ書き換え(クライアント内でのリロード) *** jQuerry (= Javascript Library) [#h27e77c4] - jQueryでスクリプトを書くときには &color(red){まずready関数を記述、}; その内側に実際の命令を記述します。 - jQueryを利用したスクリプトでは、 -- &color(red){(1)$("...")の内側にどの(X)HTMLの要素を操作するかをセレクターで指定}; -- &color(red){(2)その後ろに .(ドット) で処理の内容 (=jQueryの命令) を書く}; $(function(){ $("セレクター").jQueryの命令 }) - jQuerry で Ajax を使う場合の記述方法は異なる -- ajax()にはセレクターの指定はない -- $. で始まり、括弧内の {...} に設定(オプション)を記述 -- 設定は左側に項目名を、右側には項目に対応する値を記述し、左右は:(コロン)で区切ります。 -- 複数の設定は ,(カンマ)区切りでつなげていきます。 -- データーの取得に成功すると、取得内容は function(data){...} の「data」に入る -- success にはデータ取得成功時に実行する処理を function(data){...} の内側に記述する $.ajax({ url: '取得するXMLファイル', dataType: 'xml', success : function(data){ //取得したファイルに対する処理 } }) - セレクター 一覧 |名称|書式|指定対象|h |>|>|BGCOLOR(#5f9ef0):CSSでよく利用されるセレクター| |要素セレクター|$("要素名")|特定のhtml要素| |IDセレクター|$("#ID名")|特定のid属性の値を持つ要素| |クラスセレクター|$(".クラス名")|特定のclass属性の値を持つ要素| |子孫セレクター|$("要素1 要素2")|特定の要素の内側にある要素| |ユニバーサルセレクター|$("*")|すべての要素| |>|>|BGCOLOR(#5f9ef0):CSS2のセレクター| |グループセレクター|$("セレクター1,セレクター2,...")|複数のセレクター| |子セレクター|$("親要素名 > 子要素名")|特定の要素の直下の子要素| |隣接セレクター|$("要素1 + 要素2")|特定の要素の次の要素| |first-child擬似クラス|$("要素:first-child")|特定の要素内の最初の要素| |>|>|BGCOLOR(#5f9ef0):CSS3のセレクター| |間接セレクター|$("要素1 ~ 要素2")|特定の要素の後に出現する要素| |否定擬似クラス|$("要素1:not(要素2)")|特定の要素内の指定した要素以外の要素| |empty擬似クラス|$("要素:empty")|子要素やテキストを含まない要素| |nth-child擬似クラス|$("要素:nth-child(番号)")|特定の要素内の指定した番号の要素| |last-child擬似クラス|$("要素:last-child")|特定の要素内の最後の要素| |only-child擬似クラス|$("要素:only-child")|指定した要素が1つだけ含まれる特定の要素| |>|>|BGCOLOR(#5f9ef0):CSSの属性セレクター| |[attribute]|$("[属性名]")|特定の属性を持つ要素| |[attribute='value']|$("[属性名='値']")|特定の属性が指定した値を持つ要素| |[attribute!='value']|$("要素名[属性名!='値']")|特定の属性が指定した値を持たない要素| |[attribute^='value']|$("[属性名^='値']")|特定の属性が特定した値で始まっている要素| |[attribute$='value']|$("[属性名$='値']")|特定の属性が特定した値で終わっている要素| |[attribute*='value']|$("[属性名*='値']")|特定の属性が特定した値を含んでいる要素| |[attributeFilter1][attributeFilter2]|$("[属性セレクター1][属性セレクター2]")|複数の属性セレクターに該当する要素| |>|>|BGCOLOR(#5f9ef0):jQueryの独自フィルター| |firstフィルター|$("要素:first")|指定した要素の最初の要素| |lastフィルター|$("要素:last")|指定した要素の最後の要素| |evenフィルター|$("要素:even")|指定した要素の偶数番目の要素| |oddフィルター|$("要素:odd")|指定した要素の奇数番目の要素| |eqフィルター|$("要素:eq(番号)")|指定した番号の要素(番号は0から数える)| |gtフィルター|$("要素:gt(番号)")|指定した番号より後の要素(番号は0から数える)| |ltフィルター|$("要素:lt(番号)")|指定した番号より前の要素(番号は0から数える)| |headerフィルター|$("要素:header")|h1~h6までのheader要素| |containsフィルター|$("要素:contains(文字列)")|特定の文字列が含まれている要素| |hasフィルター|$("要素1:had(要素2)")|特定の要素が含まれている要素| |parentフィルター|$("要素:parent")|子要素やテキストを含む要素| *** jQuerry UI [#vd5f8349] - jQuery UIは、jQuery のユーザー・インターフェイス(UI)に関わる機能をを拡張する公式ライブラリ(プラグイン) - [[jQuery UIを利用するには?:https://www.buildinsider.net/web/jqueryuiref/0001]] - jQuery UI project は &color(red){2016年9月の [[ver.1.12.1:https://blog.jqueryui.com/category/releases/]] 以降更新が止まって}; いた。 - jQuery UI 1.12.1 は &color(red){jQuery 1.7.0 – 3.1.0}; に対応している - 今回は Ajax + jQuery 環境で jQuery UI が提供する progressbar を利用したいので、この環境に合わせた - jQuery UIで提供されているコンポーネント |分類|コンポーネント名|概要|h |Interactions|Draggable|ドラッグ可能な要素を定義| |~|Droppable|ドロップ可能な要素を定義| |~|Resizable|サイズ変更可能な要素を定義| |~|Selectable|マウス操作で選択可能な要素を定義| |~|Sortable|並べ替え可能な要素を定義| |Widgets|Accordion|アコーディオン・パネル| |~|Autocomplete|オート・コンプリート機能付きのテキストボックスを生成| |~|Button|ボタンやリンク、ラジオボタンなどからボタンを生成| |~|Datepicker|日付入力ボックスを生成| |~|Dialog|汎用的なダイアログを生成| |~|Menu|展開可能なリッチ・メニューを生成| |~|Progressbar|進捗(しんちょく)バーを生成| |~|Slider|スライダーを生成| |~|Spinner|アップダウン・ボタンを伴う数値入力ボックスを生成| |~|Tabs|タブ・パネルを生成| |~|Tooltip|ツールチップを生成| |Effects|Effect|基本的なエフェクトを提供| |~|Show/Hide/Toggle|要素の表示/非表示にエフェクトを適用| |~|Add/Remove/Toggle/Switch Class|スタイル・クラスの適用/解除にエフェクトを適用| |~|Color Animation|色を徐々に変化させるエフェクトを提供| - [[jQuery UI Progressbar 仕様:https://api.jqueryui.com/progressbar/]] - 参考 URL -- [[ASCII 初めてのjQuery:セレクターAPIを一挙解説(前編):https://ascii.jp/elem/000/000/439/439414/index-2.html]] -- [[ASCII 初めてのjQuery:セレクターAPIを一挙解説(後編):https://ascii.jp/elem/000/000/440/440882/index-5.html]] -- [[ASCII jQueryによるフォームのデザインの基礎(前編):https://ascii.jp/elem/000/000/452/452106/]] -- [[ASCII jQueryによるフォームのデザインの基礎(後編):https://ascii.jp/elem/000/000/456/456040/]] -- [[ASCII jQueryでAjaxを利用する基本チュートリアル:https://ascii.jp/elem/000/000/458/458236/]] -- [[:]] *** 参考 URL [#i5d1b223] - [[:]] - [[:]] - [[:]] - [[jQuery日本語リファレンス(Ajax/API/jQuery):http://semooh.jp/jquery/api/ajax/jQuery.ajax/options/]] - [[Django クロスサイトリクエストフォージェリ (CSRF) 対策 [Ajax]:https://docs.djangoproject.com/ja/2.2/ref/csrf/#ajax]] - [[s-cookie/js-cookie:https://github.com/js-cookie/js-cookie/]] - [[:]] - [[:]] ** vscode を使ったデバッグ [#x9aada46] - vscode 全般の説明は [[VsCode]] にまとめている - vscode を使ってdjango のデバッグができる。 -- vscode のデバッガー設定で python django を選択する -- デバッガーの RUN コマンド(緑色の矢印ボタン)でサーバーを起動する (別のターミナルで起動しない) -- ブラウザーでアプリを起動してブレークポイントにヒットすると vscode 画面に切り替わり、変数の内容などが確認できる ** 参考 URL [#u00645c3] -- [[Python Django入門 (1) Django イントロ:https://qiita.com/kaki_k/items/511611cadac1d0c69c54]] -- [[Python Django入門 (2) install mac編:https://qiita.com/kaki_k/items/e824cfcf089e75d43551]] -- [[Python Django入門 (3) Django 導入~Admin ツール:https://qiita.com/kaki_k/items/7b178ad39394a031b50d]] -- [[Python Django入門 (4) bootstrap による Web 画面作成:https://qiita.com/kaki_k/items/6e17597804437ef170ae]] -- [[Djangoチュートリアル - 汎用業務Webアプリを最速で作る:https://qiita.com/okoppe8/items/54eb105c9c94c0960f14]] -- [[IT技術相互 Python Django の使い方:https://jpn.itlibra.com/article?id=10796]] -- [[Djangoを最速でマスターする part1:https://qiita.com/gragragrao/items/373057783ba8856124f3]] -- [[Djangoを最速でマスターする part2:https://qiita.com/gragragrao/items/9a85a372a9c3eec06243]] -- [[Django入門編 (paiza トレーニング):https://paiza.jp/works/django/primer]]
テキスト整形のルールを表示する
添付ファイル:
ajax_dialog2.jpg
21件
[
詳細
]
django-threads.pdf
41件
[
詳細
]
django_woker.JPG
26件
[
詳細
]
cerely2_60.jpg
21件
[
詳細
]
cerely_60.jpg
22件
[
詳細
]
The Fool - real-time progress updates with django channels.pdf
64件
[
詳細
]
ASGI_1.jpg
20件
[
詳細
]
ASGI_0.jpg
20件
[
詳細
]
button2.jpg
25件
[
詳細
]
button1.jpg
27件
[
詳細
]
twelve-sectors2.jpg
20件
[
詳細
]
layout-example2.jpg
21件
[
詳細
]