この記事では、Django 6.0 から、マイクロソフト Excel の シートの読み書きを実行します。
.xlsx ファイルを使用します。
django-msaccess の更新された v0.2.0 パッケージを使います。
settings.py の DATABASES の変更が必要です。
DATABASES = {
'default': {
'ENGINE': 'django-msaccess',
'ODBC': '{Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)}',
'PATH': 'C:\\WorkXLS\\DjangoTest.xlsx(Excelファイルのフルパス)',
'DEBUG': False,
'TRUNCATENAME': True,
},
}
このような設定にしてください。
インストール編の記事
モデル操作編その1
モデル操作編その2
上記の記事を必要に応じて、ご参照下さい。
1.はじめに
先日、django-msaccess パッケージを更新している作業中に、ふと考えが浮かびました。
『別に Access に限定しなくても、いいんじゃねえ?』
どうしてこのような考えが出たのかを説明すると、django-msaccess パッケージの機能は、Django を使ったモデル操作(データベース操作)の命令を、マイクロソフト Access の SQL文に変換することが役目になっています。十年以上前に開発されたものが、メンテナンスが止まっていたので、それを使ってパッチを当て直したものです。そしてAccess用に変換されたSQL文は、Python のpyodbc パッケージを使って ODBC 経由でAccessに届けられます。
その時、思いました。AccessのSQL文って、エクセルでも使えるんじゃね?
予想は的中!、ということで、『モデル操作編その2』の記事の内容を、エクセルを使ったらどうなるか?という記事にしました。
期待を持たせておきながら、致命的なお話を。CREATE TABLEのテーブル作成SQL文は、エクセルに投げても、テーブルは作成されませんのでご注意ください。また、エクセルに対してODBC経由によるSQL文の実行は様々な問題が発生します。その問題はDjangoを使用する場合も同様に発生します。
1.1 エクセルVBAプログラマを対象に、この記事を書きました
エクセルVBAプログラマの方々が、この記事を読まれることを想定し、一部、前の記事と重複する内容がありますが、ご了承ください。
2.Djangoなどの必要なソフトウェアのインストールについて
インストールの詳細は、冒頭の『インストール編の記事』を参照頂くことになりますが、初めてこの記事を読まれるエクセル技術者に向けて、簡単に説明します。
2.1 インストールされたエクセルのビット数は?
パソコンにインストールされたエクセルが、32ビット版のエクセルですか? それとも64ビット版のエクセルですか?このビット数を確認してください。
パソコンに、Pythonコマンドなどをインストールするときは、エクセルのビット数に合わせる必要があります。Pythonコマンドにも、32ビット版と64ビット版が存在します。
2.2 Microsoft Excel Driver は、インストール済みですか?
パソコンに、Microsoft Excel Driver は、インストール済みですか?
外部のソフトウェアが、このドライバを使用して、ODBC経由でエクセルに接続します。このドライバがすでにインストール済みになっている場合がありますので、その確認が必要です。
エクセルが32ビット版の場合は、32ビット用の ODBC データソースアドミニストレーターを起動します。具体的には、"C:\Windows\SysWOW64\odbcad32.exe" このファイルを実行します。
エクセルが64ビット版の場合は、64ビット用のODBC データソースアドミニストレーターを起動します。具体的には、"C:\Windows\System32\odbcad32.exe" このファイルを実行します。
エクセルのビット数と、ドライバのプラットフォームのビット数が一致していることを確認します。
インストールされていな時は、次のイントール作業を行います。
2.3 Microsoft Excel Driver の、インストールを行う
パソコンに、Microsoft Excel Driverが入っていない時は、そのインストールを行います。このドライバは、Microsoft 365 Access ランライム のインストールで、導入されます。
マイクロソフトの
Microsoft 365 Access Runtime をダウンロードしてインストールする
サポートページから、ダウンロードします。この時も、エクセルのビット数に合わせたものダウンロードしてください。
2.4 Pythonコマンドの、インストールを行う
冒頭で触れた、インストール編の記事を、ご参照ください。
Pythonコマンドも、エクセルのビット数に合わせます。
Pythonコマンドのインストール画面で、Python.exeのパスを登録するように、チェックボックスのチェック印を付けてください( 忘れずにチェックします )。 ここが重要!。
2.5 Pythonの仮想環境を作成する
作業ディレクトリを作り、その中に、Pythonの仮想環境を構築します。
C:\>mkdir C:\WorkXLS
C:\>cd /d C:\WorkXLS
C:\WorkXLS>py -m venv venv
C:\WorkXLS>venv\scripts\activate
(venv) C:\WorkXLS>
Pythonプログラム開発を、C:\WorkXLS で行なう場合の例です。ご自分の環境に合わせてディレクトリの場所を決めます。
2.6 仮想環境の中に、Djangoのインストールを行う
Django は、Python言語から利用できるパッケージです。Python仮想環境の中で、以下のコマンドでインストールできます。pip install django
(venv) C:\WorkXLS>pip install django
Collecting django
Using cached django-6.0.1-py3-none-any.whl.metadata (3.9 kB)
Collecting asgiref>=3.9.1 (from django)
Using cached asgiref-3.11.0-py3-none-any.whl.metadata (9.3 kB)
Collecting sqlparse>=0.5.0 (from django)
Using cached sqlparse-0.5.5-py3-none-any.whl.metadata (4.7 kB)
Collecting tzdata (from django)
Using cached tzdata-2025.3-py2.py3-none-any.whl.metadata (1.4 kB)
Using cached django-6.0.1-py3-none-any.whl (8.3 MB)
Using cached asgiref-3.11.0-py3-none-any.whl (24 kB)
Using cached sqlparse-0.5.5-py3-none-any.whl (46 kB)
Using cached tzdata-2025.3-py2.py3-none-any.whl (348 kB)
Installing collected packages: tzdata, sqlparse, asgiref, django
Successfully installed asgiref-3.11.0 django-6.0.1 sqlparse-0.5.5 tzdata-2025.3
2.7 仮想環境の中に、pyodbcのインストールを行う
pyodbc は、Python言語から、ODBC接続を実現するパッケージです。Python仮想環境の中で、以下のコマンドでインストールできます。pip install pyodbc。
(venv) C:\WorkXLS>pip install pyodbc
Collecting pyodbc
Using cached pyodbc-5.3.0-cp314-cp314-win32.whl.metadata (2.8 kB)
Using cached pyodbc-5.3.0-cp314-cp314-win32.whl (65 kB)
Installing collected packages: pyodbc
Successfully installed pyodbc-5.3.0
2.8 仮想環境の中に、django-msaccess のインストールを行う
django-msaccess は、Djangoのデータベース操作命令から、マイクロソフト Access の SQL言語を作成して、作成したSQL文を、pyodbc に投げて、その結果をDjangoに返します。
Python仮想環境の中で、以下のコマンドでインストールできます。pip install django-msaccess。
(venv) C:\WorkXLS>pip install django-msaccess
Collecting django-msaccess
Using cached django_msaccess-0.2.0-py3-none-any.whl.metadata (8.6 kB)
Requirement already satisfied: django>3.1.14 in c:\workxls\venv\lib\site-packages (from django-msaccess) (6.0.1)
Requirement already satisfied: asgiref>=3.9.1 in c:\workxls\venv\lib\site-packages (from django>3.1.14->django-msaccess) (3.11.0)
Requirement already satisfied: sqlparse>=0.5.0 in c:\workxls\venv\lib\site-packages (from django>3.1.14->django-msaccess) (0.5.5)
Requirement already satisfied: tzdata in c:\workxls\venv\lib\site-packages (from django>3.1.14->django-msaccess) (2025.3)
Using cached django_msaccess-0.2.0-py3-none-any.whl (53 kB)
Installing collected packages: django-msaccess
Successfully installed django-msaccess-0.2.0
以上で、必要なソフトウェア一式のインストールが終わりました。
3 エクセルを使ったDjangoの、学習用環境の作成
インストール作業が終わりましたので、Djangoから、エクセルファイルを使った学習用環境を以下の手順で作成します。
ここでは、
ODBC経由からのテーブル作成命令は、エクセルでは実行できない
という点に、注意します。そのため、前の連載記事で解説した手順とは異なる方法になるところがあります。
3.1 Djangoプロジェクトを新規に作成する
Python仮想環境を起動して、その中に、Djangoのプロジェクトを作成します。プロジェクト名は、DJLearnとします。
次のコマンドを入力して新規プロジェクトを作成します(.exeの入力は不要です)。
django-admin.exe startproject DJLearn
(venv) C:\WorkXLS>django-admin.exe startproject DJLearn
(venv) C:\WorkXLS>dir
2026/01/20 18:34 <DIR> .
2026/01/20 18:34 <DIR> ..
2026/01/20 18:34 <DIR> DJLearn
2026/01/20 17:54 <DIR> venv
0 個のファイル 0 バイト
(venv) C:\WorkXLS>dir DJLearn
2026/01/20 18:34 <DIR> .
2026/01/20 18:34 <DIR> ..
2026/01/20 18:34 <DIR> DJLearn
2026/01/20 18:34 685 manage.py
1 個のファイル 685 バイト
3.2 settings.py 設定ファイルの修正を行う
DJLearnディレクトリの中に作成された settings.py ファイル:
フルパス表記: "C:\WorkXLS\DJLearn\DJLearn\settings.py"
このsettings.pyの、以下の部分を修正します。このファイルが、Django の設定ファイルです。
3.2.1 INSTALLED_APPS 配列に、アプリ名を追加する
自動作成された settings.py の、INSTALLED_APPS配列の初期値は、以下のようになっています。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
上記の最後に、次の行を追加します。
'DJLearn.apps.XLSConfig'
これは、manage.pyファイルが置かれた場所を基準にして、その位置にあるDJLearnサブディレクトリの中にある、apps.pyファイルの中に宣言されたXLSConfig名前を参照するという意味になります。
次のような、INSTALLED_APPS 配列の初期化にします。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'DJLearn.apps.XLSConfig',
]
3.2.2 apps.py ファイルを登録する
settings.py ファイルが存在するディレクトリに、apps.py を作成します。UTF-8の文字コードでファイルを作成します。
(venv) C:\WorkXLS>dir
2026/01/20 18:34 <DIR> .
2026/01/20 18:34 <DIR> ..
2026/01/20 18:34 <DIR> DJLearn
2026/01/20 17:54 <DIR> venv
0 個のファイル 0 バイト
(venv) C:\WorkXLS>cd DJLearn\DJLearn
(venv) C:\WorkXLS\DJLearn\DJLearn>dir
2026/01/20 18:34 <DIR> .
2026/01/20 18:34 <DIR> ..
2026/01/20 18:34 407 asgi.py
2026/01/20 18:34 3,120 settings.py
2026/01/20 18:34 785 urls.py
2026/01/20 18:34 407 wsgi.py
2026/01/20 18:34 0 __init__.py
5 個のファイル 4,719 バイト
apps.py の内容は、次のようにします。
from django.apps import AppConfig
class XLSConfig(AppConfig):
name = 'DJLearn'
このファイルを作成し、保存します。
(venv) C:\WorkXLS\DJLearn\DJLearn>dir
2026/01/20 18:56 <DIR> .
2026/01/20 18:56 <DIR> ..
2026/01/20 18:59 86 apps.py ※このファイルを作成します※
2026/01/20 18:34 407 asgi.py
2026/01/20 18:57 3,153 settings.py
2026/01/20 18:34 785 urls.py
2026/01/20 18:34 407 wsgi.py
2026/01/20 18:34 0 __init__.py
6 個のファイル 4,838 バイト
3.2.3 DATABASES 定義を変更する
settings.py の中の、DATABASESは、以下のようになっています。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
この部分を、エクセルファイルが使用できるように、修正します。
以下のように変更します。
DATABASES = {
'default': {
'ENGINE': 'django-msaccess',
'ODBC': '{Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)}',
'PATH': 'C:\\WorkXLS\\DjangoTest.xlsx',
'DEBUG': False,
'TRUNCATENAME': True,
},
}
上記、PATHの部分に、フルパスでエクセルファイル名を書きます。このファイルに対して、Pythonプログラムからデータ操作を行います。
3.2.4 エクセルファイルを準備する
DATABASESの中で定義されたエクセルファイルを新規作成します。
エクセルの第1行目は列名を書きます。
半角英字で、id,first_name,last_name とします
列名だけ記入したエクセルファイルを、名前DjangoTest.xlsxで、C:\WorkXLSの中に保存します。
(venv) C:\WorkXLS>dir
2026/01/20 19:12 <DIR> .
2026/01/20 19:12 <DIR> ..
2026/01/20 19:12 8,257 DjangoTest.xlsx
2026/01/20 18:34 <DIR> DJLearn
2026/01/20 17:54 <DIR> venv
1 個のファイル 8,257 バイト
3.2.5 models.py の設置
apps.py を作成した場所と同じ所に、models.py ファイルを設置します。UTF-8の文字コードでファイルを作成します。
from django.db import models
# Create your models here.
class Person(models.Model):
class Meta:
db_table='Sheet1$'
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
上記の内容は、エクセルシート Sheet1 に定義されたテーブル構造を宣言しています。エクセル側で準備される 構造化参照 を、Pythonプログラムから使用できるようにします。
エクセルの方では、A1セルに、idと入力しましたが、上記のmodels.pyの方では省略されています。Djangoでは、テーブルの主キー列を省略した場合は、自動的に列名idとされる規則があります。この事例では、主キー列を省略しています。ただ、エクセルのシートの方では、省略できません。Djangoから見た場合は、エクセルが、データベースのテーブルとして扱われるからです。
first_nameが第2列目の項目で、最大30文字の文字列を記憶します。
last_nameが、第3列目を表しています。
3.2.5.1 class Person とは?
エクセルのSheet1で作成されたテーブルを、PythonのDjangoからレコード操作するときは、このclass宣言された名前Personで取り扱います。
mem=Person()
mem.id=1
mem.first_name='Audrey'
mem.last_name='Hepburn'
mem.save()
例えば、このようなPythonプログラムでは、
第1行目:mem=Person()
これは、Personクラスのインスタンスを作成します。Personは、エクセルシートSheet1のテーブルを表しています。そのインスタンスとは、テーブル本体ではなく、そのテーブルに保存する1個のレコードを表します。つまり、空のレコードを1個作成し、そのレコードをmemに代入しています。
第2行目:mem.id=1
これは、レコードの列名idの値を、数字の1にする命令です。一般的なDjangoプログラムでは、このようなid列に数字を代入するプログラムは書きません。このid列はテーブルの主キーになっており、自動インクリメント型(自動採番型)で宣言されます。またレコードの挿入では、データベース側で適切な値をこの自動インクリメント型に登録します。しかしエクセルでは、このid列を自動インクリメント型で取り扱うことはできないので、明示的にmem.id=1の命令を書きます。これはエクセル特有の表現になります。
第3行目:mem.first_name='Audrey'
これは、レコードの列名first_nameの値を、文字列'Audrey'にする命令です。
第4行目:mem.last_name='Hepburn'
これは、レコードの列名last_nameの値を、文字列'Hepburn'にする命令です。
第5行目:mem.save()
これは、mem オブジェクトの、save メソッドを実行する命令です。
レコードの保存命令になります。この命令によって、エクセルのSheet1に、新しいレコードが挿入されます。
このようなプログラムはDjango のモデル操作と呼び、ORM (Object-Relational Mapping) と呼ばれる技術が使用されます。
クラス宣言された名前がテーブル名を表し、その中のプロパティ宣言された名前がテーブルの列の名前を表します。その名前をそのままオブジェクト操作でも使用します。宣言された名前がそのまま使用できるという点が、このORMの特徴になります。
3.2.5.2 class Meta とは?
このMeta宣言は、テーブル全体に関する省略できない属性値を宣言する場合に使われます。Meta宣言が省略されたときは、Django側で適切なデフォルト値がこのテーブルに与えられます。このデフォルト値と異なる値を設定するときに、このMeta宣言を行います。
ここでは、次の宣言を行っています。
db_table='Sheet1$'
これは、データベース側で作成されたテーブル名を表しています。この場合は、エクセル側で作成されたテーブル名になります。
エクセルシートの名前 Sheet1 で作成されたテーブルを、エクセルの外部のアプリケーションからODBC経由で操作するときは、シートの名前の末尾に$文字を付ける決まりがあります。このため、DjangoからSheet1に作成されたテーブル名は、Sheet1$という名前になります。
このテーブルの名前は、ODBCで流れるSQL文の中で使われます。
SELECT id, first_name, last_name FROM [Sheet1$]
のように、SQL文のテーブル名に、このdb_tableで定義された名前が使われます。
ただDjangoのPythonプログラムでは、このテーブルはORM技術によって、models.pyファイルの中で宣言された Person という名前で操作します。
4.スタンドアロン形式の Django プログラムの作り方
詳しくは、前回記事の参照をお願いしますが、エクセルシートを操作する場合も、変更点はありません。次の4行を、Pythonプログラムの冒頭に入れてください。
import os
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'DJLearn.settings')
django.setup()
エクセルを対象にした、Djangoスタンドアロンプログラムの作成ができる理由は、たまたま偶然にも、django.setup のDjangoシステム初期化処理の中で、データベース側に作成されたDjangoシステムテーブルのレコード操作が行われない現象を利用しました。システムテーブルが参照されないので、システムテーブルの作成ができないエクセルでもこの初期化処理が終了しました。
将来のDjangoでもこの方法が使えるとは限りませんのでご注意ください。
3行目の'DJANGO_SETTINGS_MODULE'の初期値の設定方法については、前回記事の参照をお願いします。
5. スタンドアロン形式プログラムを使って エクセルシートを操作しましょう
エクセルシートSheet1の第1行目に、id, first_name, last_name の列名が記録されているシートを用意してください。このエクセルファイルは、settings.py の DATABASES の PATH で定義されたファイルです。
5.1 レコードの削除
レコードを2件登録した後に、レコードを全部削除するスクリプトを実行してみます。スクリプトは、別の場所(C:\Work2)に保存しています。
5.1.1 レコード登録
次のスクリプトを実行します。
#
# Initialize the Django.
#
import os
import sys
# C:\WorkXLS\DJLearn を検索パスの先頭に追加
sys.path.insert(0, "C:\\WorkXLS\\DJLearn")
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'DJLearn.settings')
django.setup()
#
# User programs start here.
#
from DJLearn.models import Person
mem=Person()
mem.id=1
mem.first_name='(Eldred) Gregory'
mem.last_name='Peck'
mem.save()
mem=Person()
mem.id=2
mem.first_name='Audrey'
mem.last_name='Hepburn'
mem.save()
print(Person.objects.all().values_list('id', 'first_name','last_name'))
上記プログラムを、実行します。
C:\>cd /d C:\WorkXLS
C:\WorkXLS>venv\scripts\activate
(venv) C:\WorkXLS>cd /d C:\WorkXLS2
(venv) C:\WorkXLS2>python 5-1-1-INSERT.py
<QuerySet [('1', '(Eldred) Gregory', 'Peck'), ('2', 'Audrey', 'Hepburn')]>
エクセルのシートには、以下のようにレコードが登録されます。
第1列目idは、Pythonからは数字で代入していますが、ODBC経由で受け取ったエクセル側では文字列として扱われますので、ワーニングが表示されます。最終的には手作業による編集作業が必要になります。
5.1.2 レコードの全部削除
次のスクリプトを実行してみます。
#
# Initialize the Django.
#
import os
import sys
# C:\WorkXLS\DJLearn を検索パスの先頭に追加
sys.path.insert(0, "C:\\WorkXLS\\DJLearn")
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'DJLearn.settings')
django.setup()
#
# User programs start here.
#
from DJLearn.models import Person
deleted_count, details = Person.objects.all().delete()
print(f'deleted_count={deleted_count}')
print(f"details={details}")
このプログラムを実行すると、
(venv) C:\WorkXLS2>python 5-1-2-DELETE.py
*** [ODBC ERROR(2)] ***
[CursorWrapper.execute(base.py)]
sql=DELETE FROM [Sheet1$]
params=()
('HY000', '[HY000] [Microsoft][ODBC Excel Driver] この ISAM では、リンク テーブル内のデータを削除することはできません。 (-5410) (SQLExecDirectW)')
deleted_count=-1
details={'DJLearn.Person': -1}
Person.objects の objects は、 前々回記事 で解説した モデルマネージャ と呼びます。Personテーブルに対するレコードの検索やレコード操作などは、このモデルマネージャを経由して行います。
Person.objects.all() の all は、全てのレコードを表します。つまりPersonテーブルのすべてのレコードを表現します。ただ、意味合い的には「すべてのレコード」を表していますが、この命令を読み込んだ瞬間に実際のすべてのレコードが読み込まれることはありません。
Person.objects.all().delete() の delete は、モデルマネージャが管理しているレコードを削除する命令です。この例では、Personテーブルの全てのレコードを削除する意味になります。この時点で、SQL文DELETE FROM [Sheet1$]が作成されて、ODBC経由でエクセルにその実行を依頼します。しかし残念ながらその実行は失敗します。これはエクセルのODBCドライバの制限です。
エクセルのODBCドライバでは、エクセル側のレコード削除処理は実行できません。このため一般的には、レコードが生きているか?削除されたのか?を表すフラグ列を設けて、レコード削除処理の代わりにこのフラグの値を更新する処理を行います。
5.2 レコードの登録
前々回の「8.レコードを登録してみよう」で、キーボードから入力した内容を、エクセルに対して実行します。なおプログラムでは、id列に主キーに相当する値を設定していますが、これはエクセルでは自動インクリメント型(自動採番型)が対応しないためです。
次のプログラムを実行します。
#
# Initialize the Django.
#
import os
import sys
# C:\WorkXLS\DJLearn を検索パスの先頭に追加
sys.path.insert(0, "C:\\WorkXLS\\DJLearn")
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'DJLearn.settings')
django.setup()
#
# User programs start here.
#
from DJLearn.models import Person
member=Person()
member.id=3
member.first_name='家康'
member.last_name='徳川'
member.save()
del member
member=Person()
member.id=4
member.first_name='秀忠'
member.last_name='徳川'
member.save()
del member
member=Person()
member.id=5
member.first_name='家光'
member.last_name='徳川'
member.save()
del member
Person(id=6, first_name='家綱', last_name='徳川').save()
Person(id=7, first_name='綱吉', last_name='徳川').save()
Person(id=8, first_name='家宣', last_name='徳川').save()
print(f'Person={Person.objects.all().values_list('id', 'first_name','last_name')}')
上記のプログラムを実行すると、
(venv) C:\WorkXLS2>python 5-2-INSERT.py
Person=<QuerySet [('1', '(Eldred) Gregory', 'Peck'), ('2', 'Audrey', 'Hepburn'), ('3', '家康', '徳川'), ('4', '秀忠', '徳川'), ('5', '家光', '徳川'), ('6', '家綱', '徳川'), ('7', '綱吉', '徳川'), ('8', '家 宣', '徳川')]>
エクセルの画面は、
となります。
エクセル側のレコードを手作業で全部削除を行ってから、
『もう少し、かっちょいい! プログラム』
を実行してみます。
#
# Initialize the Django.
#
import os
import sys
# C:\WorkXLS\DJLearn を検索パスの先頭に追加
sys.path.insert(0, "C:\\WorkXLS\\DJLearn")
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'DJLearn.settings')
django.setup()
#
# User programs start here.
#
from DJLearn.models import Person
TokugawaFamily = ['家康','秀忠','家光','家綱','綱吉',
'家宣','家継','吉宗','家重','家治',
'家斉','家慶','家定','家茂','慶喜']
for index, name in enumerate(TokugawaFamily):
member = Person()
member.id = index+1
member.first_name = name
member.last_name = '徳川'
member.save()
print(f'Person={Person.objects.all().values_list('id', 'first_name')}')
上記のプログラムを実行すると、
(venv) C:\WorkXLS2>python 5-2-2-INSERT.py
Person=<QuerySet [('1', '家康'), ('2', '秀忠'), ('3', '家光'), ('4', '家綱'), ('5', '綱吉'), ('6', '家 宣'), ('7', '家継'), ('8', '吉宗'), ('9', '家重'), ('10', '家治'), ('11', '家斉'), ('12', '家慶'), ('13', '家定'), ('14', '家茂'), ('15', '慶喜')]>
となります。
エクセルの画面は、
となります。
5.3 レコードの検索
レコードの検索は、filterメソッドを使います。
#
# Initialize the Django.
#
import os
import sys
# C:\WorkXLS\DJLearn を検索パスの先頭に追加
sys.path.insert(0, "C:\\WorkXLS\\DJLearn")
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'DJLearn.settings')
django.setup()
#
# User programs start here.
#
from DJLearn.models import Person
print(Person.objects.filter(first_name='家康'))
member=Person.objects.filter(first_name='家康')
print(member)
print(member.values_list())
上記のプログラムを実行すると、
(venv) C:\WorkXLS2>python 5-3-1-SEARCH.py
<QuerySet [<Person: Person object (1)>]>
<QuerySet [<Person: Person object (1)>]>
<QuerySet [('1', '家康', '徳川')]>
となります。
5.4 レコードの検索(AND条件)
filterメソッド内の複数の条件は、基本的には、AND条件検索になります。
次のプログラムを実行します。
#
# Initialize the Django.
#
import os
import sys
# C:\WorkXLS\DJLearn を検索パスの先頭に追加
sys.path.insert(0, "C:\\WorkXLS\\DJLearn")
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'DJLearn.settings')
django.setup()
#
# User programs start here.
#
from DJLearn.models import Person
print(Person.objects.filter(first_name='家康', last_name='徳川'))
print(Person.objects.filter(first_name='家康', last_name='徳川').values_list())
member=Person.objects.filter(first_name='家康', last_name='山川')
print(member.values_list())
上記のプログラムを実行すると、
(venv) C:\WorkXLS2>python 5-4-AND.py
<QuerySet [<Person: Person object (1)>]>
<QuerySet [('1', '家康', '徳川')]>
<QuerySet []>
となります。
5.5 レコードの検索(OR条件)
検索条件の中にOR条件を含めるときは、from django.db.models import Q の宣言が必要です。
次のプログラムを実行します。
#
# Initialize the Django.
#
import os
import sys
# C:\WorkXLS\DJLearn を検索パスの先頭に追加
sys.path.insert(0, "C:\\WorkXLS\\DJLearn")
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'DJLearn.settings')
django.setup()
#
# User programs start here.
#
from DJLearn.models import Person
from django.db.models import Q
members=Person.objects.filter( Q(first_name='家康') | Q(first_name='家宣') )
print(members.values_list())
上記のプログラムを実行すると、
(venv) C:\WorkXLS2>python 5-5-OR.py
<QuerySet [('1', '家康', '徳川'), ('6', '家宣', '徳川')]>
となります。
5.6 レコードの検索(残りの条件)
次のプログラムを実行します。
#
# Initialize the Django.
#
import os
import sys
# C:\WorkXLS\DJLearn を検索パスの先頭に追加
sys.path.insert(0, "C:\\WorkXLS\\DJLearn")
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'DJLearn.settings')
django.setup()
#
# User programs start here.
#
from DJLearn.models import Person
from django.db.models import Q
members=Person.objects.filter(id__gt=3)
print(members.values_list())
members=Person.objects.filter(pk__gt=3)
print(members.values_list())
members=Person.objects.filter(id__gte=4)
print(members.values_list())
bar='-'*40
print(bar)
print('** lt, lte **')
members=Person.objects.filter(id__lt=3)
print(members.values_list())
members=Person.objects.filter(id__lte=4)
print(members.values_list())
print(bar)
print('** in **')
members=Person.objects.filter(id__in=[ 1, 3, 5 ])
print(members.values_list())
members=Person.objects.filter(first_name__in=[ '秀忠', '家綱', '家宣' ])
print(members.values_list())
print(bar)
print('** contains **')
members=Person.objects.filter(first_name__contains='家')
print(members.values_list())
members=Person.objects.filter( Q(first_name__contains='家') | Q(first_name__contains='吉') )
print(members.values_list())
print(bar)
print('** order_by **')
print(Person.objects.filter(first_name__in=[ '秀忠', '家綱', '家宣' ]).order_by('first_name').values_list())
print(Person.objects.filter(first_name__in=[ '秀忠', '家綱', '家宣' ]).order_by('-first_name').values_list())
members=Person.objects.filter(first_name__contains='家').order_by('-id')
print(members.values_list())
上記のプログラムを実行すると、
(venv) C:\WorkXLS2>python 5-6-SEARCH.py
<QuerySet [('4', '家綱', '徳川'), ('5', '綱吉', '徳川'), ('6', '家宣', '徳川'), ('7', '家継', '徳川'), ('8', '吉宗', '徳川'), ('9', '家重', '徳川')]>
<QuerySet [('4', '家綱', '徳川'), ('5', '綱吉', '徳川'), ('6', '家宣', '徳川'), ('7', '家継', '徳川'), ('8', '吉宗', '徳川'), ('9', '家重', '徳川')]>
<QuerySet [('4', '家綱', '徳川'), ('5', '綱吉', '徳川'), ('6', '家宣', '徳川'), ('7', '家継', '徳川'), ('8', '吉宗', '徳川'), ('9', '家重', '徳川')]>
----------------------------------------
** lt, lte **
<QuerySet [('1', '家康', '徳川'), ('2', '秀忠', '徳川'), ('10', '家治', '徳川'), ('11', '家斉', '徳川'), ('12', '家慶', '徳川'), ('13', '家定', '徳川'), ('14', '家茂', '徳川'), ('15', '慶喜', '徳川')]>
<QuerySet [('1', '家康', '徳川'), ('2', '秀忠', '徳川'), ('3', '家光', '徳川'), ('4', '家綱', '徳川'), ('10', '家治', '徳川'), ('11', '家斉', '徳川'), ('12', '家慶', '徳川'), ('13', '家定', '徳川'), ('14', '家茂', '徳川'), ('15', '慶喜', '徳川')]>
----------------------------------------
** in **
<QuerySet [('1', '家康', '徳川'), ('3', '家光', '徳川'), ('5', '綱吉', '徳川')]>
<QuerySet [('2', '秀忠', '徳川'), ('4', '家綱', '徳川'), ('6', '家宣', '徳川')]>
----------------------------------------
** contains **
<QuerySet [('1', '家康', '徳川'), ('3', '家光', '徳川'), ('4', '家綱', '徳川'), ('6', '家宣', '徳川'), ('7', '家継', '徳川'), ('9', '家重', '徳川'), ('10', '家治', '徳川'), ('11', '家斉', '徳川'), ('12', ' 家慶', '徳川'), ('13', '家定', '徳川'), ('14', '家茂', '徳川')]>
<QuerySet [('1', '家康', '徳川'), ('3', '家光', '徳川'), ('4', '家綱', '徳川'), ('5', '綱吉', '徳川'), ('6', '家宣', '徳川'), ('7', '家継', '徳川'), ('8', '吉宗', '徳川'), ('9', '家重', '徳川'), ('10', '家 治', '徳川'), ('11', '家斉', '徳川'), ('12', '家慶', '徳川'), ('13', '家定', '徳川'), ('14', '家茂', ' 徳川')]>
----------------------------------------
** order_by **
<QuerySet [('6', '家宣', '徳川'), ('4', '家綱', '徳川'), ('2', '秀忠', '徳川')]>
<QuerySet [('2', '秀忠', '徳川'), ('4', '家綱', '徳川'), ('6', '家宣', '徳川')]>
<QuerySet [('9', '家重', '徳川'), ('7', '家継', '徳川'), ('6', '家宣', '徳川'), ('4', '家綱', '徳川'), ('3', '家光', '徳川'), ('14', '家茂', '徳川'), ('13', '家定', '徳川'), ('12', '家慶', '徳川'), ('11', '家斉', '徳川'), ('10', '家治', '徳川'), ('1', '家康', '徳川')]>
となりました。
一番最初の検索で、
members=Person.objects.filter(id__gt=3)
print(members.values_list())
この命令は、id列の値が、3より大きいレコードの検索になります。徳川家の第4代から第15代までの出力になるはずです。ところが実行結果は、第4代から第9代になっています。また出力結果のid列を見ると、整数型ではなく、数字を記憶した文字列になっています。
つまり、Djangoのプログラムでは、id列は数値として扱っているが、ODBC経由でエクセルに渡ると、それが文字列として扱われる問題が発生します。エクセル側では、id列は文字列として扱われています。
エクセル側では、id列にワーニングが出ています。
このワーニング表示箇所を、数値に変換します。
数値に変換した後、同じプログラムを実行してみます。
(venv) C:\WorkXLS2>python 5-6-SEARCH.py
<QuerySet [(4.0, '家綱', '徳川'), (5.0, '綱吉', '徳川'), (6.0, '家宣', '徳川'), (7.0, '家継', '徳川'), (8.0, '吉宗', '徳川'), (9.0, '家重', '徳川'), (10.0, '家治', '徳川'), (11.0, '家斉', '徳川'), (12.0, ' 家慶', '徳川'), (13.0, '家定', '徳川'), (14.0, '家茂', '徳川'), (15.0, '慶喜', '徳川')]>
<QuerySet [(4.0, '家綱', '徳川'), (5.0, '綱吉', '徳川'), (6.0, '家宣', '徳川'), (7.0, '家継', '徳川'), (8.0, '吉宗', '徳川'), (9.0, '家重', '徳川'), (10.0, '家治', '徳川'), (11.0, '家斉', '徳川'), (12.0, ' 家慶', '徳川'), (13.0, '家定', '徳川'), (14.0, '家茂', '徳川'), (15.0, '慶喜', '徳川')]>
<QuerySet [(4.0, '家綱', '徳川'), (5.0, '綱吉', '徳川'), (6.0, '家宣', '徳川'), (7.0, '家継', '徳川'), (8.0, '吉宗', '徳川'), (9.0, '家重', '徳川'), (10.0, '家治', '徳川'), (11.0, '家斉', '徳川'), (12.0, ' 家慶', '徳川'), (13.0, '家定', '徳川'), (14.0, '家茂', '徳川'), (15.0, '慶喜', '徳川')]>
----------------------------------------
** lt, lte **
<QuerySet [(1.0, '家康', '徳川'), (2.0, '秀忠', '徳川')]>
<QuerySet [(1.0, '家康', '徳川'), (2.0, '秀忠', '徳川'), (3.0, '家光', '徳川'), (4.0, '家綱', '徳川')]>
----------------------------------------
** in **
<QuerySet [(1.0, '家康', '徳川'), (3.0, '家光', '徳川'), (5.0, '綱吉', '徳川')]>
<QuerySet [(2.0, '秀忠', '徳川'), (4.0, '家綱', '徳川'), (6.0, '家宣', '徳川')]>
----------------------------------------
** contains **
<QuerySet [(1.0, '家康', '徳川'), (3.0, '家光', '徳川'), (4.0, '家綱', '徳川'), (6.0, '家宣', '徳川'), (7.0, '家継', '徳川'), (9.0, '家重', '徳川'), (10.0, '家治', '徳川'), (11.0, '家斉', '徳川'), (12.0, ' 家慶', '徳川'), (13.0, '家定', '徳川'), (14.0, '家茂', '徳川')]>
<QuerySet [(1.0, '家康', '徳川'), (3.0, '家光', '徳川'), (4.0, '家綱', '徳川'), (5.0, '綱吉', '徳川'), (6.0, '家宣', '徳川'), (7.0, '家継', '徳川'), (8.0, '吉宗', '徳川'), (9.0, '家重', '徳川'), (10.0, '家 治', '徳川'), (11.0, '家斉', '徳川'), (12.0, '家慶', '徳川'), (13.0, '家定', '徳川'), (14.0, '家茂', ' 徳川')]>
----------------------------------------
** order_by **
<QuerySet [(6.0, '家宣', '徳川'), (4.0, '家綱', '徳川'), (2.0, '秀忠', '徳川')]>
<QuerySet [(2.0, '秀忠', '徳川'), (4.0, '家綱', '徳川'), (6.0, '家宣', '徳川')]>
<QuerySet [(14.0, '家茂', '徳川'), (13.0, '家定', '徳川'), (12.0, '家慶', '徳川'), (11.0, '家斉', '徳川'), (10.0, '家治', '徳川'), (9.0, '家重', '徳川'), (7.0, '家継', '徳川'), (6.0, '家宣', '徳川'), (4.0, '家綱', '徳川'), (3.0, '家光', '徳川'), (1.0, '家康', '徳川')]>
となりました。
id列が、文字列型から小数点の付いた実数型に変わっていることがわかります。
5.7 レコードの更新
次のプログラムを実行します。
#
# Initialize the Django.
#
import os
import sys
# C:\WorkXLS\DJLearn を検索パスの先頭に追加
sys.path.insert(0, "C:\\WorkXLS\\DJLearn")
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'DJLearn.settings')
django.setup()
#
# User programs start here.
#
from DJLearn.models import Person
from django.db.models import Q
member=Person.objects.get(first_name='家光', last_name='徳川')
member.last_name='徳川将軍家です'
member.save()
bar='-'*40
print("last_name__contains='将軍'")
member=Person.objects.filter(last_name__contains='将軍')
print(member.values_list())
print(bar)
print("first_name__contains='家'")
members=Person.objects.filter(first_name__contains='家')
for mem in members:
print(mem.first_name)
members=Person.objects.filter(first_name__contains='家')
for mem in members:
mem.first_name = mem.first_name + 'さん'
mem.save()
print(bar)
print("first_name__contains='さ'")
members=Person.objects.filter(first_name__contains='さ')
print(members.values_list())
print(members.values_list('first_name'))
上記のプログラムを実行すると、
(venv) C:\WorkXLS2>python 5-7-UPDATE.py
last_name__contains='将軍'
<QuerySet [(3.0, '家光', '徳川将軍家です')]>
----------------------------------------
first_name__contains='家'
家康
家光
家綱
家宣
家継
家重
家治
家斉
家慶
家定
家茂
----------------------------------------
first_name__contains='さ'
<QuerySet [(1.0, '家康さん', '徳川'), (3.0, '家光さん', '徳川将軍家です'), (4.0, '家綱さん', '徳川'), (6.0, '家宣さん', '徳川'), (7.0, '家継さん', '徳川'), (9.0, '家重さん', '徳川'), (10.0, '家治さん', ' 徳川'), (11.0, '家斉さん', '徳川'), (12.0, '家慶さん', '徳川'), (13.0, '家定さん', '徳川'), (14.0, '家茂さん', '徳川')]>
<QuerySet [('家康さん',), ('家光さん',), ('家綱さん',), ('家宣さん',), ('家継さん',), ('家重さん',), ('家治さん',), ('家斉さん',), ('家慶さん',), ('家定さん',), ('家茂さん',)]>
となります。
エクセルの表示は、
となります。
6.まとめ
今回は、マイクロソフト Excel のシートを、スタンドアロン Django スクリプトを使って、Python プログラムから操作することを学びました。ODBCのエクセルドライバに、仕様上の問題点があり、その問題を受けるプログラムの開発や実行はできないことがわかります。
しかし、手作業でエクセル側を修正してあげれば、問題を回避できる場合もあることもわかりました。
Python には多くのパッケージが提供されています。CSV や JSON など構造化されたデータの取り込みなどは、これらのパッケージを利用して行い、その結果をエクセルに書き出す部分に、Django スタンドアロンプログラムが役立つと思います。ORM機能を使ったテーブルのレコード操作によって、比較的簡単にプログラム作成ができると思います。
Python in Excel などの機能によって、Excel-VBA言語だけではなく、Python言語に取り組まれているExcel技術者も増えていることでしょう。その中の1つの選択肢として、このDjangoスタンドアロンプログラムの利用法が含まれることを願います。
エクセルのODBCドライバの欠点を認識しつつ、利用できそうな場面に、活用してください。






