Sublime text 3にKotlinのシンタックスハイライトを付ける
参考
https://www.kotlintips.com/kotlin-support-in-sublime-text/
やり方
Toolsの Install Package Controlを選択する。 *最初の状態のSublime Text 3ではInstall Packageコマンドが使えない。これを使えるようにするために行う。
Control + Shift + Pを押して表示されるフォームにInstall Packageを入力する。
Package Control: Install Packageが出てくるで、それを選択する
Kotlinを入力して出てきたKotlinを選択する。
これでオッケイ!
日付データをバリデーションする方法
入力された日付データをバリデーションする方法を記述する。
バリデーションする内容:現実に存在しない日付データにはfalseを返す。
fun checkInputDate(inputDate: String) :Boolean{ try{ val format = SimpleDateFormat("yyyy/MM/dd") // SimpleDateFormat.isLenientをfalseにすると現実に存在する日付かをバリデーションする仕様に変化する format.isLenient = false format.parse(inputDate) }catch (e: ParseException){ return false } return true }
DatePickerの実装について
ダイアログ | Android デベロッパー | Android Developers
Dialogクラスのサブクラスとして、 AlertDialog, DatePickerDialog, TimePickerDialogが存在する。
これらのサブクラスを使うためにコンテナとしてDialogFragmentを実装する。
フラグメントでオプションメニューを実装する
参考
FragmentでActionBarを指定したい! - Qiita
実装の概要と注意点
編集するファイルは、フラグメントが紐付けられるアクティビティ.ktファイルとフラグメント.ktファイルである。 両ファイルともにonCreateOptionsMenuをオーバーライドする。またフラグメント.ktではsetHasOptionsMenu(true)を記述する必要がある。
ちなみにアクティビティ.ktにオーバーライドしたonCreateOptionsMenuを実装しないとエラーが生じる。
その記事は以下である。
エラー対処:java.lang.IllegalStateException: menu.findItem(R.id.action_settings) must not be null - diadia
実装手順
アクティビティ.ktの実装
- onCreateOptionsMenuのオーバーライドで、表示するオプションを決定する
フラグメント.ktの実装
- onCreateViewの中のsetHasOptionsMenu(true)を選択
- onCreateOptionsMenuのオーバーライドで、表示するオプションを決定する
- onOptionsItemSelectedでメニューの処理を設定
setHasOptionsMenu(true)に関する資料(ドキュメント)
EditTextをマテリアルデザイン化する
https://developer.android.com/reference/com/google/android/material/textfield/TextInputLayout
https://developers-jp.googleblog.com/2015/07/android-design-support-library.html
要はTextInputLayoutビューを実装すれマテリアルデザイン化することができる。
当該ビューの属性を設定することで色々な機能をもたせることは分かった。
TextInputLayoutのプレイスホルダーに関して
これについてはEditTextでプレイスホルダーを設定するのと同じ様に、hint属性にセットすればよい。
TextInputLayoutをでバリデーションを行う
つまりはエラーを表示させたいわけだけれども、それには
Showing an error via setErrorEnabled(boolean) and setError(CharSequence), along with showing an error icon via setErrorIconDrawable(Drawable)
と書いてあるとおり、上記を利用すれば良い。
RecyclerViewまとめ
メモ
今までEmptyActivityからリサイクラービューを実装してきたが、フラグメントからリサイクラービューを実装する方法の方が割と楽に実装できることが分かった。これについてはまだブログに記録を残していないので時間があるときに作成する。
インストール方法
gradleのdependenciesに以下を追加する。
implementation "androidx.recyclerview:recyclerview:1.1.0"
バージョンの確認については以下をみて確認すれば良い。
Recyclerview | Android デベロッパー | Android Developers
サンプルコード
retrofit, backendにdjangoをを使用
リサイクラービューにリスナーをセット
リサイクラービューではOnClickListenerをセットすることはViewHolderの継承でインターフェースにOnClickListenerを実装すればよい。
リサイクラービューでOnItemClickListenerを実装するほう法がわからん。 https://gist.github.com/arcadefire/1e3a95314fdbdd78fb211b099d6ec9da
画面表示の際に下部の要素を表示する
参考:RecyclerViewを下から表示するにはstackFromEnd = trueを入れる · GitHub layoutmanagerの要素としてstackFromEnd = trueを追加すればい。
val layoutManager = LinearLayoutManager(context).apply{ stackFromEnd = true }
django-rest-authで"detail": "Authentication credentials were not provided."が返される時
参考
エラーメッセージ
{"detail": "Authentication credentials were not provided."}
エラーに出くわした状況
rest-authのエンドポイント/rest-auth/password/change/でパスワード変更を試みたが上記のレスポンスが返ってきた。 リクエストはcurlコマンドで送っており、ヘッダーにトークンを添付しているにも関わらず、認証に失敗している状況である。
curl -X POST http://127.0.0.1:8000/rest-auth/password/change/ -d 'new_password1=12345tweet&new_password2=12345tweet&old_password=1234tweet' -H 'Authorization: Token ebfa00bd84de2b8f319b747636270257ec24601c'
解決方法
参考のページと全く同じ方法で解決した。 かんたんに言えば、djangoのsettings.pyにおいてREST_FRAMEWORKを設定することだった。自分の場合は各APIViewのサブクラスの属性情報としてパーミッションの記述や認証の記述を書いて開発してきた。しかしながらdjango-rest-authの場合は認証やパーミッションについて設定されていない。したがってヘッダーにトークンを添付しても認証が行われなかった。 django-rest-authに認証方法を設定するために以下の記述をするとトークンによる認証もうまくいった。
######################## ###Django-Rest-Framework### ######################## REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.TokenAuthentication', 'rest_framework.authentication.BasicAuthentication', ), 'DEFAULT_PERMISSION_CLASSES': ( # https://www.django-rest-framework.org/api-guide/permissions/#setting-the-permission-policy #'rest_framework.permissions.IsAdminUser', #'rest_framework.permissions.IsAuthenticated', #'rest_framework.permissions.AllowAny', ), }
関連記事
EditTextにplaceholderを設ける
参考
EditTextで、ヒント文字列を設定する | mokelab tech sheets
Android開発ではHint属性と呼ぶ
HTMLで使われるplaceholder属性は、android開発においてEditTextのHint属性にあたる。 したがってplaceholderのような機能を実装したければ、hint属性にテキストを入力すれば良い。
関連記事
DRF PATCHの実装方法
参考
Retrofit 2 — How to Update Objects on the Server (PUT vs. PATCH)
How to make a PATCH request using DJANGO REST framework - Stack Overflow
Serializers - Django REST framework
まずPATCHとは
パッチはインスタンスの更新を行うHTTPプロトコルのメソッドである。インスタンスの更新はPUTとPATCHの2つの方法が存在する。 PUTはインスタンスすべてを書き換えることで、PATCHはインスタンスの一部を書き換えることのようだ。
やり方イメージ
Django Rest Frameworkにはシリアライザがある。シリアライザに変更したい内容、変更したいインスタンス、変更は一部のみを明示するpartial=Trueをシリアライザの引数として渡すことでPATCHが実現される。
Serializers - Django REST framework
もちろん、APIViewのメソッドとしてpatchを宣言するし、シリアライザの結果にたいしてsave()メソッドを実行することは言うまでもない。
サンプル
from .utils import getTokenFromHeader from .utils import getUserByToken class ProfileAPIView(APIView): authentication_classes = (TokenAuthentication,) permission_classes = (permissions.IsAuthenticated,) def patch(self, request, *args, **kwargs): #profileオブジェクトを更新する token = getTokenFromHeader(self) user_obj = getUserByToken(token) profile_obj = Profile.objects.get(user=user_obj) serializer = ProfileSerializer(profile_obj, data=request.data, partial=True) serializer.save() return Response({"result": "success"})
関連記事
エラーが出てpatchを実行できないパターン
AssertionError: The `.update()` method does not support writable nested fields by default. Write an explicit `.update()` method
この場合はあるモデルを表すJSONの内部に更に他のモデルがネストされている場合にエラーが生じると考えられる。
DRF tokenからuserオブジェクトを取得する
userオブジェクトを取得する道筋
例えばandroidの画面にuserのプロフィールを表示したいとする。そのためには、userオブジェクトが必要だ。 どの様にすればUserオブジェクトが取得できるのか。
TokenAuthenticationを採用している場合にはユーザーに紐付いたトークンがデータベースに保存してある。 したがってトークンを利用すればUserオブジェクトを取得できる。
では、トークンはどうすればサーバー側(django側)で取得できるのか。 トークンはHTTPリクエストのヘッダーに添付されて送られる。 したがってリクエストのヘッダーからトークン情報をとってくれば良い。
サンプルコード
from rest_framework.authtoken.models import Token class ProfileAPIView(APIView): authentication_classes = (TokenAuthentication,) permission_classes = (permissions.IsAuthenticated,) def get(self, request, *args, **kwargs): #HTTPリクエストヘッダーのトークン情報からユーザーを特定する token = self.request.META['HTTP_AUTHORIZATION'].split(" ")[1] #print(token) #Userオブジェクトの取得 user_obj = Token.objects.get(key=token).user profile_obj = Profile.objects.get(user=user_obj) serializer_context = {} serializer_context["profile_obj"] = ProfileSerializer(profile_obj).data return Response(serializer_context)
解説
httpプロトコルのヘッダー情報は、request.METAから取得することができる。 リクエストヘッダの内容を取得する まとめ - diadia
rest_framework.authtoken.models.Tokenクラスがある。 これを利用するとUserオブジェクトを直接取得できる。 参考: python - Get user object from token string in DRF? - Stack Overflow
retrofit multipart送信を行う
コンテンツ
- Multipart通信における知見
- 関連記事
- 参考
Multipart通信における知見
実装概要
実装ではInterfaceの設定とretrofitでinterfaceメソッド実行を行う。
Interfaceについて
//Itemオブジェクトを生成する //認証ユーザーのみリクエスト送れる仕組みが必要 //@Headers("Content-Type:application/json") @Multipart @POST("api/item_create_1/") fun postItemCreateAPIViewMultiPart( @Header("Authorization") authTokenHeader: String, @Part file:MultipartBody.Part, @Part("testpart") requestBody: RequestBody ):Call<ResultModel>
@Partには2つの使い方がある。 Partに付随するのはMultipartBody.Partであるか、RequestBodyであるかである。
テキストデータを送信する場合には、 RequestBodyオブジェクトを使う。 画像を送信する場合には、画像ファイルをラッピングしたMultipartBody.Partを使う。
RequestBodyオブジェクトを生成する
val reqBody :RequestBody = RequestBody.create(MediaType.parse("application/json"), itemObj.toString())
RequestBodyクラスについて
RequestBody (OkHttp 3.14.0 API)
RequestBodyクラスはOkHttp3のクラスである。
returns | method |
---|---|
static RequestBody | create(MediaType contentType, String content) |
static RequestBody | create(MediaType contentType, File file) |
RequestBodyにはstaticメソッドのcreateがある。 このメソッドの第1引数は MediaType オブジェクトである。 第2引数はいろいろ種類がある。 自分が使うものとして、FileオブジェクトとStirngオブジェクトがある。画像を送信したい場合には画像のFileオブジェクトを渡す。テキストデータの場合はStringオブジェクトを渡す。
このインスタンスは、テキストデータならこのまま送れるし、画像データならこれを核にしてMultipartBody.Partオブジェクトを生成する。
var strContent = "{ 'item' : {'id':3, 'title':'医学書', 'author':'ドクター'} }" val mediaType = MediaType.parse("application/json") val reqBody = RequestBody.create(mediaType, strContent)
MultipartBody.Partオブジェクトを生成する方法
MultipartBodyクラスはRequestBodyクラスのサブクラスである。
retruns | method |
---|---|
static MultipartBody.Part | createFormData(String name, String filename, RequestBody body) |
画像を送信するためにMultipartBody.PartのcreateFormData()メソッドを使う。
val filepath = "hoge" val imageFile = File(filePath) val mediaType = MediaType.parse("image/*") val reqBody = RequestBody.create(mediaType, imageFile) val part = MultipartBody.Part.createFormData("part_name", imageFile.name, reqBody)
memo
上記のインターフェースとオブジェクトを組み合わせてmultipart通信を実行する。
疑問点
関連記事
参考
Android Image Upload Example | Multipart Retrofit2
retrofitでマルチパートなものをPOSTする - tumblr
Upload file in Android Java with retrofit 2 - Vo Thanh Tung - Medium
curlコマンドについて
まず以下のコマンドを理解する
curl -X GET http://127.0.0.1:8000/api/example/ -H 'Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b'
これの意味を調べる。
参考:
https://www.django-rest-framework.org/api-guide/authentication/#tokenauthentication
https://www.setouchino.cloud/blogs/99#post-x-post-d
-XオプションはHTTPメソッドを定めるものである。-Xの値はPOST や GETである。
-Hオプションはリクエストヘッダーに格納したい場合に使うものである。値がヘッダーに格納される。
-d (--data)オプションはデータを送る時に使うものでる。例えばwebサービスでnameというフォームが有り、"hajime"と入力し送信する場合には、curlでは以下のように送信されることになる。
curl -X POST -d 'name=hajime' example.com/send_info
データが複数ある場合には以下のように複数の方法で送信できる。
#①データとデータの間を&でつなぐ方法 curl -X POST -d 'name=hajime&age=23' example.com/send_info/ #②各データを-dオプションを使う方法 curl -X POST -d name=hajime -d age=23 example.com/send_info/
-dオプションの参考url: https://ec.haxx.se/http/http-post
JSONをcurlコマンドで送信する方法はどうすれば良いか?
curlでjsonを送信する際に考えなければならない要素は2つある。
- -d オプションで付すデータの形式を変更する
- json形式のデータであることを明示するためにheaderに情報を追加する
-d オプションのデータ形式
まずデータをシングルクオテーションでくくる。そして肝心のデータはJSON形式で記述する。 したがって以下のような形式で記述する。
curl -X POST -d 'JSON_data_forma' https://hogegoe.com/api/
具体的には、
curl -X POST -d '{"json1":"value1", "json2": "value2"}' https://hogegoe.com/api/
headerに情報追加
ヘッダーに情報を追加するには、-H オプションを使う。
JSONデータ形式を明示するには-Hオプションの値として'Content-Type:application/json'を使う。
curl -X POST -d '{"json1":"value1", "json2": "value2"}' https://hogegoe.com/api/ -H 'Content-Type:application/json
https://developer.mozilla.org/ja/docs/Web/HTTP/Headers
https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Type