SSブログ
前の10件 | -

1.15 Django REST Framework(DRF) 画像アップロード [Django]

Django Rest Frameworkで画像アップロードの実現サンプルとなります。

画像アップロードは、カスタムのUserModelに、imageという画像用列を追加します。

動作環境 pip list コマンド結果は下記
asgiref                 3.5.2
Django                  3.2.15
django-cors-headers     3.10.1
django-filter           22.1
djangorestframework     3.13.1
djangorestframework-jwt 1.11.0
importlib-metadata      4.12.0
Markdown                3.4.1
mysqlclient             2.1.1
Pillow                  9.2.0
pip                     22.2.2
PyJWT                   1.7.1
pytz                    2022.2.1
setuptools              57.5.0
sqlparse                0.4.2
uWSGI                   2.0.20
wheel                   0.37.1
zipp                    3.8.1


DRFサイトに記載があるfileuploadparser、MultiPartParserを使用します。
クライアント側から送信時はmedia_typeを「multipart/form-data」にしてくれとのこと
https://www.django-rest-framework.org/api-guide/parsers/#fileuploadparser

setting.py
REST_FRAMEWORK = {
    #
    # Default Parser Class Setting
    # https://www.django-rest-framework.org/api-guide/parsers/#fileuploadparser
    'DEFAULT_PARSER_CLASSES': [
       'rest_framework.parsers.JSONParser',
       'rest_framework.parsers.MultiPartParser',
    ],
}

STATIC_URL = '/static/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'



カスタムのUser Model(画像保存の項目はimage)
model.py
class User(AbstractBaseUser, PermissionsMixin):
    uuid = models.UUIDField(default=uuid_lib.uuid4,
                            primary_key=True, editable=False)
    username_validator = UnicodeUsernameValidator()

    username = models.CharField(_("username"), max_length=50, validators=[username_validator], unique=True)
    first_name = models.CharField(_("first_name"), max_length=50, )
    last_name = models.CharField(_("last_name"), max_length=50, )
    nick_name = models.CharField(_("nick_name"), max_length=50, )
    email = models.EmailField(_("email_address"), unique=True)
    is_staff = models.BooleanField(_("staff status"), default=False)
    is_active = models.BooleanField(_("active"), default=True)
    authorization = models.IntegerField(_("authorization"), default=0 )
    date_joined = models.DateTimeField(_("date joined"), default=timezone.now)
    image = models.ImageField(upload_to='images/', blank=True)

    objects = UserManager()
    USERNAME_FIELD = "username"
    EMAIL_FIELD = "email"
    REQUIRED_FIELDS = ['email',]

    class Meta:
        verbose_name = _("user")
        verbose_name_plural = _("users")

    def clean(self):
        super().clean()
        self.email = self.__class__.objects.normalize_email(self.email)

    def email_user(self, subject, message, from_email=None, **kwargs):
        send_mail(subject, message, from_email, [self.email], **kwargs)


とくだん説明はないです。
urls.py
from users.views import (
    UserViewSet, GroupViewSet,
)
router = routers.DefaultRouter()
router.register(r'users', UserViewSet)

urlpatterns = format_suffix_patterns(urlpatterns)
urlpatterns += router.urls


クライアントからは、Formdataで、"image"というキーワードにイメージファイルを設定してる場合request.data.imageでアクセス出来る。
InMemoryFileUploadHandlerというオブジェクトになるらしいので、
どんなメソッドがあるかの確認はソースをみると理解出来ます。
Djaogo Document

保存するファイル名は、request.data['image'].nameに設定することで
親クラスのupdateを呼出すことで、Django側が良しなに処理してくれる。
(画像保存先のフォルダまで作成してくれるかはしらん)
ソース内のコメントアウトしているコードは、Djangoが保存してくれると知らずに、
自力で保存した処理を記念?に残している

view.py
from rest_framework.parsers import FileUploadParser

class UserViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows users to be viewed or edited.
    """
    parser_class = (FileUploadParser, MultiPartParser)

    queryset = User.objects.all().order_by('username')
    serializer_class = UserSerializer
    # permission_classes = [permissions.IsAuthenticated]

    #
    # Filte機能設定
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['uuid', 'username','email', 'first_name', 'last_name','nick_name']

    def update(self, request, *args, **kwargs):
        logger.debug("Request Data")
        print(request.data)
        image_file = ""
        if 'image' in request.data:
            image_file = "images/" + str(uuid_lib.uuid4()) + ".png"
            request.data['image'].name = image_file
        super(UserViewSet, self).update(request, *args, **kwargs)
        '''
        if 'image' in request.data:
            logger.debug("Image File Write")
            # 保存してみる
            logger.debug(f'MEDIA_ROOT={settings.MEDIA_ROOT}')
            image_file = "images/" + str(uuid_lib.uuid4()) + ".png"
            image_fullpath = os.path.join(settings.MEDIA_ROOT, image_file)
            # reqeust.data['image']は、InMemoryFileUploadHandlerというオブジェクトらしい
            with open(os.path.join(settings.MEDIA_ROOT, image_file), 'wb') as f:
                for chunk_data in request.data['image'].chunks():
                    f.write(chunk_data)
            logger.debug(f'Image File Write Success file[{image_file}]')
        else:
            logger.debug("No Image File")
        '''
        return Response({'success': 'Imported successfully'})



これでクライアントから、PATCH or PUT メソッドで送信すればUserの更新が出来ました。
もちろん、他のDB項目であるusernameやfirst_nameも更新されました。


クライアントは、Next.jsでやったのですが、axiosを利用せず、
fetch関数で実行しましたが、沢山はまったけど、なかでもハマリポイントは下記だった。
Content-Typeを指定しないようにしよう

Next.jsの理解が進んだら、アップロードのクラアント側も掲載しようかな・・?









nice!(0)  コメント(0) 
共通テーマ:パソコン・インターネット

独自Layerを参照している Lambda(Python)をローカルでステップ実行する方法 [AWS]

はじまり
 Serverless Frameworkで独自PythonライブラリをLayer化しているのですが
 Lmabda(Python)をローカルでステップ実行出来る環境を作りたいという思いが始まりでした。

環境情報:
 ローカルマシン:Windows10 (64bit)
 python:3.8.5
 node:16.5.0
 serverless framework:2.51.2
 sam:1.26.0
 aws-cli:2.0.48
 VSCode:1.58.2
 Docker:3.4.0

調査結果
 きっと出来ると思いますが、PythonでServerless Frameworkでは、
 frameworkVersion1.Xでないと、
 ローカルでステップ実行出来ないというのが自分の調査結果でした。
 それだと、他箇所がエラーになり出来ないので諦めました・・(無念)
 他の方法で見つけたやり方は、ServerlessFrameworkでDeployしたモジュールを
 AWSToolkit forVisual Studio Codeでローカルに落として、SAMを利用して
 ステップ実行する方法です。


1.はじめに
 SAMを使ってLayerを参照しているLambdaをローカルで実行出来る
 プロジェクトを下記サイトに沿って作ります。
 https://qiita.com/herohit-tool/items/4b4edcaa26c9b5eeaf2c

2.AWSToolkit forVisual Studio Codeをインストします
   https://docs.aws.amazon.com/ja_jp/toolkit-for-vscode/latest/userguide/welcome.html

3. VSCodeで空のプロジェクトを開く

4. AWSToolkit forVisual Studio Codeから1で作成したLambdaをダウンロードします。
 ここでは、sample-004-HelloWorld・・がそれ
Untitled.png


5. ダウンロード終わるとlaunch.jsonにも実行用のコードが勝手に追加されます。
 {
    "configurations": [
        {
            "type": "aws-sam",
            "request": "direct-invoke",
            "name": "sample-004-HelloWorldFunction-zbFE0M6ZEbAX:app.lambda_handler (python3.8)",
            "invokeTarget": {
                "target": "code",
                "projectRoot": "${workspaceFolder}/sample-004-HelloWorldFunction-zbFE0M6ZEbAX",
                "lambdaHandler": "app.lambda_handler"
            },
            "lambda": {
                "runtime": "python3.8",
                "payload": {},
                "environmentVariables": {}
            }
        }
    ]
}

 
6. 試しにこれを実行してみます。エラーになる。
 No Module named user_numpy とでていてこのままではLayerは参照されない。

2Untitled.png

7. プロジェクト直下にtemplate.yamlファイルを作成する
 Layers: の値は、AWSにログインして、現在DeployされているLayerのARNを張る。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  sam-app

  Sample SAM Template for sam-app

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 3

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: sample-004-HelloWorldFunction-zbFE0M6ZEbAX/
      Handler: app.lambda_handler
      Runtime: python3.8
      Events:
        HelloWorld:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /hello
            Method: get
      Layers:
        - << AWSにログインして、現在DeployされているLayerのARNを張る。 >>
        # 例: - arn:aws:lambda:ap-northeast-1:99999999:layer:HelloWorldLayer:1
    Metadata:
      BuildMethod: python3.8


8. launch.jsonを下記内容に変更
 8~10行目が変更箇所
{
    "configurations": [
        {
            "type": "aws-sam",
            "request": "direct-invoke",
            "name": "sample-004-HelloWorld-Yaml",
            "invokeTarget": {
                "target": "template",
                "templatePath": "template.yaml",
                "logicalId": "HelloWorldFunction",
            },
            "lambda": {
                "runtime": "python3.8",
                "payload": {},
                "environmentVariables": {}
            }
        }
    ]
}


9. 実行
 無事にブレーク止まった!
Untitled.png

出力結果も問題なし
START RequestId: e69bde63-a0b5-4a72-9902-4484d1073c3e Version: $LATEST
Prepending Lambda task root to path: /var/task
Starting debugger...
Debugger waiting for client...
2021-07-18 17:47:25 [INFO]: Attaching debugger to SAM application...
2021-07-18 17:47:29 [INFO]: Debugger attached
END RequestId: e69bde63-a0b5-4a72-9902-4484d1073c3e
REPORT RequestId: e69bde63-a0b5-4a72-9902-4484d1073c3e	Init Duration: 0.61 ms	Duration: 17170.39 ms	Billed Duration: 17200 ms	Memory Size: 128 MB	Max Memory Used: 128 MB	
{"statusCode": 200, "body": "{\"message\": \"0,1,2\"}"}
Command stopped: "sam local invoke"


Layerを参照しているプロジェクトは下記(Layer側のプロジェクトは無いです)
https://github.com/MakotoPlus/004_sam_sample_layers

10. 最後に
 出来て良かったけど、自分は結構ログ出力して調査するタイプだから
 あまり使わないんだけどねw今まで使ってなかったしw
 ネットには、TypeScriptでステップ実行する参考サイトが多かった。最近流行ってるのかな?

参考サイト:
チュートリアル: Hello World アプリケーションのデプロイ
[AWS] SAM + Lambda(Python)で、PythonライブラリをLayerに追加して利用してみる
サーバーレスアプリケーションのデバッグ用の設定オプション
VS Codeで AWS Toolkit を使って Lambda 関数をローカルでテストする方法 (AWS SAM)
AWS SAMによるLambda Layers & Layers利用Functionの作成手順と運用観点での注意点(2021年3月, SAM 1.19版)
ローカルでLambda開発環境の構築手順紹介

 








nice!(0)  コメント(0) 
共通テーマ:パソコン・インターネット

ReactとAngularを勉強してみた感想

JavaScriptのアロー関数もようわからない状況から
Angular、次にReactの勉強をやってみたが・・。
Angularは、RxJSで挫折しながらなんとか理解出来たが
Reactは、理解に苦しんだ。Dom内にロジックもHTMLタグも
Dom内に記述するため、煩雑になったのが理由かな。
Reduxとかも分かりづらい・・。

自分はもともと、JavaでWeb開発していたのでJSPを利用していたので、
下記記事でいうとHTMLベース開発しかやった事が無く
自分の中でAngularの方が扱いやすいという感じになった理由としてとてもしっくりした。
https://qiita.com/mqtsuo02/items/5701238e666e20416664


RactとAngularどっちという記事は下記だったり様々あったりします。
https://qiita.com/kamykn/items/2e3d6d2a71ae774d080a
https://qiita.com/nirasan/items/4e14e9b56c65ce2a5069


いままで要件定義などお客とやる場合は、
イメージ画面をExcelのオブジェクトを使ってなんとなく
イメージさせるか、HTMLでモックを作ったりしていました。
そのHTMLをJSPなどの元ネタにするなんて事は
よくあるパターンでした。
ReactはJavaScriptベースなので、上記のような事が
出来ないと思われるので、要件から作業したり、
デザイン会社と共同で同時開発するなどは、ちょっと
やりずらそうだなーと感じました。


世の中の流行りには勝てないので
次の趣味アプリはReactで頑張ってみようかなー・・。
Angularが流行ってくれると嬉しいんだけど・・。


タグ:Angular react
nice!(0)  コメント(0) 
共通テーマ:パソコン・インターネット

1.14 AWS CognitoアカウントでDjangoにログイン(シングルサインオン) [Django]

最近、AWSを仕事で触ったりSPAに触れています。
DjangoでAWSのCognitoを利用してログインが出来たので
ここにやり方の備忘録を記載する。

仕様:
・Django通常?ADFS連携などと同様に、Cognitoアカウントでログインした時にそのユーザが存在しなければ
 Django管理DBへユーザ情報を保存してくれます。
・Cognitoから受け取ったトークン情報をSessionに保持してくれます。
・シングルサインオンが実現出来る

--------------------------------------------------
▼追記
 あとで調べると、こちらのパッケージで、ソース修正なしで対応されているらしい。
 次回はこちらのツールを使って確認したいと思います。
https://django-allauth.readthedocs.io/en/latest/installation.html

▲追記ここまで
--------------------------------------------------
--------------------------------------------------
▼2021.10.23 追記
django-allauthを使って動作確認はこちらのサイトに記載しました。
https://qiita.com/MakotoPlus/items/894bc8c6c408fddc79a8

▲2021.10.23 追記ここまで
--------------------------------------------------

下記が環境構築作業になります。
前提条件、AWS側のcognito環境は出来ている。

1.初めに環境とか
ライブラリは、django-warrant 0.6.1を利用
下記pipコマンドでインストール
Python version 3.8.5
Django==3.2
django-warrant
django-utils-six==2.0


pip list コマンド結果は下記
Package                Version
---------------------- ---------
asgiref                3.3.4
boto3                  1.17.62
botocore               1.20.62
certifi                2020.12.5
chardet                4.0.0
Django                 3.2
django-braces          1.14.0
django-crispy-forms    1.11.2
django-extensions      3.1.3
django-utils-six       2.0
django-warrant         0.1.0
ecdsa                  0.16.1
envs                   1.3
future                 0.18.2
idna                   2.10
jmespath               0.10.0
pip                    21.1.1
pycryptodome           3.3.1
python-dateutil        2.8.1
python-jose-cryptodome 1.3.2
pytz                   2021.1
requests               2.25.1
s3transfer             0.4.2
setuptools             47.1.0
six                    1.15.0
sqlparse               0.4.1
urllib3                1.26.4
warrant                0.6.1


2. DJangoプロジェクを作成

django-admin startproject cognito_prj


起動確認(画像は使いまわし)
コマンド:python manage.py runserver
WS000003.JPG


3. cognito_prj/setting.pyファイルの修正
django-warrantで利用される情報を設定して行く

#
# 自分のセキュリティKEYを設定してね
SECRET_KEY = ''

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django_warrant',
    'crispy_forms',
    'django_extensions', 
    'cognito_prj'  
]

#
# 認証情報追加 
# (django.contrib.auth.backends.ModelBackendはデフォルト)
AUTHENTICATION_BACKENDS = [
    'django_warrant.backend.CognitoBackend',
    'django.contrib.auth.backends.ModelBackend'
]

# AWS CognitoユーザプールのプールID
COGNITO_USER_POOL_ID='XXXXXXXX'
# AWS CognitoアプリクライアントID
COGNITO_APP_ID='XXXXXXXXXX'

COGNITO_ATTR_MAPPING={
    'email': 'email',
    'given_name': 'first_name',
    'family_name': 'last_name',
}
# True=Djangoユーザを作成する(Default)
# False=既存Djangoユーザのみが更新される
COGNITO_CREATE_UNKNOWN_USERS=True

# 使用したいAWSアクセスキーを設定します。デフォルトはNoneで、~/.aws/credentialsファイルにあるデフォルトのクレデンシャルが使用されます。
# AWS_ACCESS_KEY_ID
# AWS_SECRET_ACCESS_KEY


4. DB作成と、管理者ユーザ作成

下記コマンドを実行
python manage.py migrate
python manage.py createsuperuser


起動確認して管理画面アクセス確認
WS000069.JPG


6. cognito_prj/urls.pyソースの修正
(1) ログイン画面のURLは accounts/は、django_warrantパッケージ側でCognitoとのやりとりを処理してくれる
 (2) user_info/は、ログイン後にトークンなどを参照できる画面

app_name ='cognito_prj'
from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include
from django.contrib.auth.views import LoginView, TemplateView

urlpatterns = [
    path('admin/', admin.site.urls),
    url(r'^accounts/', include(('django_warrant.urls','dw'))),
    path(r'user_info/', TemplateView.as_view(template_name='warrant/user_info.html'), name='user_info'),
    url(r'', LoginView.as_view(), {'template_name': 'warrant/login.html'}, name='login'),
]


7. cognito_prj/templatetags/cognito_tags.pyファイル
どこかでtaglibを利用しているらしく作成が必要らしい
from django import template

register = template.Library()

@register.filter('username')
def username(user):
    return user._metadata.get('username')



8. htmlテンプレートファイル群
 ログイン画面などは実際には自分で作成する必要があるが、
 サンプルのHTMLが用意されているのでダウンロードして配置する。
実際にどこか読み込まれているか分からないが面倒だったので下記3フォルダに配置した。
 cognito_prj/templates/warrant/
 templates/warrant/
 templates/registration/

 サンプルHTMLファイル
https://github.com/MetaMetricsInc/django-warrant/tree/master/django_warrant/templates/warrant


8. django-warrant ライブラリソースの修正
 django-warrantが Django3.0に対応されていないためこのままでは動作しない・・。
 ライブラリ更新がされていないからなのかな?
 下記内容に書き換える必要がある。

 いつか更新されるといいね。
 (1) backend.py ファイルの修正
  Djangoのバージョンチェックをしているがここが正しく動作していないらしい。
   ググったりしてもこの情報はなんこかヒットしていましたのでそちらを参照し修正

ファイル:venv\Lib\site-packages\django_warrant\backend.py
if DJANGO_VERSION[1] > 10:
↓
if DJANGO_VERSION[1] > 10 or DJANGO_VERSION[0] > 1:

参考サイト

 (2) urls.pyファイルの修正
auth_views.login, auth_views.logout がエラーを起こすので書き換え
from django.conf.urls import url
from django.contrib.auth import views as auth_views
from .views import ProfileView,UpdateProfileView,MySubsriptions,\
    AdminListUsers,AdminSubscriptions
#
# Add.
from django.contrib.auth.views import LoginView
from django.contrib.auth.views import LogoutView

urlpatterns = (
    #url(r'^login/$', auth_views.login, {'template_name': 'warrant/login.html'}, name='login'),
    #url(r'^logout/$', auth_views.logout, {'template_name': 'warrant/logout.html'}, name='logout'),
    url(r'^login/$', LoginView.as_view(), {'template_name': 'warrant/login.html'}, name='login'),
    url(r'^logout/$', LogoutView.as_view(), {'template_name': 'warrant/logout.html'}, name='logout'),
    url(r'^profile/$', ProfileView.as_view(),name='profile'),
    url(r'^profile/update/$', UpdateProfileView.as_view(),name='update-profile'),
    url(r'^profile/subscriptions/$', MySubsriptions.as_view(),name='subscriptions'),
    url(r'^admin/cognito-users/$', AdminListUsers.as_view(),name='admin-cognito-users'),
    url(r'^admin/cognito-users/(?P[-\w]+)$', AdminSubscriptions.as_view(),name='admin-cognito-user')
)


ファイル:venv\Lib\site-packages\django_warrant\urls.py


9. 稼働確認
(1) Django起動して下記URLへアクセスしてみます
http://localhost:8000/accounts/login/
WS000070.JPG

(2) AWSアカウントユーザのログインID,パスワードを
入力してログインすると下記画面でアカウント情報も無事に出力されます。
WS000071.JPG

(3) トークンID確認のため下記URLへアクセスすると、
セッションに保存されているのが確認できます。
http://localhost:8000/user_info/

WS000072.JPG


10. 最後に
 (1) このサンプルではユーザ変更も出来そうな感じですがこちらの動作は確認していません。
 (2) 今回動作確認したプロジェクトはこちらのGitに保存しています。
https://github.com/MakotoPlus/cognito_prj


本家サイト:https://github.com/MetaMetricsInc/django-warrant
参考サイト: https://cloudstep.io/2019/03/18/cognito-authentication-integration-with-django-using-authorization-code-grant/








nice!(0)  コメント(0) 
共通テーマ:パソコン・インターネット

IT系 業務経歴書 (Business History Repost) アプリ紹介 [IT系 業務経歴書 (Business Hist]

IT系 業務経歴書作成アプリ(Business History Repost) - 無料
・自分のプロフィール、携わってきた業務内容を入力しPDF出力するアプリ
  補足:SPA(Angular)、AWSの勉強がてらに、作って見ました。

 無料のIT系 業務経歴書作成アプリへのリンク(Business History Repost)

ユーザID:bizoto@nekochan.fr
パスワード:Ztest@987+

特徴
1.SIの面談時などに利用しやすいように
  出力タイミングで下記項目内容の切替が可能
  ・名前を実名とイニシャルの切替
   WS000065.JPG

  ・社名表示、非表示の切替
   WS000070.JPG

2.Webアプリケーションなのでその場でちゃちゃっと修正していつでも再出力可能です。

3.印刷日は指定でき、印刷日を基準に年齢も計算されます
  WS000071.JPG

4.業務経歴年数の自動計算や、技術のサマリ表の表示
  ・得意技術、アピールポイントの明確化
   (技術は、一番年数の多いもの順にソートされます)
   WS000068.JPG

5.PDFファイルをサーバに保存しません
  ・出力しているPDFはサーバに一切保存せずに作成していますので
   セキュリティ観点で安心です。

6.画面イメージ
  WS000062.JPG

6.印刷イメージ(1)
イニシャル+企業名なし
WS000066.JPG

7.印刷イメージ(2)
実名+企業名あり
WS000067.JPG
  
8.いかなる問題が発生しても責任は問いかねます。

9.ご要望、機能拡張、質問などありましたらコメント下さい。


nice!(0)  コメント(1) 
共通テーマ:パソコン・インターネット

DynamodbをCSV形式でエクスポートする [AWS]

Djangoのいろんな機能をまとめるためにもっと記事を書きたいのだけど
ちょっと仕事が相変わらず忙しい。最近は、AWS Lambda 系(Python)のテストを
やってるんだけど、DynamoDBを使っています。

そこで、テストだからDBの中身のDIFFをとったりするんだけど
さくっとCSVでデータ出力出来るツールが欲しいと思い
ここら辺

見つけたんだけどNode.js で動くらしいのだけど自分の環境でうまくうごかなかったりして
(設定ミスってるんだと思うけど)
断念して、自分で作って見ました。
下記Gitにアップしました。
https://github.com/MakotoPlus/PyDynamo2csv
機能としては、指定したフォルダ配下に日時(yyyymmdd_hhmmss)フォルダを作って、
パラメータで指定されたテーブル名のCSVファイルをテーブル単位で保存します。

C:\temp フォルダにMusic というテーブルのCSVファイルを出力したい場合
実行コマンド:
python PyDynamo2csv.py c:\dynamocsv Music

テーブル名は大文字小文字区別するので気を付けて


複数テーブルを出力したい場合:
python PyDynamo2csv.py c:\dynamocsv Music,NextTable


もし使う方がいれば、使って下さい。
そして「Nice!」を押して頂けると励みになります!


nice!(0)  コメント(0) 
共通テーマ:パソコン・インターネット

1.13 IISでpywin32を動作させる方法(DJango 2.2.7) [Django]

訂正事項:2020年10月

Windows Updateでセキュリティが厳しくなったかで
うまく動作しなくなったので訂正。

1. 下記説明に出てくるIUSRユーザ、 IIS_IUSRSユーザ箇所を
通常ユーザのAdministrator権限ユーザ指定に置き換える
2.IISのコネクションプール詳細設定画面で、アプリケーションプールIDを
  ビルトインアカウントのユーザ指定を、カスタムアカウント指定にし
  1で指定した同じユーザアカウントを指定する作業を追加する。
3.祈りながら、IIS再起動

---------------------------------------------------------------------------------------------------------------

Pywin32をIIS上で動作させようとしたら、エラーとなった。
意外とネット上に方法が記載されていなくて苦労したので
自分の備忘録としても記載。

Django付属のAPPではうまく動作していたのに、
IIS上では動作しなかった。
Pywin32は、COMを利用しているため、
IISからこいつを動かそうとなるとセキュリティ周りの設定が必要。
ちょっと作業的に蛇足もあると思うがとりあえず下記設定で動いた。

【動作確認済み(2020年07月頃環境】
Windwos10(64bit) + Django2.2 + Excel2016(64bit) + Python3.7.5(64bit)
Windwos2019 Server(64bit) + Django2.2 + Excel2016(32bit) + Python3.7.5(32bit)
Windwos2012R2 Server(64bit) + Django2.2 + Excel2016(32bit) + Python3.7.5(32bit)
Windwos2019 Server(64bit) + Django2.2 + Excel2016(64bit) + Python3.7.5(64bit)
Pythonと Excelのbit は合わせておいた方が良いはず。
(違うパターンは確認してない)
※Office365のExcel(64bit)でも動作しました。設定方法はだいたい同じ。



【前提】
IIS+Djangoの連携は出来ている状態

【作業1】
ディレクトリの作成
Excel2016の場合、ユーザが利用するフォルダが作成されていないらしくそれを作成する。
Excel64bit と 32bitで異なるらしいが面倒なので両方作っとく。

C:\Windows\System32\config\systemprofile\Desktop\
C:\Windows\SysWOW64\config\systemprofile\Desktop\

【作業2】
上記作成したフォルダに下記ユーザのアクセス権限をフルコントロールに設定する
1. IUSRユーザ
2. IIS_IUSRSユーザ

【作業3】
DCOMレジストリアクセス権限設定

1. コンポーネントサービスを起動する
 「ファイル名を指定して実行」
  mmc.exe comexp.msc /32
  参考

2.ExcelのDCOMセキュリティを変更する

 (1) DCOMの構成配下のMicrosoft Excel Application を選択する
WS000052.JPG

 (2) 右クリックでプロパティを表示、セキュリティタブを選択する

  A. 起動とアクティブ化のアクセス許可変更
    カスタマイズを選択し、編集ボタンをクリック
    追加ボタンクリックして、下記ユーザを追加

    a. IUSRユーザ
    b. IIS_IUSRSユーザ

    アクセス許可は、下記を許可する
    a. ローカルからの起動
    b. ローカルからのアクティブ化

  WS000056.JPG


  B. アクセス許可変更
    A.と同じようなことをやる
  C. 構成のアクセス許可
    A.と同じようなことをやる


(3)もしうまく行かなかった場合は、2(1)(2)の作業を下記(4)(5)(6)に切替える
(4) コンピュータのマイコンピュータを右クリックしてプロパティを表示し、
  COMセキュリティタブを選択する

WS000057.JPG


(5) アクセス許可の変更を行う
  A. 制限の編集ボタンクリックし、追加ボタンクリックし
    下記ユーザを追加する。
    a. IUSRユーザ
    b. IIS_IUSRSユーザ

  B. 上記方法だと全てのWebアプリからアクセス出来るようになり、
    セキュリティ上あまり良くないと思われる場合は、Excelを利用する
    IISのアプリケーションプールを限定として指定する事も可能
    例えば、DefaultAppPoolを指定したい場合は、下記のようにしていする。

    IIS APPPOOL\DefaultAppPool

    アクセス許可は、下記を許可する
    a. ローカルアクセス

WS000058.JPG


 (6) 起動とアクティブ化のアクセス許可の変更を行う
   (5) を参考に同じように設定する。

3.IISのアプリケーションプール設定変更
 (1) 今回Excelを利用するサイトのアプリケーションプールを選択し詳細設定を表示する
   A. 32ビットアプリケーションの有効化
     お約束で、自分の環境(PythonとかExcelとか)が32bitであれば、Trueに設定する
   B. ユーザプロファイルの読み込み
     Trueに設定する*ここポイント

WS000059.JPG


IISを再起動すれば、IIS+Django+Pywin経由でExcel操作が可能となります。

【最後に】
Excelって複数のVBAが同時に動作するとあまりうまく動作しないのは
みなさん経験があると思います。
Web経由でExcelを動作させるという事は、マルチスレッド起動される可能性が
あるため当然正しく動作しない。
なので、Excel動作する部分は、シングルスレッドで動作するような工夫が必要です。

まぁそもそも、Microsoft自体が、DCOMをIIS上で利用するのは保証していませんので、
Pywinではなく、openpyxlや他のライブラリを利用した方が無難です。
自分は、画像ファイルが貼り付けられているExcelを操作したり、
ExcelをPDF化したかったのでPywinを利用したけど。。。
もし同じような事で困っている方の助けになれば幸い。
だけど保証はしないです。












タグ:Django IIS Pywin
nice!(0)  コメント(0) 
共通テーマ:パソコン・インターネット

1.12 アプリケーションのフォルダ構成(DJango 2.2.7) [Django]

アプリケーションのフォルダ構成

Djangoでアプリケーションを作成すると、下記ファイルが作成され
それぞれコーディングして行けば良い

views.py
models.py
urls.py

だけど、一人でコーディングするならこれで良いのだけど、
沢山の人とコーディングするには更新ファイルがバッティングして不便・・。
Git使ってても毎回マージしてたら事故が起こりそうだし、
別々のファイルで開発したいなぁと考えてたけど、
urls で呼出すクラスをそれぞれ別ファイルにすれば良いだけなんだと
気づきました。。。(こんぐらいすぐ気づけよって感じですね・・)

今までのViewsファイルの記載していた処理をURL毎に
バラバラのファイルにして、「views」というフォルダを作成してその中に
保存しました。
※views.py は削除してます
WS000043.JPG

「forms」というフォルダも作成して
formもある程度のファイルに分離
WS000044.JPG

urls.pyの呼出しは、各viewsフォルダ配下の
ファイルを呼出すように変更。
こんな感じなら、複数人で開発出来そう。
#Recuruitment/applicantctl/urls.py
from django.urls import include, path
from django.conf.urls import url
from .views import index, add, add_judgment, upd
from .views.ItemDeleteView import ItemDeleteView
from .views.ItemUpdateView import ItemUpdateView

app_name ='applicantctl'

urlpatterns = [
    # ex: /
    path('', index.index_func, name='index'),
    path('list', index.index_func, name='index'),
    path('index', index.index_func, name='index'),

    # ex: /add/
    path('add/', add.add_func, name='add'),
    #url(r'^create$', T_ApplicantinfoCreateView.as_view()),
    # ex: /add/judgment/1/
    path('add/judgment//', add_judgment.add_judgment_func, name='add_judgment'),
    # ex: /upd/1/
    path('upd//', upd.upd_func, name='upd'),
    # ex: /delete/1/
    path('delete//', ItemDeleteView.as_view(), name='delete'), 
    # ex: /judgment/1/
    path('upd/judgment//', ItemUpdateView.as_view(), name='upd_judgment'),
]


modelsは、悩んだけど分離しませんでした。

全ソース(Git)
https://github.com/MakotoPlus/PycharmProjects4









nice!(0)  コメント(0) 
共通テーマ:パソコン・インターネット

1.10 Bootstrap 4.0+Django-bootstrap-datepicker-plusの利用(DJango 2.2.7) [Django]

今までDjango管理画面のCSSやカレンダーを利用していたが、
見栄えをBootstrap 4.0 に変更するために、
新たにPycharmProjectsd4 をこしらえました。
DBも、SQLite から MySQL 8.0.18に変更。


1.今回のポイント
(1) 日付入力(カレンダー)があるため、datepicker_plus を使いします。
(2) BootstrapのCDNは利用しません。
 パフォーマンスとか考慮するならCDNの方が良いようですが、
 仕事の本番環境を考慮し、ローカルからアクセスするようにしました。
 A. 本番環境がWebアクセス出来ない環境の場合でも耐えられるよう
 B. 何かしらの事由でCDNサイトが利用出来なくなっても耐えられるよう
 C. 社内システムのためそんなにアクセスが爆発的ではないため、
  さほどパフォーマンス考慮が不要なため

  ちなみにダウンロードして配置しているのは以下です。
  bootstrap-4.3.1-dist
  bootstrap-datepicker-1.9.0-dist
  moment.js
  popper.js
  tempusdominus-bootstrap-4
  fontawesome-free-5.11.2-desktop(今回のには関係ない)
  fontawesome-free-5.11.2-web(今回のには関係ない)

  以下フォルダに無造作に配置
  C:\PycharmProjects4\Recruitment\applicantctl\static\applicantctl

WS000029.JPG


2.作業

(1) django-bootstrap-datepicker-plusのインストール
  venv環境にて下記コマンドを実行
  pip install django-bootstrap-datepicker-plus

WS000031.JPG

(2) settings.pyの変更
  'bootstrap_datepicker_plus',を追加(11行目)

# Application definition
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'accounts.apps.AccountsConfig',
    'applicantctl',
    'bootstrap_datepicker_plus',
    'mytest',
]




(3) Baseテンプレートファイルの変更
  Bootstrap用のURLをbase.htmlへ設定。
  CDNは使わないので、
  {% load bootstrap4 %},{% bootstrap_css %},{% bootstrap_javascript jquery='full' %}
  は、きっと利用出来ないのでベタ書き。(8~11行目、48~49行目)

  Base.html
{% load static %}
<!doctype html>
<html lang="ja">
 <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 href="{% static 'applicantctl/bootstrap-4.3.1-dist/css/bootstrap.min.css' %}" rel="stylesheet">
    <!-- Bootstrap CSS Fotter-->
    <link href="{% static 'applicantctl/bootstrap-4.3.1-dist/sticky-footer-navbar.css' %}" rel="stylesheet">
    <title>applicantctl-django</title>
	{% block head_meta %}{% endblock %}
</head>
<style>
html { 
  font-size: 12px; 
}
</style>

<body>{% block navi %}
<nav class="navbar navbar-expand-sm sticky-top navbar-dark bg-primary mt-3 mb-3">
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav4" aria-controls="navbarNav4" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>
    <a class="navbar-brand" href="{% url 'applicantctl:index' %}">応募者管理システム(applicantctl-django)</a>
    <div class="collapse navbar-collapse justify-content-end">
        <ul class="navbar-nav">
            <li class="nav-item active">
                {% if user.is_authenticated %}
					<a class="nav-link"  href="{% url 'logout' %}" class="logout">Logout({{ user.first_name}} {{ user.last_name}})</a>
				{% else %}
	                <a class="nav-link" href="{% url 'accounts:signup' %}" class="signup">Sign up</a></li>
					<a class="nav-link" href="{% url 'login' %}" class="login">Login</a>
                {% endif %}
            </li>
        </ul>
    </div>
</nav>
{% endblock %}
{% block content %}{% endblock %}
{% block fotter %}
<footer class="footer mt-auto py-3 text-right">
    <span class="text-right">Copyright [コピーライト] 2019 Makoto Inc. All Rights Reserved.</span>
</footer>
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
{% endblock %}
<script src="{% static 'applicantctl/bootstrap-4.3.1-dist/js/jquery-3.4.1.min.js' %}"></script>
<script src="{% static 'applicantctl/bootstrap-4.3.1-dist/js/bootstrap.bundle.min.js' %}"></script>
</body>
</html>


(4) カレンダー利用画面用のテンプレート
  (Baseテンプレートを継承)

  base_datetimepicker.html
{% extends 'base.html' %}
{% load static %}
{% block head_meta %}

<script src="{% static 'applicantctl/bootstrap-4.3.1-dist/js/jquery-3.4.1.min.js' %}"></script>
<script src="{% static 'applicantctl/popper.js/popper.min.js' %}"></script>
<script src="{% static 'applicantctl/bootstrap-4.3.1-dist/js/bootstrap.min.js' %}"></script>

<!--bootstrap css-->
<!--local datapicker-->

<!--form.media-->
<link href="{% static 'applicantctl/bootstrap-datetimepicker_4.17.47/css/bootstrap-datetimepicker.css' %}" type="text/css" media="all" rel="stylesheet">
<link href="/static/bootstrap_datepicker_plus/css/datepicker-widget.css" type="text/css" media="all" rel="stylesheet">
<script type="text/javascript" src="{% static 'applicantctl/moment-2.9.0/moment-with-locales.min.js' %}"></script>
<script type="text/javascript" src="{% static 'applicantctl/bootstrap-datetimepicker.min.js_4.17.47/cdnjs/bootstrap-datetimepicker.min.js' %}"></script>
<script type="text/javascript" src="/static/bootstrap_datepicker_plus/js/datepicker-widget.js"></script> 


{% endblock %}

{% block content %}{% endblock %}



(5) 追加画面用のhtmlテンプレート
  1行目に、 base_datetimepicker.html を指定
  js、cssをリンクするだけで特にhtmlの内容を気にする必要は無い

  upd.html
{% extends "base_datetimepicker.html" %}
{% block content %}
</br>
{% if errmsg %}
<p class="alert alert-danger">{{ errmsg }}</p>
{%endif%}


<!--
{% for field, errors in form.errors.items %}
    {% for error in errors %}
        <p class="alert alert-danger">{{ error }}</p>
    {% endfor %}
{% endfor %}
-->

</br>

<form name="form" method="post" action="{% url 'applicantctl:upd' key_applicant %}">
		{% csrf_token %}
		<div class="container">
		{{ form.as_p }}	
		</div>
			</br>
			<div class="row">
			<div class="col-md-2">
			</div>
			<div class="col-md-2">
				<input type="button" class="btn btn-block btn-primary" OnClick="ButtonClick('1');" value="更新" >
			</div>
			<div class="col-md-2">
				<input type="button" class="btn btn-block btn-primary" OnClick="ButtonClick('2');" value="削除" >
			</div>
			<div class="col-md-6">
			</div>
		</div>
		</br>
	</div>
</form>


</br>
<script>
function ButtonClick(param){
	if (param == '2' ){
		document.form.action ="{% url 'applicantctl:delete' key_applicant %}";
		var result = confirm('削除してよろしいですか?' );
		if (result){
			document.form.submit();
		}
	}else{
		document.form.submit();
	}
}
</script>
{% endblock%}



(6) formクラスの変更
  追加日付項目という名前でDatePickerInputで年月表示のしカレンダーが表示されるように指定(14-24行目)
  ・日付フォーマットを、"%Y/%m"として指定しています。(18行目)
  ・optionで、ja(日本)と、months(年月表示)を指定している。(20-21行目)

(7) modelFormクラスの変更
  ・Metaクラス内で、年月日表示のしカレンダーが表示されるように指定(50-54行目)
  ・日付フォーマットを、"%Y/%m/%d"として指定しています。(51行目)
  ・optionで、ja(日本)を指定している。(52-54行目)

※当然ですが日付やオプションの指定方法は、Django-bootstrap-datepicker-plusでの指定方法となります。
 Bootstrap DatePickerやDateTimePickerの指定方法とは異なります。

  forms.py
import bootstrap_datepicker_plus as datetimepicker

#########################################
# 応募者登録フォーム
class T_Applicant_infoForm(forms.ModelForm):

    #
    #追加の場合はこれでOK
    #date_field = forms.DateField(
    #    widget=datetimepicker.DatePickerInput(format='%Y/%m/%d',)
    #    , label='追加日付項目'
    #)

    date_field = forms.DateField(
        label='追加日付項目',
        required = False,
        widget=datetimepicker.DatePickerInput(
            format='%Y/%m',
            options={
                'locale':'ja',
                'viewMode' : 'months'
            }
        )
    )

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

    class Meta:
        model = T_Applicant_info
        fields = ( 
            'applicant_date',
            'key_appl_route',
            'applicant_no',
            'applicant_name_text',
            'key_history_kbn',
            'u_date',
        )
        error_messages = {
            'applicant_no' : { 'required': '必須です!'
            },
            'applicant_name_text' : { 'required': '必須です!'
            }
        }
        widgets = {
            'applicant_date':datetimepicker.DatePickerInput(
                format='%Y/%m/%d',
                options={
                    'locale':'ja',
                }
            ),

        }




蛇足ですが、formatを %y/%m と年月にするとカレンダはそのように動作しましたが、
is_validで日付フォーマットではないとエラーになります。(まーとうぜんですね)



3.確認
 サーバを起動。
 画面レイアウトが少々ダサイのは、おいておいて、
 とりあえず bootstrapとして表示出来た。

WS000041.JPG
年月日バージョン
WS000042.JPG
年月バージョン
WS000034.JPG
ページング機能のレイアウトも以前より良い感じです。
ページング機能の掲載は別ページで。


中身を確認
実際に出力されたHTMLは下記内容となった。
応募日( ModelForm側 )の出力されたHTML
	<p><label for="id_applicant_date">応募日:</label> <div class="input-group date">
    <input type="text" name="applicant_date" value="2019/10/31" class="form-control" required id="id_applicant_date" dp_config="{&quot;id&quot;: &quot;dp_2&quot;, &quot;picker_type&quot;: &quot;DATE&quot;, &quot;linked_to&quot;: null, &quot;options&quot;: {&quot;showClose&quot;: true, &quot;showClear&quot;: true, &quot;showTodayButton&quot;: true, &quot;locale&quot;: &quot;ja&quot;, &quot;format&quot;: &quot;YYYY/MM/DD&quot;}}"/>
    <div class="input-group-addon input-group-append" data-target="#datetimepicker1" data-toggle="datetimepickerv">
        <div class="input-group-text"><i class="glyphicon glyphicon-calendar"></i></div>
    </div>

2行目のパラメータがエンコードされていて見づらいのでデコードすると下記のようになります。
ここでパラメータを指定していました。
	<p><label for="id_applicant_date">応募日:</label> <div class="input-group date">
    <input type="text" name="applicant_date" value="2019/10/31" class="form-control" required id="id_applicant_date" dp_config="{"id": "dp_2", "picker_type": "DATE", "linked_to": null, "options": {"showClose": true, "showClear": true, "showTodayButton": true, "locale": "ja", "format": "YYYY/MM/DD"}}"/>
    <div class="input-group-addon input-group-append" data-target="#datetimepicker1" data-toggle="datetimepickerv">
        <div class="input-group-text"><i class="glyphicon glyphicon-calendar"></i></div>
    </div>



追加項目( From側 )の出力されたHTML
<p><label for="id_date_field">追加日付項目:</label> <div class="input-group date">
    <input type="text" name="date_field" class="form-control" id="id_date_field" dp_config="{&quot;id&quot;: &quot;dp_1&quot;, &quot;picker_type&quot;: &quot;DATE&quot;, &quot;linked_to&quot;: null, &quot;options&quot;: {&quot;showClose&quot;: true, &quot;showClear&quot;: true, &quot;showTodayButton&quot;: true, &quot;locale&quot;: &quot;ja&quot;, &quot;viewMode&quot;: &quot;months&quot;, &quot;format&quot;: &quot;YYYY/MM&quot;}}"/>
    <div class="input-group-addon input-group-append" data-target="#datetimepicker1" data-toggle="datetimepickerv">
        <div class="input-group-text"><i class="glyphicon glyphicon-calendar"></i></div>
    </div>

こちらもデコードすると下記のようになります。ここでパラメータを指定していました。
<p><label for="id_date_field">追加日付項目:</label> <div class="input-group date">
    <input type="text" name="date_field" class="form-control" id="id_date_field" dp_config="{"id": "dp_1", "picker_type": "DATE", "linked_to": null, "options": {"showClose": true, "showClear": true, "showTodayButton": true, "locale": "ja", "viewMode": "months", "format": "YYYY/MM"}}"/>
    <div class="input-group-addon input-group-append" data-target="#datetimepicker1" data-toggle="datetimepickerv">
        <div class="input-group-text"><i class="glyphicon glyphicon-calendar"></i></div>
    </div>


良く見ると data-target で指定しているのが両者ともに"#datetimepicker1"を指定している。
他のDatePickerだと、JavaScriptで下記のような記載が必要なのだが、
Django-bootstrap-datepicker-plusでは不要のようです。
しかも同じ名前でもうまく動くというのが少々不思議ですが・・。
<script type="text/javascript">
 	$('#datetimepicker1').datepicker();
</script>


全ソースは、こちらに保存
https://github.com/MakotoPlus/PycharmProjects4

補足
DatePickerは、いろんな種類があるようです。
https://qiita.com/knt45/items/6d74f6785cd4547ae53b
自分は、 DateTimePicker と Bootstrap DatePicker が同じものだと勘違いしていました・・。
https://ascii.jp/elem/000/000/883/883101/
ちょっと認識間違えでハマりました・・。

Bootstrap DatePicker は、 Bootstrapで用意されているDatePicker であって、
DateTimePickerは、Jquery pluginを利用したカレンダのようである。
そして、Django用Datepckerライブラリが、django-bootstrap-datepicker-plusです。
こちら、datepickerと言いながら、利用時は、datetimepckerと「time」を付けるので
混乱する理由となった(イイワケ)

Bootstrap DatePicker
https://uxsolutions.github.io/bootstrap-datepicker/?markup=input&format=&weekStart=&startDate=&endDate=&startView=0&minViewMode=0&maxViewMode=4&todayBtn=false&clearBtn=false&language=en&orientation=auto&multidate=&multidateSeparator=&keyboardNavigation=on&forceParse=on#sandbox
DateTimePicker
https://xdsoft.net/jqplugins/datetimepicker/
Django-bootstrap-datepicker-plus
https://django-bootstrap-datepicker-plus.readthedocs.io/en/latest/Walkthrough.html

Django-bootstrap-datepicker-plusでの参考サイトは下記
https://pisuke-code.com/bootstrap-how-to-show-datepicker/
https://intellectual-curiosity.tokyo/2018/11/09/django%E3%81%A7datepicker%EF%BC%88%E3%82%AB%E3%83%AC%E3%83%B3%E3%83%80%E3%83%BC%E3%81%AB%E3%82%88%E3%82%8B%E6%97%A5%E6%99%82%E5%85%A5%E5%8A%9B%EF%BC%89%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B/






─都心の天然温泉─ 名古屋クラウンホテル

─都心の天然温泉─ 名古屋クラウンホテル

  • 場所: 愛知県名古屋市中区栄1-8-33
  • 特色: 天然温泉「三蔵温泉」あり。大小20室以上の会議室・宴会場あり。全室冷蔵庫・シャワートイレ完備。



nice!(0)  コメント(0) 
共通テーマ:パソコン・インターネット

1.11 ページング機能(DJango 2.2.7) [Django]

ひとりごと

Webサイトにはほぼ検索機能がありそこには必ずと言っていいほどある
ページング機能
ちょっとその前に、Get と Post について
今まで、セキュリティ観点で Get = ×, Post = 〇という考えでいました。
今回のページング機能は、Getを使っています。最初はPostじゃなきゃ
ダメなんじゃないかと考えていたのですが、
よくよく考えると、Linkタグ(href a) も Getなんですよね。
ログインやデータ登録時のFormタグでは、かならず Post にとして、
ページング時の内容に関してパスワードや個人情報が入っていないのであれば、Getでも良いと
自分の中では整理しました。
まあ、金融系等のセキリティに厳しいシステムでは許されないでしょうが。
(参考サイト)
https://www.ipa.go.jp/security/awareness/vendor/programmingv1/a01_04.html
https://qiita.com/kanataxa/items/522efb74421255f0e0a1

▼2020/02/08 追記
余談ですが、Postして Redirect してGetするパターンを
PRGパターンというそうです。
二重登録防止(予防?)的な考え。
ただ、PRGパターンは賛否両論のようで使うべきでないという方もおる。

https://eiry.bitbucket.io/tutorials/tutorial/models.html#id17
https://tech.nikkeibp.co.jp/it/article/COLUMN/20071112/286994/
https://blog.ohgaki.net/prga_a_ia_fa_sa_pa_bac_ai_eb

▲2020/02/08 追記

ここからページング機能について
では、ひとりごとも終わったのでタイトルの内容について書きたいと思います。
もう下記参考サイトそのままって感じです。
(参考サイト)
https://narito.ninja/blog/detail/89/
https://thinkami.hatenablog.com/entry/2016/03/17/003140

カスタムテンプレートタグ
Djangoの、カスタムテンプレートタグという機能を使います。
Javaで例えるならばTaglibですかね。


アプリケーションフォルダ直下にtemplatetagsディレクトリを作成してそこに保存します。
内容の説明はこちらのサイトを参照して下さい。(まるパクリですので)
(参考サイト)
https://thinkami.hatenablog.com/entry/2016/03/17/003140


テンプレートファイル
内容の説明はこちらのサイトを参照して下さい(まるパクリですので)
(参考サイト)
https://narito.ninja/blog/detail/89/


ビュークラス
ページング用のデータを取得する関数もまるパクリです。
(参考サイト)
https://narito.ninja/blog/detail/89/

上の関数を呼び出すindex処理は、少しだけ独自です。
ページング時に、GETパラメータ内に検索条件有無を確認し、
検索条件があるのならWhere句を生成し、DBデータを取得して
paginate_querysetを呼出します。

def index(request):

    log.info('index start')
    forms = SearchFormSet(request.GET or None)

    """
    GETパラメータのキー内容について。
    ページング処理のパラメータは、 'page'
    検索絞込み条件の'm_work_history','m_appl_route'の
    情報キーが存在した場合は、is_valid実施後にSQLのWhere句を生成する。
    """

    whereSql = ''
    
    #
    # パラメータに検索条件があるかチェックする
    # Request.GETからパラメータをリストに保存(lists)
    # タプルの中にタプルが格納されているので二重ループで、キー名の文字列が含まれているパラメータキーが存在するか
    # チェックし存在したらフラグをTrueに設定する。
    # 1つあったらそれで十分なのでループを抜ける
    lists = request.GET.lists()
    is_m_appl_route_key = False     #検索条件パラメータ存在有無フラグ
    is_m_work_history_key = False   #検索条件パラメータ存在有無フラグ
    for tupls in lists:
        for key in tupls:
            if 'm_work_history' in key:
                is_m_work_history_key = True
                break
            elif 'm_appl_route' in key:
                is_m_appl_route_key = True
                break
        #どちらかのフラグがTrueとなったら終わり
        if is_m_appl_route_key or is_m_appl_route_key:
            break

    #
    # 1. パラメータに検索条件があったらSQL文のWHERE句を生成する。
    # 2. 検索パラメータが正常に作成されていない場合は下記エラーとなるため、新規にFormを生成する。
    #   「マネージメントフォームのデータが見つからないか、改竄されています。」
    #    これは検索条件なしでページ遷移パラメータが存在している場合に発生する。
    #
    if is_m_work_history_key or is_m_appl_route_key:
        if forms.is_valid() == True:
            for form in forms:
                if form.cleaned_data.get('m_appl_route'):
                    #print( 'm_appl_route=' + str(type(form.cleaned_data.get('m_appl_route'))))
                    whereSql = ' WHERE M_Appl_Route.key_appl_route =\'' + str(form.cleaned_data.get('m_appl_route').key_appl_route) + '\' '
                
                if form.cleaned_data.get('m_work_history'):
                    if whereSql:
                        whereSql = whereSql + ' AND '
                    else:
                        whereSql = ' WHERE '
                    whereSql = whereSql + 'M_Work_History.key_history_kbn=\'' + str(form.cleaned_data.get('m_work_history').key_history_kbn) + '\''
            #print( 'WHERE=[' + whereSql + ']' )
        else:
            #ありえないけと念のため
            forms = SearchFormSet(None)
    else:
        forms = SearchFormSet(None)


   #
   # SQL文作ったり・・ロジック省略
   #
   #
    cursor = connection.cursor()
    cursor.execute(sSql)
    rows = cursor.fetchall()
    page_obj = paginate_queryset( request, rows, 30 )

    #print( '-------------------------------------------------------------------' )
    #print( forms )
    context = {
        'forms' : forms,
         'list' : page_obj.object_list,
        'page_obj' : page_obj,
    }
    #print( context );
    return render(request, 'applicantctl/index.html', context)
  











nice!(0)  コメント(0) 
共通テーマ:パソコン・インターネット
前の10件 | -

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。