SSブログ

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) 
共通テーマ:パソコン・インターネット

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) 
共通テーマ:パソコン・インターネット

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) 
共通テーマ:パソコン・インターネット

1.9 Django +IIS連携(DJango 2.2.7) [Django]

仕事の関係上、サーバがWindowsになるそうで
必然的にWebサーバはIISになりそうなのでDjango+IISの連携を
自宅マシンでやってみた。

1.IISインストール(Windwos10)
  参考サイト http://www.wannko.net/windows10/iis/iis_00.html


2.IISでPythonが実行出来るように環境設定
  参考サイト https://support.microsoft.com/ja-jp/help/276494/using-python-scripts-with-iis

  自分は、ASP上でうまく動作しなかったのですが、Djangoを動かすには
  必須ではないので無視しました。
  拡張子.py のファイル関連をちゃんとPythonにしないと動作しないので注意です。

3.Django + IIS + Windwos10 の設定
  wfastcgiというのをインストールするらしいです。

  参考サイト1. https://thinkami.hatenablog.com/entry/2015/10/26/070118
  参考サイト2.https://docs.microsoft.com/ja-jp/previous-versions/azure/virtual-machines/windows/classic/python-django-web-app?toc=%2Fazure%2Fvirtual-machines%2Fwindows%2Fclassic%2Ftoc.json

  #補足
  ・Windwos7でしたがWindwos10でも問題なく動作しました。
  ・virtualenvでインストールとなっているが、venvで大丈夫。







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

1.8 データベースへ反映(DJango 2.2.7) [Django]

少し時間が空きましたが・・。

データベースの作成作業です。流れ的には下記となります。
これは、今後テーブルの内容を変更したい場合も同じ操作となります。

1. データベース反映のスクリプトファイル作成
2. スクリプトファイル実行してデータベースへ反映


1. データベース反映のスクリプトファイル作成方法
 1.6で作成したモデルクラスをDJangoが読取って自動で
 スクリプトファイルを作ってくれます。(DDLの作成)
 Create文を作らなくていいんです。素敵ですね。

コマンド:
python manage.py makemigrations applicantctl

WS000027.JPG


DDLの内容確認したい場合は下記コマンドを実行すると確認出来ます(やらなくてもOK)
コマンド:
python manage.py sqlmigrate applicantctl 0001

コンソールに出ているメッセージを見ればわかるけど、
実際にファイルも出力されているので見ることも出来ます。
(こちらはDDLにはなってないけど)
こんな感じ

# Generated by Django 2.2.7 on 2019-11-24 10:52

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='M_Appl_Route',
            fields=[
                ('key_appl_route', models.AutoField(primary_key=True, serialize=False)),
                ('appl_route_text', models.CharField(max_length=200, verbose_name='応募経路')),
                ('u_user', models.CharField(max_length=100, verbose_name='更新者')),
                ('u_date', models.DateTimeField(auto_now=True, verbose_name='更新日時')),
            ],
        ),
        migrations.CreateModel(
            name='M_Department',
            fields=[
                ('key_index', models.AutoField(primary_key=True, serialize=False)),
                ('headquarters_text', models.CharField(max_length=100, verbose_name='本部')),
                ('u_user', models.CharField(max_length=100, verbose_name='更新者')),
                ('u_date', models.DateTimeField(auto_now=True, verbose_name='更新日時')),
            ],
        ),
        migrations.CreateModel(
            name='M_Judgment',
            fields=[
                ('key_judgment', models.AutoField(primary_key=True, serialize=False)),
                ('judgment_text', models.CharField(max_length=100, verbose_name='判定内容')),
                ('u_user', models.CharField(max_length=100, verbose_name='更新者')),
                ('u_date', models.DateTimeField(auto_now=True, verbose_name='更新日時')),
            ],
        ),
        migrations.CreateModel(
            name='M_Work_History',
            fields=[
                ('key_history_kbn', models.AutoField(primary_key=True, serialize=False)),
                ('work_history_kbn', models.CharField(max_length=100, verbose_name='経歴区分')),
                ('u_user', models.CharField(max_length=100, verbose_name='更新者')),
                ('u_date', models.DateTimeField(auto_now=True, verbose_name='更新日時')),
            ],
        ),
        migrations.CreateModel(
            name='T_Applicant_info',
            fields=[
                ('key_applicant', models.AutoField(primary_key=True, serialize=False)),
                ('applicant_date', models.DateField(verbose_name='応募日')),
                ('applicant_no', models.CharField(max_length=20, verbose_name='応募者No')),
                ('applicant_name_text', models.CharField(max_length=100, verbose_name='応募者名')),
                ('u_user', models.CharField(max_length=100, verbose_name='更新者')),
                ('u_date', models.DateTimeField(auto_now=True, verbose_name='更新日時')),
                ('key_appl_route', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='applicantctl.M_Appl_Route')),
                ('key_history_kbn', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='applicantctl.M_Work_History', verbose_name='経歴区分')),
            ],
        ),
        migrations.CreateModel(
            name='T_Judgment',
            fields=[
                ('key_judgment', models.AutoField(primary_key=True, serialize=False)),
                ('judgment_index', models.IntegerField(choices=[(1, '1'), (2, '2'), (3, '3')], verbose_name='優先順位')),
                ('u_user', models.CharField(max_length=100, verbose_name='更新者')),
                ('u_date', models.DateTimeField(auto_now=True, verbose_name='更新日時')),
                ('judgment', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='applicantctl.M_Judgment', verbose_name='面談実施判定')),
                ('key_applicant', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='applicantctl.T_Applicant_info', verbose_name='応募者情報Key')),
                ('key_department', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='applicantctl.M_Department', verbose_name='本部')),
            ],
        ),
    ]




2. スクリプトファイル実行してデータベースへ反映  
 スクリプトが出来たので実際にデータベースへ反映するコマンドを実行します。
 コマンド:
 python manage.py migrate

 ※コンソールにはDJango用のDB構築をしていなかったため
 そちらも出力されています。

WS000026.JPG


 プロジェクトフォルダ直下にdb.sqlite3というファイルがデータベース(SQLite)のファイルです。
 中身を除くと下記のような感じでテーブルが作成されています。 (フリーソフト:PupSQLite使用)
 テーブル名は、「アプリケーション名」+ '_' + 「テーブル名」という感じで作成されるようです。

WS000028.JPG






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

1.7 管理サイト構築(DJango 2.2.7) [Django]

Djangoではデフォルトで用意されている管理サイトがあり、
ログインするためにスーパーユーザを作成します。

python manage.py createsuperuser

ユーザ名
メールアドレス
パスワードを入力すればOK

WS000023.JPG





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

1.6 モデルクラスの作成(DJango 2.2.7) [Django]

前回説明を飛ばしたモデルの説明をしたいと思います。

◆クラス名は下記にしました。
判定マスタ:M_Judgmentクラス
応募経路マスタ:M_Appl_Routeクラス
業務経歴マスタ:M_Work_Historyクラス
部マスタ:M_Departmentクラス
応募者情報:T_Applicant_infoクラス
判定テーブル:T_Judgmentクラス

ER図
WS000013.JPG



◆各列情報を定義するフォーマットは下記となります。
 列名 = データ型(データ型に対するパラメータ指定)
 データ型、AutoField, ForeignKey, CharField, DateFieldなど様々なものがあります。
詳細は他のサイトでお願いします。


◆全部説明するのもあれなので判定テーブルだけ説明します。
 判定テーブル(T_Judgmentクラス)
 1.判定テーブルキー列(key_judgment)
  プライマリキーで自動で値が割振られる列となります。
 2.部INDEX列(key_department)
  部マスタ(M_Department)をカスケードしているのでパラメータにクラス名を指定している。
  on_delete=models.PROTECTは、参照しているデータが存在していたら削除できない指定。
 3.応募者情報キー列(key_applicant)
  応募者情報(T_Applicant_info)をカスケードしているのでパラメータにクラス名を指定している。
  on_delete=models.CASCADEは、参照しているデータが存在していたら一緒に削除される指定。
 4.優先順番列(judgment_index)
  三つの部を登録する仕様なので、必然的に1~3までとなるので
  choicesパラメータをしている。(12行目)
  こうする事により、画面側では、1~3のコンボボックスをDJangoが表示してくれます。
 5.判定列(judgment)
  判定マスタ(M_Judgment)をカスケードしているのでパラメータにクラス名を指定している。

#判定テーブル
class T_Judgment(models.Model):
    #
    CONST_JUDGMENT_INDEX = (( 1, '1'), (2, '2'), (3, '3'))
    #判定テーブルキー
    key_judgment = models.AutoField(primary_key=True)
    #部INDEX
    key_department = models.ForeignKey(M_Department, null=False, on_delete=models.PROTECT, verbose_name='本部')
    #応募者情報キー
    key_applicant = models.ForeignKey(T_Applicant_info, null=False, on_delete=models.CASCADE, verbose_name='応募者情報Key')
    #優先順番
    judgment_index = models.IntegerField(null=False, verbose_name='優先順位', choices=CONST_JUDGMENT_INDEX)
    #判定
    judgment = models.ForeignKey(M_Judgment, null=True, on_delete=models.PROTECT, verbose_name='面談実施判定')
    #更新者
    u_user = models.CharField(max_length=100, verbose_name='更新者')
    #更新日
    u_date = models.DateTimeField(verbose_name='更新日時',auto_now=True)
    def __str__(self):
        return str(self.key_judgment)





全てのmodelは下記を参照。
https://github.com/MakotoPlus/PycharmProjects3/blob/master/Recruitment/applicantctl/models.py






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

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