以下の内容はhttps://cream-worker.blog.jp/tag/Pythonより取得しました。


Pythonのスター演算子

当たり前の話かもですが、最近気づいて使うようになったのでちょっと記載。

Javascriptにはスプレッド演算子っていうのがある。

const arr = [1,2,3]
console.log(...arr)

みたいな。中身をばらして引数とかに渡す場合に良く使うやつ。

で、Pythonにも似たようなのがある。それがスター演算子。

arr = [1,2,3]
print(*arr)

みたいな。

で、"**"にすると、dictでも展開できる。

引数で展開するときによく見るけど、普通に式の中でも使えるので(当たり前)、オブジェクトをdictで色々したい時に使えそうな感じ。まー、"__dict__"でやりたい事はほぼ出来るのですが。。。

{**object.__dict__}

PythonとNUXT3でSAML認証

凄い久しぶりに書く気がする。最近忙殺されすぎだな。少しずつ再開しましょう。。。


独自アプリにSAML認証付けたくなった。IdPはGoogle。

SAML認証は結構実装の情報少な目な感じがする。

認証フロー的なのはあまりちゃんと見てないのですが、

とりあえず動くようになったので流れを記載しておこう。

基本的なところは下記の先駆者さんの参考ページを見た方が確実。


まず、Pythonのバックエンド側。

自前で全部実装するのはかなり大変そうなので、コア部分はOSSを利用したい。

候補は下記2つ。

今回はpysaml2を利用。コア部分だけ使うならこっちの方がやりやすそうだったので。

公式に記載の通り、xmlsec1が必要みたいなので、事前にインストールしておく。

apt-get install xmlsec1
pip install pysaml2

そしたら、まず下記。

from saml2 import (
    BINDING_HTTP_POST,
    BINDING_HTTP_REDIRECT,
    entity,
)
from saml2.client import Saml2Client
from saml2.config import Config as Saml2Config

class SamlUtil:
    @classmethod
    def saml_client_for(cls):
        settings = {
            'entityid': 'https://*****/****',
            # 'metadata': {
            #     'remote': [
            #         {'url': 'https://*****/****'},
            #     ],
            # },
            "metadata": {
                "local": [
                    "/opt/app/samle-meta.xml",
                ],
            },
            'service': {
                'sp': {
                    'endpoints': {
                        'assertion_consumer_service': [
                            ("[call-back-url]", BINDING_HTTP_REDIRECT),
                            ("[call-back-url]", BINDING_HTTP_POST),
                        ]
                    },
                    "name_id_policy_format": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
                    'allow_unsolicited': True,
                    'authn_requests_signed': False,
                    'want_assertions_signed': False,
                    'want_response_signed': False,

                }
            }
        }

        spConfig = Saml2Config()
        spConfig.load(settings)
        spConfig.allow_unknown_attributes = True
        saml_client = Saml2Client(config=spConfig)
        return saml_client
        
    @classmethod
    def saml_request(cls):
        saml_client = cls.saml_client_for()
        req_id, info = saml_client.prepare_for_authenticate()
        return (req_id, info)

settingsの中身がまず肝。

GoogleでのSAML認証の場合というか、だいたいどこも同じだと思うけど、

IdP側へのアプリの登録時に以下の情報を入れる。

EntityIdとコールバックのURL。

EntityIdは大体アプリのURLが多いんじゃないでしょうか。

コールバックのURLは適宜自分のアプリに応じて。

entityidとendpointsの内容は上記で指定しているはずの内容。

自分はemailのみ欲しいので、name_id_policy_formatでemailを指定。

この辺の細かい設定内容は公式のこの辺参照

もう一つの肝が`metadata`。

ネット上の良くあるサンプルだと、IdP側のメタデータ取得用のURLをremoteで記載している場合が多い。

GoogleでのSAML認証の場合、やり方は色々あると思うのですが、メタデータのXMLを予めDLしておける。

DLした該当ファイルのローカルでのパス指定でも同様に指定可能。その場合は上述のサンプルの記載の感じ。

※いちを記載しておくと、こういうファイルはGitHubとかに上げたらダメ。

で、ここまでpysaml2でリクエストURLを作成できる。

上述のコード上だと`req_id, info = saml_client.prepare_for_authenticate()`の箇所。

infoが以下のようなdictなので、これをフロントに返してリダイレクトなりさせる。

{
  "headers": [
    [
      "Location",
      "https://*****?idpid=******&SAMLRequest=[文字列の羅列]"
    ]
  ],
  "data": [],
  "status": 303,
  "url": "https://[認証URL]",
  "method": "GET"
}

headersのLocationのURLがクライアントがブラウザでつなぐ先のURL。

SAMLRequestのパラメータがSAMLリクエストのxmlをURLエンコードしたもの。

この文字列の内容を見たい場合はこのツールサイトあたりでSAMLデコードすると何かいてるか見れる。

で、クライアントでLocationのURLにアクセスすると、今回の場合はGoogle側で認証確認して、

コールバック先のURLにPOSTでリクエストが戻ってくる。

POSTではBODYにSAMLResponseの名前でレスポンスのXMLをURLエンコードした文字列が入ってくる。

これのパースはpysaml2で下記の様にしてパースできる。

saml_response = req.data.get("SAMLResponse")  # この値の取得は適宜いい感じに。。。
saml_client = cls.saml_client_for()
authn_response = saml_client.parse_authn_request_response(saml_response, entity.BINDING_HTTP_POST)
authn_response.parse_assertion()
user_info = authn_response.get_subject()

# 例えばメタデータでemail指定してる場合はuser_info.textにその値が入ってる。
user_email = user_info.text

具体的に生のXMLがどういう内容かは前述のツールサイトでデコードすれば見れる。

あと、request時のIDに一意なIDがセットされるので、検証しておいた方がよいかな。

ここまでくれば、後は独自アプリ側の認証情報をどう作るか次第。


フロントはNUXT3なのですが、NUXT3のみでも上手い事やれば完結出来そうな感じがした。

NUXT3はserver側でのリクエストのみで処理させることも出来る。

公式:https://nuxt.com/docs/guide/directory-structure/server

コールバックのURLを例えば、sso/saml/callbackとかにした場合、

`server/routes/sso/saml/callback.ts`のようにファイル作って、下記の様に記述すれば、

SAMLResponseをPOSTで受け取って処理できる。

※参考に載せたURLのISSUEで記載されているのは少し古いやり方なので、今のNUXT3では動作しません。

import { defineEventHandler, readBody } from "h3";

export default defineEventHandler(async (event) => {
  if (event.node.req.method === "POST") {
    const backUrl = "http://サーバ側からバックエンドのAPI叩く場合のURLとか";
    const data = await readBody(event); // POSTのデータを読み込み
    const samlData = await $fetch(backUrl, {
        method: "POST",
        body: {saml_response: data.SAMLResponse}
    });
    await sendRedirect(event, [リダイレクトさせたいURLとか], 302)
  }
});

この辺のやり方が分かってればNUXT3のみで完結も出来そう。

ただ、XMLのパースとかURLエンコードとか結構大変そうかな。

もっと簡単に出来る方法はないんだろうか。

Python インクリメントもどき

Pythonには他の言語で言うところのインクリメントの式がない。

「i++」こういうやつ。

言うほど困らないのですが、例えば、

a["hoge"] = x[i++]
a["fuga"] = x[i++]

みたいな事、書きたくなった時に1行で評価するの書けないので、

i += 1
a["hoge"] = x[i]
i += 1
a["fuga"] = x[i]

みたいに書くしかない。

どうにかして「x[i++]」みたいな書き方したいなーと、やっきになってみた。

グローバル変数的なものとか、クラス変数みたいな外部に変数置くのは無しで。


結論:無理矢理出来た。

他にもいいやり方あるんだろうなーとは思いますが、ちょっと探してみても出てこなかったので記載。

  • 結果のコード
def make_counter():
    def count_up(dummy=[]):
        dummy.append(1)
        return len(dummy)

    return count_up

counter = make_counter()

x[counter()] = 1
x[counter()] = 2
x[counter()] = 3


Pythonの関数定義でdefault値を定義出来るのですが、このdefault値って結構特殊な動きをする。

公式にも確か注意してね、みたいな事が書いてあったと思う。

特にlistとかdictみたいなオブジェクトをdefault値にすると、デフォ値の参照が固定される。

def xyz(a=[]):
   a.append(1)
   print(a)

xyz() -> [1]
xyz() -> [1,1]
xyz() -> [1,1,1]
xyz() -> [1,1,1,1]

↑みたいな。で、この性質を使って無理矢理作った感じ。


めちゃんこダサいのですが、まー1行で評価までいけたからとりあえずいいかな?

他にいい方法あったらぜひぜひ教えていただきたいです。

Python Django+uWSGIの設定

公式に載ってるやつだと上手くいかないので特筆。


結論から言うと下記コマンド。

uwsgi --chdir=/path/to/your/project \
    --module=mysite.wsgi:application \
    --env DJANGO_SETTINGS_MODULE=mysite.settings \
    --home=/path/to/virtual/env \
    --master --pidfile=/tmp/project-master.pid \
    --http=0.0.0.0:8088 \
    --processes=5 \
    --harakiri=20 \
    --max-requests=5000 \
    --vacuum \
    --buffer-size=32768

socket じゃなくて http。

buffer-sizeの指定が必要。

これやらないと↓のエラーが出る。

# buffer-size指定なしだとuwsgi側のログで↓
invalid request block size: 21573 (max 4096)...skip

# httpにしておかないと、ブラウザ側(Chrome)で↓
ERR_EMPTY_RESPONSE

socket指定はnginxでリバースプロキシ構成にしてdjangoにアクセスする場合。

直でブラウザからuwsgi叩く時はhttpにしておかないとダメ。

あとバッファがデフォで4kbで少ないので多めにセットしてあげないとほとんど動かない。

ファイルで指定の場合は↓の感じ。

[uwsgi]

chdir=/path/to/your/project
module=mysite.wsgi
home=/path/to/virtual/env
env=DJANGO_SETTINGS_MODULE=mysite.settings

http=0.0.0.0:8088
buffer-size=32768
master=true
max-requests=5000
processes=3
enable-threads=true
vacuum=true
env=LANG=en_US.UTF-8

PythonとサロゲートとIVS

PythonのIVS(異字体セレクタ)対応を調べてたのですが、全く出てこない...

誰も困ってないのかしら?

Pythonで普通に文字数とか取得しようとするとIVSはとりあえずダメになると思うんだけど。

s = '\U00008FBB'
print(s, len(s))  # 辻(2点ツジ), 1
s = '\U00008FBB\U000E0102'
print(s, len(s))  # 辻(1点ツジ), 2

色々調べたのですが、lenとかsubstringとか良い感じにカウントしたり、処理したりしてくれるライブラリ的なものも無い。。。

どーなんだろ。

まーいいや。ないなら作るしかあるまい。

という事で作った。

# 文字数カウント
def newLen(s):
    l = re.findall(
        '([\uD800-\uDBFF][\uDC00-\uDFFF]|[^\uD800-\uDFFF])[\U000E0100-\U000E01EF]?', s)
    return len(l)

# substring
def newSubstring(s, st, ed):
    l = re.findall(
        '([\uD800-\uDBFF][\uDC00-\uDFFF]|[^\uD800-\uDFFF])[\U000E0100-\U000E01EF]?', s)
    return ''.join(l[st:ed])

substringの方はかなり適当でエラー処理的なものとかガン無視。

重要なのは正規表現でサロゲとIVS考慮で一文字ずつの配列に変換して、

その配列の要素数やsliceでsubstringの代替をする感じ。

出来上がる配列は↓の感じ。

"あ\uE000\U00008205\U00008204\U000E0101\U00020C25\U0002B7CB\U000E0102"
↓
["あ","\uE000","\U00008205","\U00008204\U000E0101","\U00020C25","\U0002B7CB\U000E0102"]
※[普通の字 , 外字 , UNI , UNI+IVS , サロゲ , サロゲ+IVS]

ちなみに、javascriptでもほぼ同じロジックで同じこと出来るはず。

ただ、まともに検証してないので、今度ちゃんとやろ。

Python Djangoで単体テストの結果をマージ

Djangoで単体テストやってる時の話。規模が大きくなると実行対象分割して実行させたくなる。分割の仕方は色々あると思うので割愛。

で、分割実行した場合って結果のcovrageとかも分割されるので、結果をどうにかマージしたい。

jenkinsなんかに表示させてる場合、xunit形式の結果と.coverageが必要になる。

これをどうマージするのかという話。


coverage

Coverage.pyというのがあり、それを使う。というかDjangoの中でも使ってる模様。

普通の単体テストは「python manage.py test」みたいに実行すると思うのですが、

色々やりにくいので、Coverage.pyを通して実行するように変える。↓の感じ。

coverage run --source='.' manage.py test app/tests

で、これだとcoverageのデータファイルが全部「.coverage」になっちゃうので、ファイル名を変えるために下記にする。

COVERAGE_FILE=.coverage_datafile coverage run --source='.' manage.py test app/tests

環境変数の「COVERAGE_FILE」にファイル名指定しておいてから実行するらしい。

そうすると分割したテスト毎にファイル名固定出来るのであとはそれを最後に下記でマージする。

coverage combine .coverage_*

「.coverage_」でファイルを全部指定みたいな。上を実行すると「.coverage」ってデータファイルにまとまるので、あとは、「coverage html」とか「coverage xml」何かで必要なレポート形式に変換する。


xunit

xunitも同様にマージが必要。とりあえず、テストの実行時に下記のオプション指定して結果のファイル名を実行毎に指定してあげる。

manage.py test app/tests/target --with-xunit --xunit-file nose_result_xxx.xml

そしたら、こっちも実行毎にファイルが出来るので、これをマージする。

で、pythonでこれをマージするツール無いかなーって探してみたのですが、下記しかない。

https://pypi.org/project/xunitmerge/

ただ、このツール開発止まってるぽくて更新6年くらい止まってるんですよね。。。

ついでに、python3系で出来ない書き方してるようでそのままだと動かない。

下のプルリクの修正が必要みたい。

https://github.com/miki725/xunitmerge/pull/9

本体に取り込まれる事は無さそうな気がするので、自分でリポジトリ作ってそっち直して、そっちからPIPでインストールする。

GitHubとかGitLabとかリモートリポジトリからpipインストールする場合は↓の感じ。

※別に直で実行してもOK。

pip install  -b release git+https://github.com/***/***/xunitmerge

実行できるようにしたら、下記でマージする。

xunitmerge nosetests_*.xml nosetests.xml

「nosetests_」で始まるファイルを「nosetests.xml」にマージするみたいな。

あとはこれらをjenkinsなりに食わせればOKという寸法です。


最終的にDjangoのtest実行する時のコマンドはもろもろオプションとかつけた感じにすると下記の感じ。

COVERAGE_FILE=.coverage_datafile coverage run --source='.' manage.py test app/tests --settings=dapp.settings_hoge.py --keepdb --with-xunit --xunit-file nosetests_xxx.xml

Python import thisの裏

import thisは色んな人が解説書いてるので少し違った個人の視点をば。

import thisのソースってこんな感じ。

s = """Gur Mra bs Clguba, ol Gvz Crgref

Ornhgvshy vf orggre guna htyl.
Rkcyvpvg vf orggre guna vzcyvpvg.
Fvzcyr vf orggre guna pbzcyrk.
Pbzcyrk vf orggre guna pbzcyvpngrq.
Syng vf orggre guna arfgrq.
Fcnefr vf orggre guna qrafr.
Ernqnovyvgl pbhagf.
Fcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.
Nygubhtu cenpgvpnyvgl orngf chevgl.
Reebef fubhyq arire cnff fvyragyl.
Hayrff rkcyvpvgyl fvyraprq.
Va gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.
Gurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.
Nygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.
Abj vf orggre guna arire.
Nygubhtu arire vf bsgra orggre guna *evtug* abj.
Vs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.
Vs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.
Anzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!"""

d = {}
for c in (65, 97):
    for i in range(26):
        d[chr(i+c)] = chr((i+13) % 26 + c)

print("".join([d.get(c, c) for c in s]))

文字列表示してるだけなのですが、カエサル式(シーザー式)暗号っていうのやってます。

65がasciiコードでいう「A」で97が「a」。でそれぞれ26文字分繰り返して13文字分ずらした文字に直して出力。みたいな。


こういうの見ると「遊び心」があるー。って感じするじゃん?

何か洒落てる感じするじゃないですか。

「遊び心」が大事だよねって思うじゃないですか。

で、遊ぶってすごいなと思うわけです。

例えば将棋とかチェスとか、野球とかサッカーとかバスケみたいなスポーツでもいいし、大工さんだったりF1だったり、別にゲームでもいい、例えば地球防衛軍とかバイオハザードとか。

何でもそうだと思うのですが、本気で遊べるためには、その何かに精通してなきゃ無理だと思うんですよ。

それなりに精通してないと遊ぶ余裕が全くないし、動かすだけで精一杯。

遊び心が出せるって事はそれに精通してるって事。

そのくらい知識とか経験とかちゃんと身に着けなよって言われた気がしたのです。

まー気のせいだ。。。

Pythonのクラス変数を定数(再代入禁止の変数)として扱う - 名前空間を分けたい

Pythonには他の言語でいう定数がない。

慣習的に定数扱いにしたい場合は大文字で書くみたいなのはある。

FOO = 100

みたいな。。。

ちんまりしたアプリだったら別に上記のくらいのルールでもいいかもだけど、

そこそこ大きいアプリになって色んな開発者が長期に渡って触る可能性がある場合。

定数を定数として扱わなくなる危険性があるので、ちゃんと再代入は禁止できるようにしたい。

で、これをやるのによく見るのって下の感じ。

# 「const.py」ってファイルで作成

class _const:
    def __setattr__(self, name, value):
        if name in self.__dict__:
            # 定義されたら例外出す
            raise TypeError(f'Can\'t rebind const ({name})')
        self.__dict__[name] = value

import sys
sys.modules["const"]=_const()

# ↑を作ったら↓

import const

const.FOO = 'hoge'
const.FOO = 'fuga' # 例外でる

これで再代入を禁止した定数っぽいものが作れると。参考元


ただ、これって「const」クラスの「インスタンス変数」として突っ込んでるだけ。ってそれがダメなわけじゃないですが、大量に定義したい時なんかはちょっと・・・。

あと、試してないのですが、名前空間一つになっちゃうと思うので、色んなPGで使うと変数名かぶったりした時にやられたりしないのかしら?

という事で、定数をある程度まとまった役割(名前空間)で分けたい場合に例えば、、、

class Moge:
   FOO = 100

# ↑こういう風にまとめて、
# ↓こういう風に使いたい

do_something(Moge.FOO)

って感じにしたいのですが、前述のやり方だと

class Moge:
   const.FOO = 100

みたいになっちゃうので無理。

何かやり方ないかなと思ってたら、PythonのClass定義は、それ自体もインスタンスだったなと思い出しまして、こんな風にしてみたら意外と出来た。


# クラス定義そのものに対してのsetter制御用メタクラス
class ConstMeta(type):
    def __setattr__(self, name, value):
        if name in self.__dict__:
            raise TypeError(f'Can\'t rebind const ({name})')
        else:
            self.__setattr__(name, value)

# 定数定義
class MyConstClass(metaclass=ConstMeta):
    X = 10
    Y = 20

MyConstClass.X # 10
MyConstClass.X = 30 # 例外
MyConstClass.Z = 40 # これは別の例外

これで定数を役割なんかでまとめた分類が出来るという寸法です。

Metaクラスってクラス定義に対しての特殊メソッドを定義出来るので、

↑の感じでクラス変数(属性)へのsetterをねじ込んだ感じ。

ただ、この場合は本当に定数だけのクラスじゃないとダメだと思う。

Python matplotlibのインストールでグダる

Pythonでグラフ表示したくなったので、matplotlibをインストールしようとしたら

かなりグダったので残しておく。

matplotlibの公式

OSはCentOS7

pip install matplotlib

↑で基本は良いようですが、これだけだとCentOSでは動かない。

GUI系の処理をするのにtcl/tkを使ってるらしく、

それのPython用モジュールが標準だとPython3側にはないっぽ。

なのでPython3用のやつをインストールする。

sudo yum install tcl -y
sudo yum install tcl-devel -y
sudo yum install tk -y
sudo yum install tk-devel -y
sudo yum install tkinter -y
sudo yum install python-matplotlib-tk -y

なんだけど、これでもtkinterが入らない。

探してみると

> yum search tkinter

python3-tkinter.i686 : A GUI toolkit for Python
python3-tkinter.x86_64 : A GUI toolkit for Python
python34-tkinter.x86_64 : A GUI toolkit for Python 3
python35u-tkinter.x86_64 : A GUI toolkit for Python 3
python36-tkinter.x86_64 : A GUI toolkit for Python
python36u-tkinter.x86_64 : A GUI toolkit for Python

なので、

sudo yum install python36u-tkinter -y

でやってみると、下の感じでモジュールが競合してんぞって怒られる。

・・・
file /usr/lib64/python3.6/xml/sax/__pycache__/xmlreader.cpython-36.opt-2.pyc from install of python36-libs-3.6.6-5.el7.x86_64 conflicts with file from package python36u-libs-3.6.7-1.ius.centos7.x86_64
・・・

で、これを解消するには一回Python3をアンインストールする必要があるっぽい。

コワ。

なので

sudo yum remove python36u

sudo yum install python36u
sudo yum install python36u-devel

↑でアンインスト+インストしなおし。

そしたら、↓でOK。

sudo yum install python36u-tkinter -y

長かった。

あと、途中でAggはもう使えないよってエラーが出るので、

matplotlib.use('TkAgg')

を入れてみたり、↓の設定ファイルを書き換えたりする必要がある

vi ~/pyenv/lib/python3.6/site-packages/matplotlib/mpl-data/matplotlibrc

## 'module://my_backend'.
backend      : Tkagg

って事をみたのですが、インストールが上手くいった後だと、

特にやらなくてもちゃんと動いた。なんでしょね。

Python Djangoで動的にモデルを作成

Djangoのモデルはすごく便利なのですが、

固定の定義を作らないとダメなので、

何かの設定とかに応じてモデルを作りたい場合はちょっと使えない。

SQLを自前で書いてゴニョゴニョしてもいいんだけど、

ORMは出来ればDjangoの使いたいのでどうにかしたい。

ということで、モデルを動的に作りたくなった場合。

あと、動的に作ったモデルのテーブルも動的に作りたい場合についても。

参考:https://code.djangoproject.com/wiki/DynamicModels

※すごく古いのでちょっと微妙かも。。。

まず、動的にモデル作る本体は下の感じ。

from django.apps import apps
from django.db import models

def create_model_class(cls, name: str, fields=None, app_label: str = "", module: str = "", options=None):
    """指定されたモデルのClassを作成する"""
    class Meta(object):
        pass
    if app_label:
        setattr(Meta, "app_label", app_label)
    if options is not None:
        for key, value in options.items():
            setattr(Meta, key, value)
    attrs = {"__module__": module, "Meta": Meta}
    if fields:
        attrs.update(fields)
    model = type(name, (models.Model,), attrs)
    return model

普通にクラス定義するときにやってる事を、コードで書いてるだけ。肝は↓のとこ。

type(name, (models.Model,), attrs)

models.Modelを継承したクラス定義にする。

で、どう使うかというと↓の感じ。

app_label = "app"
model_name = "DynamicModelName"
ops = {"db_table": "table_name"}
fields = {}
fields["id"] = models.AutoField(primary_key=True, null=False)
fields["test01"] = models.IntegerField(null=False, default=0)
fields["test02"] = models.CharField(null=False, max_length=255, default="")
ret = create_model_class(
    name=model_name,
    app_label=app_label,
    module=".".join([app_label, model_name]),
    options=ops,
    fields=fields
)

obj = ret()
obj.test01 = 123
obj.test02 = "abc"
obj.save()

q = ret.objects.all()
q.values()

ただ、動的に作ったモデルはDBにはまだテーブル作られてないので、

save()とか、objects.all()とかはもちろん動ない。

テーブルをこの作ったモデル定義が作成する場合はschema-editorを使う。

こんな感じ。

from django.db import connections

connection = connections[con_label]
with connection.schema_editor(collect_sql=False, atomic=True) as se:
    se.create_model(model)  # ここでCreate Tableが発行される

SQLだけ拾いたいときは下の感じ

with connection.schema_editor(collect_sql=True, atomic=True) as se:
    se.create_model(model)
    "\n".join(se.collected_sql)

collect_sqlをTrueにすると、操作に対してのSQLがcollected_sqlのlistに

たまってくので、最後に「"\n".join(se.collected_sql)」で全部取るみたいな。

collect_sqlをTrueにした場合は、create_model()とかしても、SQLは発行されない。

あと、作ったモデルは↓の感じで使いまわせる。

from django.apps import apps

django_model_name = model_name.lower()
if django_model_name in apps.all_models[app_label].keys():
    ret = apps.all_models[app_label][django_model_name]

q = ret.objects.all()
q.values()

「apps.all_models」がDjangoがmodel定義を全部キャッシュしてある場所。

削除したい場合はここから消せばいい気がするんだけど、

ちゃんと調べてないので、要調査。今のとこ使い終わった場合は↓の感じで消す。

def unload(cls, app_label, model_name):
    django_model_name = model_name.lower()
    if app_label in apps.all_models:
        mdic = apps.all_models[app_label]
        if django_model_name in mdic.keys():
            mdic.pop(django_model_name)
            apps.clear_cache() # これないとapps.get_models()から消えてない

あと、外部キー制約がどうもうまくいかなかった。

他にも上手くいかないオプションはあると思うのですが、

単純なテーブルのORMやるだけなら十分。。。

と、ここまで書いてて、こんなパッケージがある事に気づいた。

後でコード見ておこ。。。




以上の内容はhttps://cream-worker.blog.jp/tag/Pythonより取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14