自動化無しに生活無し

WEBとかAIとかLinux関係をひたすら書く備忘録系ブログ

【Django】models.pyとforms.pyからviews.pyを自動的に作る【コマンド1発で生成】

thumbnail

以前の自動生成ツール

【Django】models.pyからforms.py及びadmin.pyを自動的に作る【コマンド1発で生成】

この続き。今回はviews.pyを自動的に作る。

いつも同じようなものをimportして、ビュークラスを作り、DB操作とレンダリングしているだけなので。

内容は以前のコードをviews.py仕様にしただけで、特別なものは使っていない。

ソースコード

import glob, re, sys

## 引数の指定がある場合: 特定のアプリのmodels.pyに対して実行する。
## 引数の指定が無い場合: プロジェクトの全アプリのmodels.pyに対して実行する。

if len(sys.argv) > 1:
    models_paths    = glob.glob(f"./{sys.argv[1]}/models.py")
    forms_paths     = glob.glob(f"./{sys.argv[1]}/forms.py")
else:
    models_paths    = glob.glob("./*/models.py")
    forms_paths     = glob.glob("./*/forms.py")


views_paths     = [ models_path.replace("models", "views") for models_path in models_paths ]


# すべてのmodels.pyを順次読み込み
for models_path,forms_path,views_path in zip(models_paths, forms_paths, views_paths):

    # アプリ名を取得しておく。
    app_name        = models_path.replace("./","").replace("/models.py","")

    #====views.pyの冒頭===========================
    views_code      = [ "# == This code was created by https://noauto-nolife.com/post/django-auto-create-views/ == #\n"]

    # 検索(クエリビルダ)、ページネーション、リダイレクト、LoginRequiredMixin をimportしておく。
    views_code.append("from django.shortcuts import render,redirect")
    views_code.append("from django.views import View")
    views_code.append("from django.contrib.auth.mixins import LoginRequiredMixin")
    views_code.append("from django.db.models import Q")
    views_code.append("from django.core.paginator import Paginator")

    views_code.append("")

    # モデルクラスをimportする。
    with open(models_path, "r") as mf:

        models_code     = mf.read()
        model_classes   = re.findall(r'class (\w+)\(models\.Model\):', models_code)

        import_models   = ""
        for model_class in model_classes:
            import_models += f"{model_class},"

        # 末尾の , を削除
        import_models   = import_models.rstrip(",")

        # forms.pyのコードを作る。
        views_code.append( f"from .models import {import_models}" )


    # フォームクラスをimportする
    with open(forms_path, "r") as mf:

        forms_code     = mf.read()
        form_classes   = re.findall(r'class (\w+)\(forms\.ModelForm\):', forms_code)

        import_forms   = ""
        for form_class in form_classes:
            import_forms += f"{form_class},"

        # 末尾の , を削除
        import_forms   = import_forms.rstrip(",")

        # forms.pyのコードを作る。
        views_code.append( f"from .forms import {import_forms}\n" )


    #====views.pyの冒頭===========================

    # 本体は class [モデルクラス名]View(View): でgetメソッド、postメソッドを作っておく。
    # レンダリングするテンプレートは [アプリ名][モデルクラス名(小文字)].html
    # アプリのトップページのView:  IndexViewは作っておく。

    views_code.append(f'class IndexView(View):')
    views_code.append(f'')
    views_code.append(f'    def get(self, request, *args, **kwargs):')
    views_code.append(f'')
    views_code.append(f'        context = {{}}')
    views_code.append(f'        return render(request, "{app_name}/index.html", context)')
    views_code.append(f'')
    views_code.append(f'    def post(self, request, *args, **kwargs):')
    views_code.append(f'')
    views_code.append(f'        return redirect("{app_name}:index")')
    views_code.append(f'')
    views_code.append(f'index   = IndexView.as_view()')
    views_code.append(f'')


    # 作成(Create)、読み込み(Read)、編集(Update)、削除(DELETE)、
    for model_class,form_class in zip(model_classes, form_classes):

        model_class_lower   = model_class.lower()

        # ===読み書き====

        views_code.append(f'class {model_class}View(View):')
        views_code.append(f'    def get(self, request, *args, **kwargs):')
        views_code.append(f'')
        views_code.append(f'        context = {{}}')
        views_code.append(f'        context["{model_class_lower}"] = {model_class}.objects.all()')
        views_code.append(f'')
        views_code.append(f'        return render(request, "{app_name}/{model_class_lower}.html", context)')
        views_code.append(f'')
        views_code.append(f'    def post(self, request, *args, **kwargs):')
        views_code.append(f'')
        views_code.append(f'        form = {form_class}(request.POST)')
        views_code.append(f'')
        views_code.append(f'        if form.is_valid():')
        views_code.append(f'            print("保存")')
        views_code.append(f'            form.save()')
        views_code.append(f'        else:')
        views_code.append(f'            print(form.errors)')
        views_code.append(f'')
        views_code.append(f'        return redirect("{app_name}:{model_class_lower}")')
        views_code.append(f'')
        views_code.append(f'{model_class_lower}   = {model_class}View.as_view()')
        views_code.append(f'')
        views_code.append(f'')

        # ===編集====

        views_code.append(f'class {model_class}UpdateView(View):')
        views_code.append(f'    def get(self, request, pk, *args, **kwargs):')
        views_code.append(f'')
        views_code.append(f'        context = {{}}')
        views_code.append(f'        context["{model_class_lower}"] = {model_class}.objects.filter(id=pk).first()')
        views_code.append(f'')
        views_code.append(f'        return render(request, "{app_name}/{model_class_lower}_update.html", context)')
        views_code.append(f'')
        views_code.append(f'    def post(self, request, pk, *args, **kwargs):')
        views_code.append(f'')
        views_code.append(f'        {model_class_lower} = {model_class}.objects.filter(id=pk).first()')
        views_code.append(f'        form = {form_class}(request.POST, instance={model_class_lower})')
        views_code.append(f'')
        views_code.append(f'        if form.is_valid():')
        views_code.append(f'            print("編集")')
        views_code.append(f'            form.save()')
        views_code.append(f'        else:')
        views_code.append(f'            print(form.errors)')
        views_code.append(f'')
        views_code.append(f'        return redirect("{app_name}:{model_class_lower}_update")')
        views_code.append(f'')
        views_code.append(f'{model_class_lower}_update   = {model_class}UpdateView.as_view()')
        views_code.append(f'')

        # ===削除====

        views_code.append(f'class {model_class}DeleteView(View):')
        views_code.append(f'    def get(self, request, pk, *args, **kwargs):')
        views_code.append(f'')
        views_code.append(f'        context = {{}}')
        views_code.append(f'        context["{model_class_lower}"] = {model_class}.objects.filter(id=pk).first()')
        views_code.append(f'')
        views_code.append(f'        return render(request, "{app_name}/{model_class_lower}_delete.html", context)')
        views_code.append(f'')
        views_code.append(f'    def post(self, request, pk, *args, **kwargs):')
        views_code.append(f'')
        views_code.append(f'        {model_class_lower} = {model_class}.objects.filter(id=pk).first()')
        views_code.append(f'        {model_class_lower}.delete()')
        views_code.append(f'')
        views_code.append(f'        return redirect("{app_name}:{model_class_lower}_delete")')
        views_code.append(f'')
        views_code.append(f'{model_class_lower}_delete   = {model_class}DeleteView.as_view()')
        views_code.append(f'')


    """
    for code in views_code:
        print(code)
    """

    #====対応するforms.pyへ保存===================

    # 書き込みするコード
    with open(views_path, "a") as ff:
        for code in views_code:
            ff.write(code + "\n")

    #====対応するforms.pyへ保存===================

冒頭でモデルクラスとフォームクラスを全てimportしており、

クエリビルダ、ページネーション、LoginRequiredMixin等必要なものもimportした。

閲覧と新規作成、編集と削除をそれぞれ用意した。

もっとも、個人的には編集と削除にgetメソッドは不要だと考えているので、コメントアウトしておく。

注意

  • 読み込み時のコンテキストのキー名が単数形になっている
  • 検索とクエリビルダはとりあえずimportはしたが、使ってはいない
  • リダイレクト先のURL名の指定、及びpkの指定までは考慮していない。
  • アプリ名の単語の途中で_がない(スネークケースになっていない) ← 生成後に置換で対処する。

結論

状況によってビューの書き方は大きく異なるので、この自動生成ツールの効果は未知数である。

あとは実際に使ってみて、問題箇所を調べ、改良を繰り返す。

次は、urls.pyの自動生成を作りたい。

その次はテンプレートの自動生成だ。簡単なウェブアプリならモデル作った後、コマンド1発で作れるようにするのが理想。

スポンサーリンク

シェアボタン

Twitter LINEで送る Facebook はてなブログ