Skip to content

Switch Wagtail page‐type while keeping existing ID and Slug

John Carroll edited this page Sep 26, 2024 · 55 revisions

Example scenario: The content team wants to convert a page originally created using a CustomPage template to a ResourcePage while maintaining the original page ID and slug to preserve Wagtail's existing internal links and references to the page.

This can be done for any page type, the example below uses the most common request of converting a CustomPage to a ResourcePage.

  1. A content team member creates a draft page in Wagtail on production using the ResourcePage template (or whatever the new page type is). We will call this the "dummy page."

    • Important: Publish the draft dummy page temporarily and create a published copy of it in Wagtail. This copy will ultimately be the final page.
      • Make sure to check the publish checkbox when creating your copy.
      • Make a note of your copied page's ID.
      • Don't make any edits to your copied page in Wagtail as this will not work if the page has a revision history.
    • Unpublish the draft dummy page once you have created your published copy.
  2. Log into CloudFoudry(CF) and access PSQL on production (TWO TERMINAL TABS)

    
    FIRST TERMINAL TAB:
    
    # Log into CloudFoudry:
      cf login --sso
    
    # Target the environment you're exporting from
      cf target [-o <org name>] -s <space name>
    
    # Set app GUID
      cms_app_guid=$(cf app --guid cms)
    
    # Establish the tunnel for the SSH session
      tunnel=$(cf curl /v2/apps/$cms_app_guid/env \
      | jq -r '[.system_env_json.VCAP_SERVICES."aws-rds"[0].credentials 
      | .host, .port] 
      | join(":")')
    
    # Check what PG services you are running locally and stop them
      brew services list
      brew services stop postgresql@[#]
    
    # Start the tunnel. Leave this window open.
      cf ssh -N -L 5432:$tunnel cms
    
    
    
    SECOND TERMINAL TAB:
    
    
    # In the second terminal, set credentials and database name to connect
      cms_app_guid=$(cf app --guid cms)
    
      creds=$(cf curl /v2/apps/$cms_app_guid/env \
      | jq -r '[.system_env_json.VCAP_SERVICES."aws-rds"[0].credentials 
      | .username, .password] 
      | join(":")')
    
      dbname=$(cf curl /v2/apps/$cms_app_guid/env \
      | jq -r '.system_env_json.VCAP_SERVICES."aws-rds"[0].credentials 
      | .name')
    
    # Connect to the database with PSQL
      psql postgres://$creds@localhost:5432/$dbname
    
    

Once your connected to the database with PSQL:

  • End all commands with a semi-colon
  • You can use ABORT; to get out of a sequence
  • Make sure to COMMIT; each change
  • The IDs used in this example:
    • Original CustomPage ID (the ID we want to keep): 1234
    • Your copy of dummy ResourcePage ID: 5678
    • content_type_ids: CustomPage:30, ResourcePage:49 (Find others with select * from django_content_type;)
  1. Delete the record from the public.home_custompage table for the original CustomPage ID:

    => BEGIN;
    BEGIN
    => DELETE FROM public.home_custompage WHERE page_ptr_id=1234;
    DELETE 1
    => COMMIT;
    COMMIT
    
  2. Change that page's content_type_id in wagtailcore_page table to a ResourcePage(49). Tip: You can see all content_type_ids by querying: select * from django_content_type;

    => BEGIN;
    BEGIN
    => update public.wagtailcore_page set content_type_id=49 where id=1234;
    UPDATE 1
    => COMMIT;
    COMMIT
    
    
  3. Change the page_ptr_id of your published copy to the original page_ptr_id in the public.home_resourcepage table :

    => BEGIN;
    BEGIN
    => update public.home_resourcepage set page_ptr_id=1234 where page_ptr_id=5678;
    UPDATE 1
    => COMMIT;
    COMMIT
    
    
  4. The page has now been converted. View the page in a browser and make sure you can edit and save it in Wagtail*. Make sure you are testing the original ID, not the dummy or the dummy copy. Use an icognito window or a hard-refresh to avoid confusion of Wagtail caching. If all looks good, go on to step 7.

    *If you get an error (500 Server Error) when trying to edit in Wagtail, you can try to fix this by going into the Django Python Shell (WIKI) and run: page.save_revision().publish()

  5. A little cleanup: Your copy of the dummy page(5678) now remains orphaned in wagtailcore_page table. Attempt to delete that record with the delete command below. If you get any errors due to foreign key constraints as shown in the example code below, you can remove them (step 8) and then repeat the delete command, hopefully without errors. Likely foreign key constraints errors are for wagtailcore_pagesubscription or wagtailcore_revision.

     => BEGIN;
     BEGIN;
     => DELETE FROM public.wagtailcore_page WHERE id=5678;
     DELETE 1
     => COMMIT;
     COMMIT
    
    ERROR:  update or delete on table "wagtailcore_page" violates foreign key constraint "wagtailcore_pagesubs_page_id_a085e7a6_fk_wagtailco" on table "wagtailcore_pagesubscription"
     DETAIL:  Key (id)=(5678) is still referenced from table "wagtailcore_pagesubscription".
    
    
  6. Delete foreign key constraints and then repeat step 7 to delete the record without error. Tip: for wagtailcore_pagesubscription table the page is referenced by page_id, for wagtailcore_revision table it is referenced by object_id(in quotes)

     => BEGIN;
     BEGIN;
     => DELETE FROM public.wagtailcore_pagesubscription WHERE page_id=5678;
     DELETE 1
     => COMMIT;
     COMMIT
    
    
    
  7. Ask content team to verify that the new page looks correct and ask them to delete their dummy page in Wagtail.

Optional maintenance steps:

  • Search.gov indexing of temporarily published pages: When you temporarily publish a copy of a page for this process, it automatically becomes part of the /sitemap-wagtail.xml. Search.gov indexes our sitemap about every two hours. So If that page happens to still be published when search.gov kicks off indexing, it will be added to the index and will show up in site-search results. While un-publishing the page removes it from sitemap, it does not remove it from the index. It takes about a month for un-published pages to drop off the index. Content editors often test the searchability of pages after making changes, and may report that there is an unwanted search-result for one of the temporary pages. If you do not want to wait a month for it to drop off, you can email search.gov to have it removed immediately (WIKI).

  • Wagtail admin search duplicates: The page converted in the process in this WIKI may be listed twice in search results when searching in the Wagtail admin (It does NOT show up twice in the admin explorer listing, just admin search results). While this is a harmless side effect of this process, sometimes this is bothersome to content editors. You can solve this by removing the duplicate entry that has the old content_type in the wagtailsearch_indexentry table. Important: Make sure you are deleting the one with the old content_type. (An alternate option that might solve this problem is: ./manage.py update_index, but that has not been tested. If you test it and it works, please update this Wiki.)

    DELETE FROM public.wagtailsearch_indexentry WHERE object_id='1234' AND content_type_id=30;