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

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

ただいまの
回答率

90.50%

  • Python 3.x

    9862questions

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

  • Django

    1624questions

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

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

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 180

koki3

score 2

 質問の詳細

下記のコードのように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
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • gh640

    2018/11/10 10:52

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

    キャンセル

  • gh640

    2018/11/10 10:52

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

    キャンセル

  • koki3

    2018/11/10 18:15

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

    キャンセル

回答 1

checkベストアンサー

+1

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

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

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

from polls.core.models.users import Users

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

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

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

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

  • django.db.models.Model: https://github.com/django/django/blob/1.11.5/django/db/models/base.py#L470
  • django.db.models.base.ModelBase: https://github.com/django/django/blob/1.11.5/django/db/models/base.py#L79
  • https://github.com/django/django/blob/1.11.5/django/db/models/base.py#L325
  • https://github.com/django/django/blob/1.11.5/django/apps/registry.py#L207
  • https://github.com/django/django/blob/1.11.5/django/apps/registry.py#L225

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/10 17:53

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

    キャンセル

  • 2018/11/12 12:23

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

    キャンセル

  • 2018/11/12 22:38

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

    キャンセル

  • 2018/11/12 22:42

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

    キャンセル

  • 2018/11/12 22:51

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

    キャンセル

  • 2018/11/12 23:18

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

    キャンセル

  • 2018/11/12 23:34

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

    キャンセル

  • 2018/11/12 23:39

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

    キャンセル

同じタグがついた質問を見る

  • Python 3.x

    9862questions

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

  • Django

    1624questions

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

  • トップ
  • Djangoに関する質問
  • [django]makemigrationsを実行したときのAppConfig継承クラス:import_modelsメソッド()の挙動が理解できません。