diadia

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

DateFieldをカレンダーwidgetを使って入力したい

DateFieldにカレンダーwidgetを使う理由

期待された形式で入力されないデータはエラーが出てしまう。
カレンダー形式のウィジェットを使えば、上記の問題を避ける事ができる。

参考資料

https://simpleisbetterthancomplex.com/tutorial/2019/01/03/how-to-use-date-picker-with-django.html
https://github.com/tempusdominus/bootstrap-4
https://github.com/FlipperPA/django-tempus-dominus
https://pypi.org/project/django-tempus-dominus/

結論

django-tempus-dominusは使いやすかった。これで実装するのが良い。

geodjangoでポリゴンデータを表示したい

前提

モデルにポリゴンデータを格納済みで、views.pyで如何にして表示させるかを焦点とする。

資料:
https://medium.com/@h4k1m0u/display-a-polygonized-raster-in-a-django-webapp-1-2-polygonize-a-raster-and-save-polygons-to-the-7779680a029e

https://github.com/makinacorpus/django-leaflet/issues/211

参考先

https://www.youtube.com/watch?v=ieJ0J-iFJuY

マップに描画するまでのイメージ:
templateを呼び出すためのviewを用意することに加え、polygonデータを呼び出すためのviewの2つを用意する。前者を使ってleafletのマップをテンプレートに表示させる。テンプレート内にjavascriptのコードを記述する。ロジックはpolygonデータの変数を作成する。jQueryのgetJSONを使い、読み込んだpolygonデータをmapに加える。

<!DOCTYPE html>
<html>
<head>
{% load leaflet_tags %}
{% leaflet_css %}
{% leaflet_js %}
<style type="text/css">
	.leaflet-container {height : 80%;}
</style>
<script
  src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
</head>
<body>
<h1>list</h1>


<div style="height :1200px;">
{% leaflet_map "main" callback="map_init_basic" %}
</div>

<script type="text/javascript">
	function map_init_basic(map, options){
	
		var polygons = "{% url 'prefecturas:data' %}";
		console.log(polygons);
		$.getJSON(polygons, function(data){
			//console.log(data);
			L.geoJson(data).addTo(map);

		})
}
</script>
</body>
</html>





postgresqlをmacosにbrewでインストールする場合について

Brewでインストールする場合

https://docs.djangoproject.com/ja/2.2/ref/contrib/gis/install/#homebrew
下記の方法でpostgresqlをインストールすることができる。

brew install postgresql
#$ brew install postgis
#$ brew install gdal
#$ brew install libgeoip

なお、インストールしたままpsqlコマンドを叩くと、

psql: could not connect to server: No such file or directory
	Is the server running locally and accepting
	connections on Unix domain socket "/tmp/.s.PGSQL.5432"?

となる。brewでインストールしたものは別途postgresqlサーバーを起動させなければならない。

pg_ctl -D /usr/local/var/postgres start

pg_ctlでサーバーを起動させる方法はデーモンモードで起動させる事ができるようだ。

postgres -D /usr/local/var/postgres

こちらの方法もあるようだ。https://qiita.com/mochizukikotaro/items/84204d5c46b67c9b74f4

brewを使ってインストールする場合には、インストールしたソフトウエアの環境変数をセットする必要があることがわかった。今までpythonを使った場合、pipでインストールしたソフトウエアのパスを書く必要がなかった?けど、全てがそのようなことではないらしい。brew installした場合にはbash_profileに記述すべき要素がインストールした時点で表示されるのでそれを利用すると楽に環境構築できる。

https://morphocode.com/how-to-install-postgis-on-mac-os-x/

改定版

ここからはpostgisをどのようにインストールするかについてのメモを書く。

参考:
https://docs.djangoproject.com/ja/2.2/ref/contrib/gis/install/#homebrew
https://medium.com/@Umesh_Kafle/postgresql-and-postgis-installation-in-mac-os-87fa98a6814d

まずbrewをインストールする。次にbrewを使ってpostgresql, postgis, gdal, libgeoipをインストールする。それができたらpostgresqlのサービスを開始する。そしてinitdbコマンドを実行する。しかしながらうまく実行できないのでまず、ディレクトリを削除してもう一度initdbを実行する。つぎにcreatedbコマンドを実行し、データベースを作成する。データベースを作成するcreatedbコマンドは、サービスがスタートされていないと実行できないので注意すること。

postgresqlをアンインストールする

最近macで開発をするようになってwindowsと勝手が違うことがわかってきた。

少しメモしておく。

 

windowsの場合インストールしたソフトウエアはコントロールパネルからアンインストーとできる認識でいる。一方、macはアプリをゴミ箱に入れればそれで終了と読んだことがあったがそれは間違いのようだ。

アンインストールには複数存在している。一つはApp Storeからダウンロードしたソフトのアンインストールは lounch padから行う。

http://c-through.blogto.jp/archives/34140070.html

App Store以外からダウンロードしたものは、また処理に分岐が存在する。

アプリケーション内にアンインストールする物がある場合と、postgresqlをwebからインストールした場合のような特定ディレクトリ内に存在しているアンインストールプログラムを起動する方法がある。

https://rksoftware.hatenablog.com/entry/2018/11/21/205223

https://shinodogg.com/?p=4327

postgresqlのデータベースがどのディレクトリに存在しているか確認する

ディレクトリを確認する方法

参考:https://dba.stackexchange.com/questions/1350/how-do-i-find-postgresqls-data-directory

#psqlコマンド等でデータベースに接続して、、、

SHOW data_directory;

電話番号に最適なDjangoのフィールドは何か

 

https://stackoverflow.com/questions/19130942/whats-the-best-way-to-store-phone-number-in-django-models

どうやらライブラリがあり、それを利用するのが良いようだ。使い方を随時まとめておく。

https://github.com/stefanfoulis/django-phonenumber-field

インストール方法

pip install django-phonenumber-field
pip install phonenumbers

エラー:form.is_valid()の結果が常にFalseになる場合

エラー内容

form.is_valid()エラーが常に出てしまう問題が出ている

コード

views.py
    
class ItemCreateView(View):
    def get(self, request, *args, **kwargs):
        context = {}
        form = ItemModelForm()
        context["form"] =form
        return render(request, "items/create_item.html", context)

    def post(self, request, *args, **kwargs):
		
        form = ItemModelForm(request.POST, request.FILES)
        print(form.is_valid())
        if form.is_valid():
            obj = form.save(commit=False)
            obj.user_name = request.user
            obj.save()
            print("成功した")

            return render(request, "items/create_item.html")
forms.py

class ItemModelForm(forms.ModelForm):
    class Meta:
        model  = Item
        fields = '__all__'



    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            #self.fields["user_name"].widget.attrs["class"] = "form-control"
            self.fields["title"].widget.attrs["class"] = "form-control"
            self.fields["description"].widget.attrs["class"] = "form-control"
            self.fields["image1"].widget.attrs["class"] = "form-control"
            self.fields["image1"].widget.attrs["type"] = "file"

    
{%  block content %}

<form method="post" enctype="multipart/form-data" action="{% url 'items:item_create'  %}">{% csrf_token %} 
	<!--label>{{ form.user_name.label }}</label>
	{{ form.user_name }}
	<br /-->
	<label>{{ form.title.label }}</label>
	{{ form.title }}
	<br />
	<label>{{ form.description.label }}</label>
	{{ form.description }}
	<br />
	<label>{{ form.image1.label }}</label>
	{{ form.image1 }}



	<br />




	<button type="submit" class="btn btn-primary">送信</button>
</form>

{% endblock %}    

原因

models.pyのItemにuser_nameがある。このuser_nameはviews.pyでItemオブジェクトに追加する方法をとっているので、html上でuserオブジェクトを入力するフォームを描画していなかった。しかしながらuser_nameはrequired Trueなのでuser_nameフォームが入力されない限り、is_valid()はFalseが出続ける。

解決方法

formにrequired Falseを加えたformインスタンスを生成するか、そもそもformにuser_nameを加えないように、fields="__all__"を使わないで記述する。

InputStreamオブジェクトを文字列に変換

InputStreamReaderオブジェクト

ドキュメント:https://developer.android.com/reference/kotlin/java/io/InputStreamReader

An InputStreamReader is a bridge from byte streams to character streams:

InputStreamReaderはバイトストリームから文字ストリームへの橋渡しの役割をするようだ。ちなみにストリームという表現はオブジェクトやデータ型に対して使われることが多いようだ。

参考:http://e-words.jp/w/%E3%82%B9%E3%83%88%E3%83%AA%E3%83%BC%E3%83%A0.html

具体的な使い方

ドキュメントのコンストラクタは以下のように使うようだ。
https://developer.android.com/reference/kotlin/java/io/InputStreamReader#init_1

val obj = InputStreamReader(stream_data, "UTF-8")

InputStreamReaderオブジェクトを読み込む

ドキュメントによるとオブジェクトをそのまま読み込むのではなく、BufferedReaderを使うことを勧めている。
https://developer.android.com/reference/kotlin/java/io/BufferedReader

kotlinでインターネットに接続する処理

http接続

処理

//接続先のurlを文字列で準備する    
val urlStr : String = "接続したいurl"

//urlの文字列を使ってURLオブジェクトを生成
val url = java.net.URL(urlStr)
//URlオブジェクトを使ってHttpURLConnectionオブジェクトを作成
val con = url.openConnection() as HttpURLConnection
//接続メソッドをGETに設定
con.requestMethod = "GET"
//接続
con.connect()

//HttpURLConnectionからレスポンスデータを取得
val stream = con.inputStream
//レスポンスデータ(inputStream)を文字列(JSON)に変換
val result = is2String(stream)
//HttpURLConnectionオブジェクトを解放
con.disconnect()
//InputStreamオブジェクトを解放
stream.close()

https://developer.android.com/reference/kotlin/java/net/URL#

インタネットに接続するために他に必要なこと

android アプリケーションを使うには、アプリケーションがネットに接続する許可が必要になる。これはmanifestファイルにその旨を記述しなければならない。

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>    
<manifest xmlns:android="http://schemas.android.com/apk/res ...>    
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

    <application 
        android:usesCleartextTraffic="true"
        ...>

    </application>    
</manifest>    

ListViewの要素をタップしてデータを取得する

ListViewの要素をタップをしたときにデータを取得するコードをどこに書くか

コードはオーバーライドしているonItemClickの中に記載する。

データを取得するコードの書き方

 

    
val item = parent.getItemAtPosition(position) as MutableMap<String, Any>    

position

getItemAtPosition()

https://developer.android.com/reference/android/widget/AdapterView#getItemAtPosition(int)

adapterの使い方について整理する

adapterの概念

Adapterは配列またはデータベースなどからそれぞれの要素をリストに入れられるように変換し自動的に挿入してくれます。

他に必要な引数は別において、共通的な考え方には以下がある。
adapterの引数にリスト様式のデータとレイアウトを引数としてadapterのインスタンスを生成する。このapapterをlistviewのプロパティであるadapterでつなげて作成する。

ArrayAdapter

sample

val lv_object = findViewById<ListView>(R.id.hoge)
val list_data = arrayOf("寿司","ラーメン", "カレー")
val adapter = ArrayAdapter(applicationContext, R.layout.activity_sample, list_data)

lv_object.adapter = adapter

https://developer.android.com/reference/kotlin/android/widget/ArrayAdapter

ArrayAdapterはListViewやSpinnerを使うときに使われるようだ。

SimpleAdapter

simpleadapterはmapのリスト様式データを使う。

 

adapterとレイアウトの関係について

レイアウトファイルはandroid studioビルトインのファイルと自作ファイルの2種類がある。そこで自分がどのレイアウトファイルを使うかを選ぶ必要がある。自分がどのレイアウトファイルを使用するかの宣言はadapterのコンストラクタで行う。具体的にはArrayAdapter, SimpleAdapterの引数の一つとしてR値でレイアウトファイルを記述する。また記述も多少違いがある。
自作したファイルを宣言する場合はR.id.ファイル名
ビルトインファイルを宣言する場合はandroid.R.id.ファイル名

このRの前にandroidをつけるか否かは、このように考えれば良い。Rはresourceつまりresを表す。res以下には、drawableやlayoutフォルダがある。だからR.drawable.hoge, R.layout.fooと書く。Rから書く場合は自分のファイルをR値として宣言する。アンドロイドビルトインのファイルを使う場合はそれを示すandroid.R...と続けて書く。

https://developer.android.com/guide/topics/resources/accessing-resources.html?hl=ja#%E6%A7%8B%E6%96%87

ボタンを押すとTextViewに"PUSH"と表示させる

リスナとイベントハンドラの使い方

リスナはイベントが起きるのをじっと待機するもので、イベントハンドラはイベントが起きたと伝達を受けたとき、何らかの処理を起こすもの(何らかの処理は自分がコードを書いて処理を規定する)。
今回はTextViewに"待っている"と表示させておいて、ボタンが押されたときTextViewの内容を"PUSH"に変更する仕組みをどのように構築するかメモする。
イベントハンドラとリスナはktファイルに書くものである。ktファイルを除いたものだけでまずボタン、テキストビューを表示させる。表示できたらイベントハンドラとリスナを書き、動きがあるようにしようと思う。

.ktファイルを除いてファイルを記述する

res/layout/activity_main.xml


<TextView
    andorid:id="@+id/tvMessage"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:text="@string/tv_message"/>
    
<Button
    android:id="@+id/btClick"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:text="@string/bt_click"/>
res/values/strings.xml

<string name="tv_message">待っている </string>
<string name="bt_click">button </string>
    

これでテキストビューとボタンを表示することが出来た。これにイベントハンドラとリスナを追加していく。

イベントハンドラとリスナの追加

イベントハンドラとリスナは独立した関係ではなく、ある関係をもっている。
リスナを承継したクラスをリスナとし、そのリスナのメソッドとして処理を記述する。つまり承継したリスナのメソッドがイベントハンドラという関係をもつ。 そしてリスナのインスタンスを画面部品(ビュー)に設置する。これはsetOnClickListenerで設置する。
リスナについてはViewを調べると良い。
https://developer.android.com/reference/kotlin/android/view/View

A View occupies a rectangular area on the screen and is responsible for drawing and event handling.

Viewは画面上の長方形の領域を占有し、描画とイベント処理を担当します。とあるように、Viewにリスナが入っている。だからリスナから継承してサブクラスを作り、イベントハンドラの動きを規定するにはViewからリスナを引っ張り出す。そのソースは以下にある。

Once you have created a tree of views, there are typically a few types of common operations you may wish to perform: ... Set up listeners: Views allow clients to set listeners that will be notified when something interesting happens to the view. For example, all views will let you set a listener to be notified when the view gains or loses focus. You can register such a listener using setOnFocusChangeListener(android.view.View.OnFocusChangeListener). Other view subclasses offer more specialized listeners. For example, a Button exposes a listener to notify clients when the button is clicked.

View.onClickListener()のサブクラスを生成し、onClick(view:View)をオーバーライドする。そしてこのサブクラスのインスタンスを生成する。ViewオブジェクトのsetOnClickListener()メソッドに先程のサブクラスのインスタンスを引数として渡す。

MainActivity.kt


class MainActivity : AppCompatActivity(){
    override fun onCreate(savedInstanceState: Bandle?){
        super,onCreate(SavedInstanceState)
        setContentView(R.layout.activity_main)
        
        val btn = findViewById<Button>(R.id.btClick)
        listener= ButtonListener()
        btn.setOnClickListener(listener)
        }
    
    
    
    private inner class ButtonListener: View.OnClickListener{
        override fun onClick(view: View){
            val tv = findViewById<TextView>(R.id.tvMessage)
            tv.text = "PUSH"
            }
        }
    }
    

        

参考URL:https://developer.android.com/reference/kotlin/android/view/View.OnClickListener.html
https://developer.android.com/reference/kotlin/android/view/View.html#setOnClickListener(android.view.View.OnClickListener)

strings.xmlの内容を表示する方法

TextViewに文字列を表示させる方法

TextViewに文字列を表示させる場合には、画面の表示を担当するxmlにTextViewを配置する。そしてTextviewに表示したい内容をstrings.xmlで決めてあげると表示することができる。.ktファイルは今回はイジる必要がない。.ktファイルはdjangoでいうviews.pyのようなものだと考えていたが、イベントに対するロジックを記述するものだと今は理解している。

res/layout/activity_main.xml

<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="matc_parent"
    android:layout_height="match_parent"
    android:text="@string/tv_number1"/>
res/values/strings.xml

<string name="number1">テストケース1</string>

上記のように記述するとactivity_main.xmlがテンプレートの役割を果たし、テンプレート上のTextViewに"テストケース1"が表示される。
この手法はTextViewに限らず、Buttonやその他画面部品(ビュー)でも使うこと事ができる。

リストを表示させることは出来ない

上記の方法ではリスト系のデータは表示することが出来ない。リスト型のデータとはListViewやSpinner(ドロップダウンリスト)を指す。リスト型のデータを表示させるにはstrings.xmlとactivity_main.xmlのような画面ファイルで多少変わったように書く。

res/layout/activity_main.xml

<ListView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="matc_parent"
    android:layout_height="match_parent"
    android:entries="@array/lv_number2"/>
res/values/strings.xml

<string-array name="number2">
    <item>ノート </item>
    <item>鉛筆 </item>
    <item>ボールペン </item>
    <item>メモ帳 </item>
</string-array>