diadia

興味があることをやってみる。自分のメモを残しておきます。

サイドバーを設置したい

https://getbootstrap.com/docs/4.3/examples/dashboard

構造を理解する

参考先の構造は、以下のようなものだった。

<div class="container-fluid">
<nav サイドバーの役割></>
<main メインの役割></>

</div>

ここで新たにわかったことはメインタグはセクショニングコンテントではないので、ドキュメントのアウトラインに影響を与えない。

sqlite:DBに格納されたテーブルデータを確認する方法

djangoでは開発時にsqlite3が使われる。このDBに直接接続し、データを入れてみようと思った。そのときにテーブル名が必要になる。どのような名前のテーブルか

参考文献:https://crimnut.hateblo.jp/entry/2018/04/17/172709

import sqlite3

con = sqlite3.connect("mydb.sqlite")
cursor = con.cursor()
cursor.execute("select * from sqlite_master where type='table'")
for x in cursor.fetchall():
    print(x)

con.close()

調べたいことはいろいろあるけど、とりあえずこのコードで 実行できた。

追記:SQLite3のテーブル確認だけしたいならDB Brouser(SQLite)を使えばコマンドを覚えておく必要はなくなるのでおすすめ。
補足情報:http://torajirousan.hatenadiary.jp/entry/2019/03/03/024103

ヘッダーとフッターのデザイン

ヘッダーとフッターのデザイン

わかったことがある。
スマホ等に画面を変更した場合、背景色があるヘッダーやフッターはフルで背景色が広がらない。

とても見栄えが悪い。ヘッダー、フッターは必ずフル画面に広がる設定でデザインすること。
bootstrap4では以下のクラスが役に立つ。

class="container-fluid"

sqlite3:エラー対処:sqlite3.OperationalError: near ")": syntax error

sqlite3.OperationalError: near ")": syntax error

このエラーがたまに出て、足止めをくらうのでメモにしておく。

コード

cur.execute("INSERT INTO mydb VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,)", (url,title,description,price,None,None,None,None,None,None,None,None,None,None))

 

エラーの意味が最初理解出来なかった。 これは閉じ括弧周辺に構文エラーがあることを示す。 今回は閉じ括弧の前に,をおいていることが原因だった。修正すると以下になる。

コード

cur.execute("INSERT INTO mydb VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)", (url,title,description,price,None,None,None,None,None,None,None,None,None,None))

sqlite3データベースの変更

sqlite3で既存のデータベースを修正したデータベースを作る必要が出てきた。

この作業は自分にとってヘビーだったので楽してできる方法を考る。今後同じケースにあたったときのために。

 

今回の方法

今回は別のファイルを作成した。拡張子がない”mydb”がファイル名だったが、これだとSQLAlchemyが読み込こまない問題が発覚した。

そこで"mydb.sqlite"と名付け、データをコピーすることになった。

工程

データベースファイルmydb.sqliteを作成

テーブルを作成

前データベースからデータ読み込み、リスト型データ化、新データベースに更新登録(カラムを分けて実施)

 

これが時間かかった。次回からはインサート一本で行う方法を用いる。

改善工程

データベースファイルmydb.sqliteを作成

前データベースからデータをcsv化する

テーブル作成

インサート

 

 

 

 

 

SQLAlchemy:エラー対処:sqlalchemy.exc.ArgumentError: Unknown arguments passed to Column: ['precision', 'asdecimal']

エラーコード 
sqlalchemy.exc.ArgumentError: Unknown arguments passed to Column: ['precision', 'asdecimal']

 

コード
class Product(Base):
    __tablename__ = 'mydb_jp'
 
    description     = Column(Text)
    price           = Column(Float, precision=(10,0), asdecimal=True)
    

どうやらFloatからエラーが発生している。sqlalchemyの場合Floatを使ってdecimalのデータ型に変更するようだ。 でdecimalのデータ型に変更する際に引数がいる。そういうわけで引数を書いたら知らない引数が渡されています、とエラーが出てしまった。解消しだい更新するとする。

SQLAlchemy:エラー対処:sqlalchemy.exc.OperationalError: (OperationalError) unable to open database file None None

エラーメッセージ:

sqlalchemy.exc.OperationalError: (OperationalError) unable to open database file None None

 

エラーが出た背景

sqlite3のデータベースを読み込みいろいろ変更しようとする際に起きた。

エラー部分

engine = create_engine('sqlite:///mydb')

原因解消

どうやらsqlite3のデータベースを開くことが出来ないことに起因するエラーだった。sqlite3のデータベースを拡張子なしで作ってしまったことが原因だった。新たにmydb.sqliteというファイル名でデータベースを作成し、以下のコードを書いた結果エラーは出なかった。

engine = create_engine('sqlite:///mydb.sqlite')

したがってsqliteでデータベースを作成する際は拡張子をちゃんと書くのが教訓となった。

参考:sqlite3のデータベースの作り方

import sqlite3
connection = sqlite3.connect(dbpath) 
# 例えばconnectの引数として"mydb"とした場合でmydbがなければ、新たにmydbというファイルが作成される。
''' 新たにdbファイルを作る場合、拡張子がなくても作ることができる。しかし他のモジュール利用のためにも.sqlite等の拡張子をつけるべき。'''

SQLAlchemy

参考文献:https://it-engineer-lab.com/archives/1183

sqlite3とdjangoのmodelsとSQLalchemyの共通点と差異についてメモできるとよい。

sqliteはそもそもすべてのデータをテキストのデータとして保存している。。。?

https://blog.ohgaki.net/sqlite-data-type-specification

 

データ型について

floatとdecimal

https://docs.sqlalchemy.org/en/latest/core/type_basics.html#sqlalchemy.types.Float

floatとdecimalはFloatを使って表現する。

Floatのasdecimal=Trueに設定した場合にDecimalとなる。

 

djangoの場合は

TextFieldがあるけどこれはどうなっているのか。

SQLAlchemyの場合でもTextがあるようだ。

 

djangoの場合はDecimalFieldで登録していたけど、

sqlite3は便宜的にIntegerにしてた。これは問題ないか?

sqlalchemyの場合は、Floatでasdecimal=Trueにすればdecimalが使える。

 

 

 

モデルの作成について

分かったこと。 データベースのテーブルデータを呼び出すためにモデルの作成がある。
例えばsqlite3のテーブルデータを呼び出すには、sqlite3のテーブルを自分で作成する必要がある。
その作り方はBaseモデルを継承して呼び出したいテーブルに似せたものを作る。この工程はdjangoのテーブルを作成する方法に似ている。

 

from sqlalchemy.ext.declarative import declarative_base

# まずベースモデルを生成します
Base = declarative_base()

# 次にベースモデルを継承してモデルクラスを定義します
class Product(Base):
    __tablename__ = 'mydb'
 
    title           = Column(String(255))
    description     = Column(Text)
    price           = Column(Integer)

 

わかったことは実際にテーブルに存在しないアトリビュートをモデルクラスとして定義すると、エラーが出てしまう。したがってテーブルのカラム名を正確に記述しなければいけない。

 

CRUDについて

SQL文の実行においてsqlite3でcur.execute()メソッドを使う。
SQLAlchemyの場合はsession.[sessionのメソッド名]()という形で行う。
例えば以下のようになる。

  • add() -- INSERT
  • query() -- SELECT
  • filter() -- WHERE

ドキュメント(チュートリアル

https://www.pythoncentral.io/migrate-sqlalchemy-databases-alembic/
sqlalchemyはAlembicを使って動かしているようだ。

psycopg2についてメモ

分からないことは、pysopg2とpsycopg2-binaryの違い。これはどうやって使い分けるのか。コンパイラや外部のライブラリ等を必要としないのがバイナリの方らしい。

そういう使い分け。

You can also obtain a stand-alone package, not requiring a compiler or external libraries, by installing the psycopg2-binary package from PyPI:

https://pypi.org/project/psycopg2/

コードを走らせる際に必要なライブラリがあるらしい。それがlibpqなるもののようだ。これをbinaryバージョンは別に準備しなくても実行できるってことのようだ。 

 

 

psycopg2ドキュメントから得たい情報

 http://initd.org/psycopg/docs/

 

自分が知る必要がある情報

一通りの流れのコマンド

csv読み込んでアップデートする方法

csv読み込んでインサートする方法

sqlite3,postresqlのsql文とどう違っているのか

 

一通りの流れのコマンド

http://initd.org/psycopg/docs/usage.html#basic-module-usage

>>> import psycopg2

# Connect to an existing database
>>> conn = psycopg2.connect("dbname=test user=postgres")

# Open a cursor to perform database operations
>>> cur = conn.cursor()

# Execute a command: this creates a new table
>>> cur.execute("CREATE TABLE test (id serial PRIMARY KEY, num integer, data varchar);")

# Pass data to fill a query placeholders and let Psycopg perform
# the correct conversion (no more SQL injections!)
>>> cur.execute("INSERT INTO test (num, data) VALUES (%s, %s)",
...      (100, "abc'def"))

# Query the database and obtain data as Python objects
>>> cur.execute("SELECT * FROM test;")
>>> cur.fetchone()
(1, 100, "abc'def")

# Make the changes to the database persistent
>>> conn.commit()

# Close communication with the database
>>> cur.close()
>>> conn.close()

postgresqlpythonのデータ型の対応

http://initd.org/psycopg/docs/usage.html#adaptation-of-python-values-to-sql-types

 

postgresqlに接続する

ドキュメントではconn = psycopg2.connect("dbname=test user=postgres")と書いてあるけど、パスワードを渡す場合は引数にパスワードを使えばよい。

http://initd.org/psycopg/docs/module.html
また別の方法として、
DATABASE_URL = postgresql://{username}:{password}@{hostname}:{port}/{database}
これを使う方法もある。

conn = psycopg2.connect(DATABASE_URL)

補足:hostnameはlocalhostとすればローカルのpostgresqlにつなげる。

データベースにデータをインサートする

sqlite3の使い方とほぼ同じ。curオブジェクトをつくり、execute()メソッドを実行する。両者の違いは?を使うか%sを使うかだけだ。

#参考:pythonのsqlite3の使い方
import sqlite3

conn = sqlite3.connect("mydb.sqlite3")
cur  = conn.cursor()
INSERT_SQL = """INSERT INTO test_teable (c1, c2, c3) VALUES (?,?,?)"""
t = ("テスト", "インサート文", "やり方",)
cur.execute(INSERT_SQL, t)
conn.commit()
#参考:pythonのpsycopg2の使い方
import psycopg2

conn = psycopg2.connect(DATABASE_URL)
cur  = conn.cursor()
INSERT_SQL = """INSERT INTO test_teable (c1, c2, c3) VALUES (%s,%s,%s)"""
t = ("テスト", "インサート文", "やり方",)
cur.execute(INSERT_SQL, t)
conn.commit()

参考:
https://www.lewuathe.com/python/postgresql/remind-for-insert-into-with-psycopg2.html
https://algorithm.joho.info/programming/python/sqlite3-insert-into/

django-allauthのログインリンクのはり方

webアプリケーションのnavバーにログインリンクをはろうと試みた。

メモしておく。

 

試みたこと

いつものようにapp_nameをアプリurls.pyで定め、app.urls.pyでnameを定める計画をする。しかしそもそもこれは自分が作ったアプリではない。てことで少し悩む事になった。

django-allauthのurls.pyに直接app_name="accounts"を書き込んでみた。

app_nameは読み込めるようになったが、nameは読み込めない。

 

nameはそもそもどこで確認するか。

それはもちろんallauth内のurls.pyであるが、自分の場合は以下のパスとなっていた。

/anaconda3/envs/django36/lib/python3.6/site-packages/allauth/account/urls.py

githubの方がコードの確認はしやすい。
https://github.com/pennersr/django-allauth/blob/master/allauth/account/urls.py

urlpatterns = [
    url(r"^signup/$", views.signup, name="account_signup"),
    url(r"^login/$", views.login, name="account_login"),
    url(r"^logout/$", views.logout, name="account_logout"),
    ....................
    

ログインのnameは"account_login"だと判明した。

結論

リンクが貼れた結果のみ書くと、app_nameやnamespaceを特段気にせず、むしろ無視してnameのみ書く

 <a href="{% url 'account_login' %}">ログイン</a>"

django-allauth : テンプレートのカスタマイズ

django-allauthを使ってみた結果ユーザのログインページが味気ない感じだった。

ここを修正していこうと思う。

 

https://qiita.com/s-katsumata/items/b667c81a127223d2e868

こちらにテンプレートのカスタマイズ方法があった。これを参考にテンプレートをカスタマイズしてみようと思う。

django-allauthのテンプレートのコピー

コマンドを使ってファイルを操作することに苦手なのでここもメモしておく。
まずコピーのコマンドは以下のような形式である。

 

#windowsの場合
copy コピー元ファイル コピー先のディレクトリ

#mac,linuxの場合
cp コピー元ファイル コピー先ディレクト

つぎにコピー元のファイルがどこに有るか?
django-allauthはpip でインストールした。pip でインストールした場合には、pip show モジュール名で情報参照できるようだ。

pipでインストールしたパッケージの場所を調べる

pip show django-allauth
Name: django-allauth
Version: 0.38.0
Summary: Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (social) account authentication.
Home-page: http://github.com/pennersr/django-allauth
Author: Raymond Penners
Author-email: raymond.penners@intenct.nl
License: UNKNOWN
Location: /anaconda3/envs/django36/lib/python3.6/site-packages
Requires: python3-openid, requests-oauthlib, requests, Django
Required-by: 

locationがdjango-allauthのモジュールだ。ここからaccount,openid,socialaccount,base.htmlをディレクトリごとにコピーしておく。コピー先はテンプレートの優先順位を上げたい思惑からBASE_DIR/config/templates/allauth以下にコピーする。するとallauth以下にaccount,openid,socialaccountディレクトリが置かれる状態となる。
テンプレートはドキュメントで以下のように言及されている。

allauth ships many templates, viewable in the allauth/templates directory.

https://django-allauth.readthedocs.io/en/latest/templates.html#overridable-templates 

#参考
#mac, linuxの場合
cp -r /Users/chiaki/opt/anaconda3/lib/python3.7/site-packages/allauth/templates コピー先ディレクトリ
#-rオプションはディレクトリごとコピーすることができる

settings.pyのテンプレートの読み込み先を追加する

settings.pyのDIRSの欄にallauth部分を追加する。
djangoのテンプレートの読み込みは優先順序がある。それはtemplateのDIRSをまず読み込み、該当するテンプレートがなかった場合に各アプリのtemplatesディレクトリ(つまりAPP_DIRS)以下を読み込みに行く。そして言うまでもないが、各アプリはsettings.pyのINSTALLED_APPSに登録されて使われることが前提である。allauthの場合も同様、INSTALLED_APPSにallauthを登録するして初めてつかわれる。このことはテンプレートにおいてはallauthアプリ以下にあるテンプレートを読み込んでいるに過ぎない。したがってDIRSにカスタマイズしたallauthテンプレートのパスを記せば、カスタマイズのテンプレートを純正のテンプレートを無視して自己カスタマイズしたテンプレートを読みこむことになる。
https://docs.djangoproject.com/ja/2.2/topics/templates/#support-for-template-engines

具体的なコード例
config/settings.py にて
    
TEMPLATES = [
    {
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [os.path.join(BASE_DIR, "config", "templates"),
            os.path.join(BASE_DIR, "config", 'templates', 'allauth')],  # ←ココ
    'APP_DIRS': True,
    'OPTIONS': {
        'context_processors': [
            'django.template.context_processors.debug',
            'django.template.context_processors.request',
            'django.contrib.auth.context_processors.auth',
            'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

#  上記の追加はBASE_DIR/config/templates/allauthのテンプレ読込先を追加したことを意味する!

上記の追加がなければ、BASE_DIR/config/templates以下のテンプレートをまず探して、該当テンプレートがなければAPP_DIRSを探す。そしてallauth純正のテンプレートを探し当てる。上記を追加すればBASE_DIR/config/templates以下のテンプレートをまず探す。なければ、BASE_DIR/config/templates/allauth以下にテンプレートがないか探す。ここにカスタマイズしたテンプレートを置けば純正allauthテンプレートを探す前に見つけてもらえるのでカスタマイズテンプレートが実際使われる。



テンプレートのカスタマイズ

各テンプレートはextends base.htmlとされているので、それに対しbootstrapのコードを加える。

<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

    <title>Hello, world!</title>
  </head>
  <body>
    <h1>Hello, world!</h1>

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
  </body>
</html>

forms.pyのカスタマイズ

forms.pyのフィールドにclassのアトリビュートを足してbootstrap効果をつけたいと考える。この場合はどうするか。。。?

https://django-allauth.readthedocs.io/en/latest/forms.html#forms

https://www.reddit.com/r/django/comments/3uykx8/customizing_djangoall_auth/

 

結論から言うと、settings.pyにカスタムしたフォームを使う宣言と、allauthのloginformを継承した子フォームさえ作成すれば良い。

 

settings.pyにカスタムしたフォームを使う宣言

ACCOUNT_FORMS = {'login': 'myapp.forms.CustomLoginForm'}
上記のように宣言すると、settings.pyにてallauth純正ではなく自分がカスタムしたフォームを使う挙動に変更される。したがってmyapp/forms.pyにてLoginFormを継承したクラスを作成すればよい。

https://django-allauth.readthedocs.io/en/latest/forms.html#account-forms

デフォルトでは以下のような設定になっているカスタムする場合は下記を参考にkeyをsettings.pyに書き込む。。

ACCOUNT_FORMS = {
    'login': 'allauth.account.forms.LoginForm',
    'signup': 'allauth.account.forms.SignupForm',
    'add_email': 'allauth.account.forms.AddEmailForm',
    'change_password': 'allauth.account.forms.ChangePasswordForm',
    'set_password': 'allauth.account.forms.SetPasswordForm',
    'reset_password': 'allauth.account.forms.ResetPasswordForm',
    'reset_password_from_key': 'allauth.account.forms.ResetPasswordKeyForm',
    'disconnect': `allauth.socialaccount.forms.DisconnectForm`,
}

allauthのloginformを継承した子フォームの作成

from allauth.account.forms import LoginForm


class CustomLoginForm(LoginForm):

	def __init__(self, *args, **kwargs):
		super().__init__(*args, **kwargs)
		for field in self.fields.values():
			field.widget.attrs['class'] = "form-control"

django mediaについて

mediaの配信について分かっていないので、少し

調べてみることにする。

 

  

class Hoge(modelsModel):
    image = models.ImageField(upload_to="hoge/static/images", null=True, blank=True)

このように書くと、サーバー上のpostgresqlのテーブルでは一体どのようなデータの状態で情報が保存されるのか?

 

 

    id | image 
-------+----------------------------------------------------------
    1  | hoge/static/images/my_image1.jpg
    2  | hoge/static/images/manzana_1.jpg
    

 

こんな感じになっている。
postgresqlにはupload_toのパスとjpgファイルの組み合わせが保存されていた。

ここで仮説を立てる。 サーバーのmediaを置くディレクトリに画像を置く。そしてpostgresqlにupload_toのパスと画像の名前を保存する。これで画像を表示することができる。


この仮説は正しいか?

この仮説は正しかった。mediaファイルディレクトリに画像を置くと画像表示できることは正しい。しかしながらmediaファイルのディレクトリの構成については理解が正しくなかった。以下のような理解となった。

mediaファイルの格納先

djangoの構造が少しわかった。デプロイ環境でmediaファイルはどこに格納されるのか?当初はnginxのmediaファイル用のディレクトリだと思っていた。しかしこれは厳密には正しいと言えないことが分かった。

 

デプロイ時にはmediaのために以下のコマンドを入力した。

mkdir /usr/share/nginx/html/media

Imagefieldは以下のように規定した。

image = models.ImageField(upload_to="hoge/static/images", null=True, blank=True)

そしてmediaのパスは以下の通り。

/usr/share/nginx/html/media/hoge/static/images/my_image1.jpg

 

要するにmediaのパスは3要素で構成される。1つめはnginxのディレクトリ(/usr/share/nginx/html/media)。この次にupload_toで定めたhoge/static/imagesにつながる。そして最後にファイル名になる。

staticについて理解を深める

まず開発中の場合、django.views.static.serve() ビューを用いてstaticファイル、mediaファイルを配信するようだ。

https://docs.djangoproject.com/ja/2.1/howto/static-files/#serving-static-files-during-development https://docs.djangoproject.com/ja/2.1/howto/static-files/#serving-files-uploaded-by-a-user-during-development

collectstaticのこと

python manage.py collectstatic

このコマンドにより、各staticフォルダからSTATIC_ROOTのディレクトリにファイルがコピーされるようだ。

 

django.contrib.staticfilesのこと

https://docs.djangoproject.com/ja/2.1/ref/contrib/staticfiles/#module-django.contrib.staticfiles

 

3つのコマンドがあるらしい

collectstatic

python manage.py collectstatic

findstatic

python manage.py findstatic css/base.css

runserver

django-admin runserver [addrport]