{"action":"created","comment":{"url":"https://api.github.com/repos/wagtail/wagtail/pulls/comments/195841201","pull_request_review_id":129279712,"id":195841201,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDE5NTg0MTIwMQ==","diff_hunk":"@@ -0,0 +1,114 @@\n+.. _creating_migrating_pages:\n+\n+============================\n+Creating and migrating pages\n+============================\n+\n+When performing initial migrations of existing content to Wagtail or when doing Django data migrations of existing Wagtail content we have manipulate Wagtail :class:`~wagtail.core.models.Page` and :class:`~wagtail.core.models.PageRevision` objects programmatically. \n+\n+Creating new pages\n+==================\n+\n+Imagine we have a new ``BlogPage`` similar to the one below and we want to migrate existing blog content into our new Wagtail blog. \n+\n+.. code-block:: python\n+\n+    # models.py\n+\n+    from wagtail.core.models import Page\n+    from wagtail.core.fields import RichTextField\n+\n+    class BlogPage(Page):\n+\n+        body = RichTextField()\n+        # Other relevant blog fields\n+\n+We can create new blog posts like we would with any `Django model <https://docs.djangoproject.com/en/stable/intro/tutorial02/#playing-with-the-api>`_:\n+\n+.. code-block:: python\n+\n+    post = BlogPage()\n+    post.body = source_post['body']\n+    # Fill in other blog fields from the source post\n+ \n+    \n+If we need to backdate our new objects we can do so by setting the :class:`~wagtail.core.models.Page.first_published_at` and :class:`~wagtail.core.models.Page.last_published_at` date/time fields that all Wagtail pages have, but that are not exposed in the admin UI:\n+\n+.. code-block:: python\n+\n+    post.first_published_at = source_post['first_published_at']\n+    post.latest_revision_created_at = source_post['last_revision_created_at']\n+\n+\n+Before we can save the page, however, we need to save our changes to a new :class:`~wagtail.core.models.PageRevision` object. Revisions are representations of a page at a specific point in time. Every edit of a Wagtail page in the admin creates a new revision when it is saved. \n+\n+.. code-block:: python\n+    \n+    revision = post.save_revision()\n+    post.save()\n+\n+Once the revision and post are saved, the revision can be published.\n+\n+.. code-block:: python\n+\n+    revision.publish()\n+\n+Migrating pages and revisions\n+=============================\n+\n+When making changes to a Wagtail page's field schema we sometimes have to change the data for those fields on any pages that exist already in the database. See the  `Django data migrations documentation <https://docs.djangoproject.com/en/stable/topics/migrations/#data-migrations>`_ for a detailed description of how data migrations work. Data migrations are straight-forward for :class:`~wagtail.core.models.Page` obejcts themselves, but pages also have historical revisions and potentially draft revisions and each of this :class:`~wagtail.core.models.PageRevision` objects also needs to be migration.\n+\n+Modifying page revisions\n+------------------------\n+\n+:class:`~wagtail.core.models.PageRevision` objects store the :class:`~wagtail.core.models.Page` object at the time the revision was created as a JSON string in :class:`~wagtail.core.models.PageRevision.content_json`. Any modification of the revision has to load this JSON, modify it, and then dump it back to a string to store it. Given a particular ``revision`` object:\n+\n+.. code-block:: python\n+\n+    # Load the revision content JSON\n+    revision_content = json.loads(revision.content_json)\n+    # Migrate the revision content\n+    revision_content['name'] =  ' '.join(\n+        revision_content['first_name'], \n+        revision_content['last_name']\n+    )\n+    # Dump the revision back to JSON and store it\n+    revision.content_json = json.dumps(revision_content)\n+\n+Creating the data migration\n+---------------------------\n+\n+Knowing that we need to modify both the page objects and the page revision objects, we have to get both models from the `Django app registry <https://docs.djangoproject.com/en/2.0/ref/applications/>`_. From there we can loop over all the page objects and modify them as necessary, and then loop over each of the page objects' revision and modify them as described above.\n+\n+.. code-block:: python\n+","path":"docs/advanced_topics/migrating.rst","position":84,"original_position":84,"commit_id":"4aac35cd4c0d6f2c3d4fa813fe56a794ff03d647","original_commit_id":"4aac35cd4c0d6f2c3d4fa813fe56a794ff03d647","user":{"login":"chosak","id":654645,"node_id":"MDQ6VXNlcjY1NDY0NQ==","avatar_url":"https://avatars1.githubusercontent.com/u/654645?v=4","gravatar_id":"","url":"https://api.github.com/users/chosak","html_url":"https://github.com/chosak","followers_url":"https://api.github.com/users/chosak/followers","following_url":"https://api.github.com/users/chosak/following{/other_user}","gists_url":"https://api.github.com/users/chosak/gists{/gist_id}","starred_url":"https://api.github.com/users/chosak/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chosak/subscriptions","organizations_url":"https://api.github.com/users/chosak/orgs","repos_url":"https://api.github.com/users/chosak/repos","events_url":"https://api.github.com/users/chosak/events{/privacy}","received_events_url":"https://api.github.com/users/chosak/received_events","type":"User","site_admin":false},"body":"`import json` here?","created_at":"2018-06-15T19:29:38Z","updated_at":"2018-06-15T19:31:42Z","html_url":"https://github.com/wagtail/wagtail/pull/4621#discussion_r195841201","pull_request_url":"https://api.github.com/repos/wagtail/wagtail/pulls/4621","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/wagtail/wagtail/pulls/comments/195841201"},"html":{"href":"https://github.com/wagtail/wagtail/pull/4621#discussion_r195841201"},"pull_request":{"href":"https://api.github.com/repos/wagtail/wagtail/pulls/4621"}}},"pull_request":{"url":"https://api.github.com/repos/wagtail/wagtail/pulls/4621","id":195249772,"node_id":"MDExOlB1bGxSZXF1ZXN0MTk1MjQ5Nzcy","html_url":"https://github.com/wagtail/wagtail/pull/4621","diff_url":"https://github.com/wagtail/wagtail/pull/4621.diff","patch_url":"https://github.com/wagtail/wagtail/pull/4621.patch","issue_url":"https://api.github.com/repos/wagtail/wagtail/issues/4621","number":4621,"state":"open","locked":false,"title":"Add documentation for programmatic manipulation of pages","user":{"login":"willbarton","id":10562538,"node_id":"MDQ6VXNlcjEwNTYyNTM4","avatar_url":"https://avatars0.githubusercontent.com/u/10562538?v=4","gravatar_id":"","url":"https://api.github.com/users/willbarton","html_url":"https://github.com/willbarton","followers_url":"https://api.github.com/users/willbarton/followers","following_url":"https://api.github.com/users/willbarton/following{/other_user}","gists_url":"https://api.github.com/users/willbarton/gists{/gist_id}","starred_url":"https://api.github.com/users/willbarton/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/willbarton/subscriptions","organizations_url":"https://api.github.com/users/willbarton/orgs","repos_url":"https://api.github.com/users/willbarton/repos","events_url":"https://api.github.com/users/willbarton/events{/privacy}","received_events_url":"https://api.github.com/users/willbarton/received_events","type":"User","site_admin":false},"body":"This PR is an attempt to address some of @williln's documentation requests in #4613.\r\n\r\nIt adds a section to Advanced Topics called \"Creating and migrating pages\", and includes:\r\n\r\n- How to create and save a Page instance programmatically, with examples, including the necessity to save a revision with a small explanation of the relationship between pages and revisions\r\n- How to handle data migrations of existing pages and their revisions\r\n\r\nThe GitHub rendition is available at https://github.com/willbarton/wagtail/blob/add-programmatic-page-docs/docs/advanced_topics/migrating.rst","created_at":"2018-06-15T18:20:24Z","updated_at":"2018-06-15T19:31:42Z","closed_at":null,"merged_at":null,"merge_commit_sha":"9a3121bba5405f59c5a3ee8728e474401f46b600","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":966064146,"node_id":"MDU6TGFiZWw5NjYwNjQxNDY=","url":"https://api.github.com/repos/wagtail/wagtail/labels/sprint:Philly2018","name":"sprint:Philly2018","color":"5319e7","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/wagtail/wagtail/pulls/4621/commits","review_comments_url":"https://api.github.com/repos/wagtail/wagtail/pulls/4621/comments","review_comment_url":"https://api.github.com/repos/wagtail/wagtail/pulls/comments{/number}","comments_url":"https://api.github.com/repos/wagtail/wagtail/issues/4621/comments","statuses_url":"https://api.github.com/repos/wagtail/wagtail/statuses/4aac35cd4c0d6f2c3d4fa813fe56a794ff03d647","head":{"label":"willbarton:add-programmatic-page-docs","ref":"add-programmatic-page-docs","sha":"4aac35cd4c0d6f2c3d4fa813fe56a794ff03d647","user":{"login":"willbarton","id":10562538,"node_id":"MDQ6VXNlcjEwNTYyNTM4","avatar_url":"https://avatars0.githubusercontent.com/u/10562538?v=4","gravatar_id":"","url":"https://api.github.com/users/willbarton","html_url":"https://github.com/willbarton","followers_url":"https://api.github.com/users/willbarton/followers","following_url":"https://api.github.com/users/willbarton/following{/other_user}","gists_url":"https://api.github.com/users/willbarton/gists{/gist_id}","starred_url":"https://api.github.com/users/willbarton/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/willbarton/subscriptions","organizations_url":"https://api.github.com/users/willbarton/orgs","repos_url":"https://api.github.com/users/willbarton/repos","events_url":"https://api.github.com/users/willbarton/events{/privacy}","received_events_url":"https://api.github.com/users/willbarton/received_events","type":"User","site_admin":false},"repo":{"id":105017569,"node_id":"MDEwOlJlcG9zaXRvcnkxMDUwMTc1Njk=","name":"wagtail","full_name":"willbarton/wagtail","owner":{"login":"willbarton","id":10562538,"node_id":"MDQ6VXNlcjEwNTYyNTM4","avatar_url":"https://avatars0.githubusercontent.com/u/10562538?v=4","gravatar_id":"","url":"https://api.github.com/users/willbarton","html_url":"https://github.com/willbarton","followers_url":"https://api.github.com/users/willbarton/followers","following_url":"https://api.github.com/users/willbarton/following{/other_user}","gists_url":"https://api.github.com/users/willbarton/gists{/gist_id}","starred_url":"https://api.github.com/users/willbarton/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/willbarton/subscriptions","organizations_url":"https://api.github.com/users/willbarton/orgs","repos_url":"https://api.github.com/users/willbarton/repos","events_url":"https://api.github.com/users/willbarton/events{/privacy}","received_events_url":"https://api.github.com/users/willbarton/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/willbarton/wagtail","description":"A Django content management system focused on flexibility and user experience","fork":true,"url":"https://api.github.com/repos/willbarton/wagtail","forks_url":"https://api.github.com/repos/willbarton/wagtail/forks","keys_url":"https://api.github.com/repos/willbarton/wagtail/keys{/key_id}","collaborators_url":"https://api.github.com/repos/willbarton/wagtail/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/willbarton/wagtail/teams","hooks_url":"https://api.github.com/repos/willbarton/wagtail/hooks","issue_events_url":"https://api.github.com/repos/willbarton/wagtail/issues/events{/number}","events_url":"https://api.github.com/repos/willbarton/wagtail/events","assignees_url":"https://api.github.com/repos/willbarton/wagtail/assignees{/user}","branches_url":"https://api.github.com/repos/willbarton/wagtail/branches{/branch}","tags_url":"https://api.github.com/repos/willbarton/wagtail/tags","blobs_url":"https://api.github.com/repos/willbarton/wagtail/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/willbarton/wagtail/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/willbarton/wagtail/git/refs{/sha}","trees_url":"https://api.github.com/repos/willbarton/wagtail/git/trees{/sha}","statuses_url":"https://api.github.com/repos/willbarton/wagtail/statuses/{sha}","languages_url":"https://api.github.com/repos/willbarton/wagtail/languages","stargazers_url":"https://api.github.com/repos/willbarton/wagtail/stargazers","contributors_url":"https://api.github.com/repos/willbarton/wagtail/contributors","subscribers_url":"https://api.github.com/repos/willbarton/wagtail/subscribers","subscription_url":"https://api.github.com/repos/willbarton/wagtail/subscription","commits_url":"https://api.github.com/repos/willbarton/wagtail/commits{/sha}","git_commits_url":"https://api.github.com/repos/willbarton/wagtail/git/commits{/sha}","comments_url":"https://api.github.com/repos/willbarton/wagtail/comments{/number}","issue_comment_url":"https://api.github.com/repos/willbarton/wagtail/issues/comments{/number}","contents_url":"https://api.github.com/repos/willbarton/wagtail/contents/{+path}","compare_url":"https://api.github.com/repos/willbarton/wagtail/compare/{base}...{head}","merges_url":"https://api.github.com/repos/willbarton/wagtail/merges","archive_url":"https://api.github.com/repos/willbarton/wagtail/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/willbarton/wagtail/downloads","issues_url":"https://api.github.com/repos/willbarton/wagtail/issues{/number}","pulls_url":"https://api.github.com/repos/willbarton/wagtail/pulls{/number}","milestones_url":"https://api.github.com/repos/willbarton/wagtail/milestones{/number}","notifications_url":"https://api.github.com/repos/willbarton/wagtail/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/willbarton/wagtail/labels{/name}","releases_url":"https://api.github.com/repos/willbarton/wagtail/releases{/id}","deployments_url":"https://api.github.com/repos/willbarton/wagtail/deployments","created_at":"2017-09-27T12:49:34Z","updated_at":"2018-06-15T13:58:58Z","pushed_at":"2018-06-15T18:34:08Z","git_url":"git://github.com/willbarton/wagtail.git","ssh_url":"git@github.com:willbarton/wagtail.git","clone_url":"https://github.com/willbarton/wagtail.git","svn_url":"https://github.com/willbarton/wagtail","homepage":"wagtail.io","size":70484,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"wagtail:master","ref":"master","sha":"8270a117f5c1e587132cda7099b0cd4be31dd8dc","user":{"login":"wagtail","id":23708009,"node_id":"MDEyOk9yZ2FuaXphdGlvbjIzNzA4MDA5","avatar_url":"https://avatars3.githubusercontent.com/u/23708009?v=4","gravatar_id":"","url":"https://api.github.com/users/wagtail","html_url":"https://github.com/wagtail","followers_url":"https://api.github.com/users/wagtail/followers","following_url":"https://api.github.com/users/wagtail/following{/other_user}","gists_url":"https://api.github.com/users/wagtail/gists{/gist_id}","starred_url":"https://api.github.com/users/wagtail/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wagtail/subscriptions","organizations_url":"https://api.github.com/users/wagtail/orgs","repos_url":"https://api.github.com/users/wagtail/repos","events_url":"https://api.github.com/users/wagtail/events{/privacy}","received_events_url":"https://api.github.com/users/wagtail/received_events","type":"Organization","site_admin":false},"repo":{"id":16479108,"node_id":"MDEwOlJlcG9zaXRvcnkxNjQ3OTEwOA==","name":"wagtail","full_name":"wagtail/wagtail","owner":{"login":"wagtail","id":23708009,"node_id":"MDEyOk9yZ2FuaXphdGlvbjIzNzA4MDA5","avatar_url":"https://avatars3.githubusercontent.com/u/23708009?v=4","gravatar_id":"","url":"https://api.github.com/users/wagtail","html_url":"https://github.com/wagtail","followers_url":"https://api.github.com/users/wagtail/followers","following_url":"https://api.github.com/users/wagtail/following{/other_user}","gists_url":"https://api.github.com/users/wagtail/gists{/gist_id}","starred_url":"https://api.github.com/users/wagtail/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wagtail/subscriptions","organizations_url":"https://api.github.com/users/wagtail/orgs","repos_url":"https://api.github.com/users/wagtail/repos","events_url":"https://api.github.com/users/wagtail/events{/privacy}","received_events_url":"https://api.github.com/users/wagtail/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/wagtail/wagtail","description":"A Django content management system focused on flexibility and user experience","fork":false,"url":"https://api.github.com/repos/wagtail/wagtail","forks_url":"https://api.github.com/repos/wagtail/wagtail/forks","keys_url":"https://api.github.com/repos/wagtail/wagtail/keys{/key_id}","collaborators_url":"https://api.github.com/repos/wagtail/wagtail/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/wagtail/wagtail/teams","hooks_url":"https://api.github.com/repos/wagtail/wagtail/hooks","issue_events_url":"https://api.github.com/repos/wagtail/wagtail/issues/events{/number}","events_url":"https://api.github.com/repos/wagtail/wagtail/events","assignees_url":"https://api.github.com/repos/wagtail/wagtail/assignees{/user}","branches_url":"https://api.github.com/repos/wagtail/wagtail/branches{/branch}","tags_url":"https://api.github.com/repos/wagtail/wagtail/tags","blobs_url":"https://api.github.com/repos/wagtail/wagtail/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/wagtail/wagtail/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/wagtail/wagtail/git/refs{/sha}","trees_url":"https://api.github.com/repos/wagtail/wagtail/git/trees{/sha}","statuses_url":"https://api.github.com/repos/wagtail/wagtail/statuses/{sha}","languages_url":"https://api.github.com/repos/wagtail/wagtail/languages","stargazers_url":"https://api.github.com/repos/wagtail/wagtail/stargazers","contributors_url":"https://api.github.com/repos/wagtail/wagtail/contributors","subscribers_url":"https://api.github.com/repos/wagtail/wagtail/subscribers","subscription_url":"https://api.github.com/repos/wagtail/wagtail/subscription","commits_url":"https://api.github.com/repos/wagtail/wagtail/commits{/sha}","git_commits_url":"https://api.github.com/repos/wagtail/wagtail/git/commits{/sha}","comments_url":"https://api.github.com/repos/wagtail/wagtail/comments{/number}","issue_comment_url":"https://api.github.com/repos/wagtail/wagtail/issues/comments{/number}","contents_url":"https://api.github.com/repos/wagtail/wagtail/contents/{+path}","compare_url":"https://api.github.com/repos/wagtail/wagtail/compare/{base}...{head}","merges_url":"https://api.github.com/repos/wagtail/wagtail/merges","archive_url":"https://api.github.com/repos/wagtail/wagtail/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/wagtail/wagtail/downloads","issues_url":"https://api.github.com/repos/wagtail/wagtail/issues{/number}","pulls_url":"https://api.github.com/repos/wagtail/wagtail/pulls{/number}","milestones_url":"https://api.github.com/repos/wagtail/wagtail/milestones{/number}","notifications_url":"https://api.github.com/repos/wagtail/wagtail/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/wagtail/wagtail/labels{/name}","releases_url":"https://api.github.com/repos/wagtail/wagtail/releases{/id}","deployments_url":"https://api.github.com/repos/wagtail/wagtail/deployments","created_at":"2014-02-03T12:41:59Z","updated_at":"2018-06-15T15:44:50Z","pushed_at":"2018-06-15T18:57:51Z","git_url":"git://github.com/wagtail/wagtail.git","ssh_url":"git@github.com:wagtail/wagtail.git","clone_url":"https://github.com/wagtail/wagtail.git","svn_url":"https://github.com/wagtail/wagtail","homepage":"https://wagtail.io","size":70723,"stargazers_count":5593,"watchers_count":5593,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1253,"mirror_url":null,"archived":false,"open_issues_count":629,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":1253,"open_issues":629,"watchers":5593,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/wagtail/wagtail/pulls/4621"},"html":{"href":"https://github.com/wagtail/wagtail/pull/4621"},"issue":{"href":"https://api.github.com/repos/wagtail/wagtail/issues/4621"},"comments":{"href":"https://api.github.com/repos/wagtail/wagtail/issues/4621/comments"},"review_comments":{"href":"https://api.github.com/repos/wagtail/wagtail/pulls/4621/comments"},"review_comment":{"href":"https://api.github.com/repos/wagtail/wagtail/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/wagtail/wagtail/pulls/4621/commits"},"statuses":{"href":"https://api.github.com/repos/wagtail/wagtail/statuses/4aac35cd4c0d6f2c3d4fa813fe56a794ff03d647"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"chosak"}}