4
4

More than 5 years have passed since last update.

Django 1.7:リリースノート: Fieldサブクラス新メソッド関連

Posted at

deconstruct()

deconstruct(self):

To help power both schema migrations 
and to enable easier addition of composite keys in future releases of Django, 
the Field API now has a new required method: deconstruct().

4アイテムのタプルを返す:

This method takes no arguments, and returns a tuple of four items:

name: The field’s attribute name on its parent model, 
      or None if it is not part of a model

path: A dotted, Python path to the class of this field, 
      including the class name.

args: Positional arguments, as a list

kwargs: Keyword arguments, as a dict

These four values allow any field to be serialized into a file, 
as well as allowing the field to be copied safely, both essential parts of these new features.

Todoモデルのフィールドのdeconstruct():


    >>> from todos.models import Todo
    >>> import json
    >>> print json.dumps(dict([ (f.name, f.deconstruct()) for f in Todo._meta.fields ]),indent=2)
    {
      "title": [
        "title", 
        "django.db.models.CharField", 
        [], 
        {
          "max_length": 50
        }
      ], 
      ...... (省略)
      "user": [
        "user", 
        "django.db.models.ForeignKey", 
        [], 
        {
          "to": "auth.User"
        }
      ], 
      "id": [
        "id", 
        "django.db.models.AutoField", 
        [], 
        {
          "verbose_name": "ID", 
          "serialize": false, 
          "auto_created": true, 
          "primary_key": true
        }
      ]
    }

deconsturct()でモデル定義のシリアライズをカスタマイズできるので、マイグレーションとかで便利:

This change should not affect you unless you write custom Field subclasses; 

if you do, 
you may need to reimplement the deconstruct() method 
if your subclass changes the method signature of __init__ in any way. 

If your field just inherits from a built-in Django field and doesn’t override __init__, 
no changes are necessary.

試しにしビルトインフィールドのdeconstuct()をオーバーライドして親クラスのメソッド読んだり
フィールドを削除してみたりとかやれば:

If you do need to override deconstruct(), 
a good place to start is the built-in Django fields 
(django/db/models/fields/__init__.py) as several fields
, including DecimalField and DateField, 
override it and show how to call the method 
on the superclass and simply add or remove extra arguments.

要はフィールドクラスへの引数は全部シリアライズされないといけない。
詳細は、 リファレンスを: 

This also means that all arguments to fields must themselves be serializable; 
to see what we consider serializable, and to find out how to make your own classes serializable, 
read the migration serialization documentation.

MigrationAutodetector

  • MigrationAutodetectorchanges()が、モデルの差分を判断します
  • モデルの差分は前後のモデルのフィールドの deconstruct() を取得して、比較して作成する
  • generate_ ではじまるメソッドが差分の生成
  • 差分をまとめたものがMigration
  • makemigrations

新規モデルを追加:


    class Log(models.Model):
        todo = models.ForeignKey(Todo)
        created_at = models.DateTimeField(auto_now_add=True, )

MigrationLoader:


    >>> from django.db.migrations.loader import MigrationLoader
    >>> loader = MigrationLoader(None, ignore_no_migrations=True)

マイグレーション前のプロジェクトステータス(かな?):


    >>> project_state = loader.project_state()
    >>> type(project_state)
    <class 'django.db.migrations.state.ProjectState'>

    >>> project_state.models
    {
     ('todos', u'todo'): <ModelState: 'todos.Todo'>, 
     ('admin', u'logentry'): <ModelState: 'admin.LogEntry'>,
     ('contenttypes', u'contenttype'): <ModelState: 'contenttypes.ContentType'>, 
     ('sessions', u'session'): <ModelState: 'sessions.Session'>, 
     ('auth', u'permission'): <ModelState: 'auth.Permission'>, 
     ('auth', u'user'): <ModelState: 'auth.User'>, 
     ('auth', u'group'): <ModelState: 'auth.Group'>, 
    }

マイグレーションしようとしているプロジェクトのステータス(かな?):


    >>> from django.apps import apps
    >>> from django.db.migrations.state import ProjectState

    >>> ProjectState.from_apps(apps)
    <django.db.migrations.state.ProjectState object at 0x3161290>
    >>> states = _

    >>> states.models
    {
     (u'todos', u'log'): <ModelState: 'todos.Log'>, 
     (u'todos', u'todo'): <ModelState: 'todos.Todo'>, 
     ('admin', u'logentry'): <ModelState: 'admin.LogEntry'>, 
     ('contenttypes', u'contenttype'): <ModelState: 'contenttypes.ContentType'>, 
     ('sessions', u'session'): <ModelState: 'sessions.Session'>, 
     ('auth', u'permission'): <ModelState: 'auth.Permission'>, 
     ('auth', u'user'): <ModelState: 'auth.User'>, 
     ('auth', u'group'): <ModelState: 'auth.Group'>
    }

マイグレーションの対話(かな?):


    >>> InteractiveMigrationQuestioner(specified_apps=app_labels, dry_run=True)
    <django.db.migrations.questioner.InteractiveMigrationQuestioner object at 0x3172ad0>
    >>> q = _

ディテクタ生成:


    >>> MigrationAutodetector(project_state, states, q)
    <django.db.migrations.autodetector.MigrationAutodetector object at 0x3172b10>
    >>> detector = _

generate_ メソッド:


    >>> import json
    >>> print json.dumps([i for i in dir(detector) if i.startswith('generate_')], indent=2)
    [
      "generate_added_fields", 
      "generate_altered_fields", 
      "generate_altered_index_together", 
      "generate_altered_options", 
      "generate_altered_order_with_respect_to", 
      "generate_altered_unique_together", 
      "generate_created_models", 
      "generate_created_proxies", 
      "generate_created_unmanaged", 
      "generate_deleted_models", 
      "generate_deleted_proxies", 
      "generate_deleted_unmanaged", 
      "generate_removed_fields", 
      "generate_renamed_fields", 
      "generate_renamed_models"
    ]
4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4