質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

1回答

402閲覧

[django]makemigrationsを実行したときのAppConfig継承クラス:import_modelsメソッド()の挙動が理解できません。

koki3

総合スコア12

Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2018/11/08 07:26

編集2018/11/12 14:31

質問の詳細

下記のコードのようにAppConfig継承クラスにimport_modelsメソッドを定義して実行すると
下記結果の通りmodelクラスをインポートしただけで突然OrderedDictの値が格納されました。

AppConfig継承クラス(polls/apps.py)

from django.apps import AppConfig class PollsConfig(AppConfig): name = 'polls' def import_models(self): print('+'*20,'before_import','+'*20,self.apps.all_models[self.label]) from polls.core.models.users import Users print('+'*20,'after_import','+'*20,self.apps.all_models[self.label]) self.models = self.apps.all_models[self.label]

結果

prompt:~/environment/xxxxxx/dev $ python manage.py makemigrations polls ++++++++++++++++++++ before_import ++++++++++++++++++++ OrderedDict() ++++++++++++++++++++ after_import ++++++++++++++++++++ OrderedDict([('users_groups', <class 'polls.core.models.users.Users_groups'>), ('users_user_permissions', <class 'polls.core.models.users.Users_user_permissions'>), ('users', <class 'polls.core.models.users.Users'>)])

直前の呼び元のall_models(下記のモジュール Appsクラス:)も確認しましたが
特別な処理をしているようには見受けられませんでした。
django/apps/registry.py

一応この記述でもmigrationは通りましたが不適切な実装であるか非常に不安です。
makemigrationsを実行して偶然上手くいったというのが正直なところです。
どなたか次の2点について教えて頂けませんでしょうか。

1、なぜ、modelクラスをインポートしただけでOrderedDictの値が設定されるのか
2、この実装は適切なのか

以上宜しくお願いします。

仕様

python3.6
django 1.11.5
※modelクラスの配置は必ずpolls.core.models配下に格納する

補足

モジュールの配置図

├── dev │   ├── __init__.py │   ├── __pycache__ │   ├── settings.py ① │   ├── urls.py ② │   └── wsgi.py ├── manage.py └── polls ├── admin.py ├── apps.py ③ ├── __init__.py ④ ├── migrations │   └── __pycache__ ├── __pycache__ ├── urls.py ⑤ └── core     ├── __init__.py ⑥      ├── models      │   ├── __init__.py ⑦     │   ├── __pycache__     │   └── users.py ⑧     └── __pycache__      └── views

①dev/settings.py(抜粋)

INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'polls.apps.PollsConfig', ] AUTH_USER_MODEL = 'polls.Users'

②dev/urls.py(抜粋)

urlpatterns = [ url(r'^polls/', include('polls.urls')), url(r'^admin/', admin.site.urls), ]

③polls/apps.py

from django.apps import AppConfig class PollsConfig(AppConfig): name = 'polls' def import_models(self): print('+'*20,'before_import','+'*20,self.apps.all_models[self.label]) from polls.core.models.users import Users print('+'*20,'after_import','+'*20,self.apps.all_models[self.label]) self.models = self.apps.all_models[self.label]

④polls/init.py

from polls import apps

⑤polls/urls.py(抜粋)

urlpatterns = [ ]

⑥polls/core/init.py

from polls.core import models

⑦polls/core/models/init.py

from polls.core.models import users

⑧polls/core/models/users.py

from django.db import models from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, UserManager from django.contrib.auth.validators import UnicodeUsernameValidator from django.utils import timezone from django.utils.translation import gettext_lazy as _ from django.core.mail import send_mail import uuid as uuid_lib from django.utils import timezone class Users(AbstractBaseUser, PermissionsMixin): uuid = models.UUIDField(default=uuid_lib.uuid4, primary_key=True, editable=False) username_validator = UnicodeUsernameValidator() username = models.CharField( _('username'), max_length=20, unique=True, help_text=_( 'Required. 20 characters or fewer. Letters, digits and @/./+/-/_ only.'), validators=[username_validator], error_messages={ 'unique': _("A user with that username already exists."), }, ) email = models.EmailField(_('email address'), max_length=20, unique=True, blank=True) is_superuser = models.BooleanField(verbose_name='superuser', default=True) is_staff = models.BooleanField(verbose_name='管理サイトアクセス権限', default=False) is_active = models.BooleanField(verbose_name='有効/無効', default=True) created_at = models.DateTimeField(default=timezone.now) registrant = models.CharField( max_length=20, verbose_name='登録者', default='system') USERNAME_FIELD = 'email' EMAIL_FIELD = 'email' REQUIRED_FIELDS = [ 'username'] objects = UserManager() class Meta: permissions = ( ("can_view", "Can see content"), ("can_add_service", "can_add_service"), ("can_edit_service", "can_edit_service"), ("can_delete_service", "can_delete_service"), ) verbose_name = _('users') app_label = 'polls' def get_full_name(self): # The user is identified by their email address return self.email def get_short_name(self): # The user is identified by their email address return self.email

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gh640

2018/11/09 13:50

質問からで失礼します。前提としての背景を教えていただきたいのですが、「 ※modelクラスの配置は必ずpolls.core.models配下に格納する 」というのはなぜそうされているのですか? https://teratail.com/questions/151450 の質問の「ディレクトリ構成」で書かれているように、通常使用する `models.py` は使わない形なのでしょうか?(「この実装は適切なのか」の議論に関わってくると思いお聞きしてます)
koki3

2018/11/10 00:46

ご質問ありがとうございます。 "アプリケーション名.core.models"配下にクラスを配置する設計上の理由を 聞いたのですが、正直言ってよく分かりませんでした。(お恥ずかしい話です) 強いて言えば、チーム内での慣習を踏襲したということです。 ただ既存他サービスで"アプリケーション名.core.models"配下に モデルクラスを配置してサービスを実運用しています。 また、実際に使用するモデルクラスは多いのでmodels.pyを分割する必要があります。
gh640

2018/11/10 02:41 編集

ご回答くださりありがとうございます。なるほど、そういうことなのですね。ということですと、問題なく運用できている既存他サービスの方でも( `import_models()` の書き方含めて)この形になっている、ということでしょうか。それとも、 `import_models()` の部分は今回のプロジェクトで初めて試されている形ですか?もし問題が無い実績があるのであれば、ポイント 2 については「既存他サービスを踏襲した」で(チーム内的には)一応の結論にはなるのかなと思いますがいかがでしょう。
gh640

2018/11/10 01:52

`アプリケーション名.core.models` にする点については、設計を担当した方が Django の標準的な models.py の形から外れることを理解していてなおかつメリット・デメリットを適切に把握した上でそうされたのであれば、特に問題なさそうな気がします。その方が十分な知識をお持ちであれば各 app の AppConfig の適切な書き方もわかっていらっしゃるものと思います。
gh640

2018/11/10 01:52

ポイント 1 についてはこの欄ではコメントしづらいので回答欄でコメントさせていただきますね。
koki3

2018/11/10 09:15

まずはポイント1についてのご回答ありがとうございました。 ポイント2の件ですがAppConfig自体を他サービスでは使用していないので AppConfig:import_modelsへの実装自体は今回が初めてです。 ですがポイント1でご回答頂いた挙動について理解できたので 実装自体には問題ないと結論付けても良いかとも考えてます。 ただmodels.pyの形から外れた場合のメリット・デメリットは分かっておりません。 宜しければその点も教えて頂けると大変助かります。
guest

回答1

0

ベストアンサー

まずはポイント 1 の方だけ回答させてください。

1、なぜ、modelクラスをインポートしただけでOrderedDictの値が設定されるのか

端的には、 django.db.models.Model のメタクラス ModelBase__new__() メソッドでそのような処理が行われているから、のようです。

python

1from polls.core.models.users import Users

の行が実行されたときの処理をおっかけていくと、おおよそ次のとおりになります。

text

1from polls.core.models.users import Users 2→ django.db.models.base.ModelBase.__new__() 3→ django.apps.apps.register_model() 4→ import されメモリに読み込まれた Model が self.apps.all_models の OrderedDict に追加される

この結果、「結果」としてお出しになっている挙動のとおりになるようです。

ポイントだけですが、( Django 1.11.5 の)該当コードへのリンクも以下に載せておきますので、ご参考になさってください。

投稿2018/11/10 02:09

gh640

総合スコア1407

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

koki3

2018/11/10 08:53

分かりやすい説明で有難うございます。 早速該当コードも確認してみます。
koki3

2018/11/12 03:23

gh640さん。コード確認してみました。遅くなりましてすみません。 おっしゃる通りでした。挙動が確認できましたので解決済みにします。 大変有難うございました。
gh640

2018/11/12 13:38

コメントお戻しくださりありがとうございます。このあたりは本当によくできていますが、処理があちこちに行くので、正確な処理の流れを把握するのが大変ですよね。。ともあれご解決とのことでよかったです。
gh640

2018/11/12 13:42

とても些末なところなのですが、タイトルの mekemigration は makemigrations ではないかと思います。いつかまったく同じ問題に直面する方のために makemigrations に変えられるとどこかの誰かが助かるかもしれません。お手間でなければぜひ :)
koki3

2018/11/12 13:51

あ、すみません。ご指摘ありがとうございます。 早速修正しました。
gh640

2018/11/12 14:18

早々にありがとうございます。枝葉のポイントでこちらこそスミマセン。それと、事前にきちんとお伝えすればよかったのですが、先頭の `meke` も `make` でした。
koki3

2018/11/12 14:34

何度もすみません(汗)。見落としてました。今後とも宜しくお願い致します。
gh640

2018/11/12 14:39

私の方こそすみません。遅い時間にご対応くださりありがとうございました。どうぞ宜しくお願いいたします :D
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問