mirror of
https://github.com/Hopiu/wagtail-modeltranslation.git
synced 2026-05-12 01:03:12 +00:00
Move Page translation fields to Page table (breaking changes!)
- Bumps version to 0.8.0-alpha - Introduces 'makemigrations_translation' command to avoid generating wagtailcore_page migrations - Adds 'sync_page_translation_fields' which mimicks 'sync_translation_fields' but restricts it to Page model - This change introduces breaking changes and has no migration script to move from 0.6 to 0.8 - Updates tests
This commit is contained in:
parent
1a656b98da
commit
333c392dc8
8 changed files with 127 additions and 83 deletions
|
|
@ -54,8 +54,10 @@ def runtests():
|
|||
'wagtail.contrib.wagtailapi',
|
||||
|
||||
'wagtail_modeltranslation',
|
||||
|
||||
),
|
||||
# remove wagtailcore from serialization as translation columns have not been created at this point
|
||||
# (which causes OperationalError: no such column)
|
||||
TEST_NON_SERIALIZED_APPS=['wagtail.wagtailcore'],
|
||||
ROOT_URLCONF=None, # tests override urlconf, but it still needs to be defined
|
||||
LANGUAGES=(
|
||||
('en', 'English'),
|
||||
|
|
|
|||
2
setup.py
2
setup.py
|
|
@ -3,7 +3,7 @@ from distutils.core import setup
|
|||
|
||||
setup(
|
||||
name='wagtail-modeltranslation',
|
||||
version='0.6.0rc2',
|
||||
version='0.8.0-alpha',
|
||||
description='Translates Wagtail CMS models using a registration approach.',
|
||||
long_description=(
|
||||
'The modeltranslation application can be used to translate dynamic '
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
from django.core.management.commands.makemigrations import Command as MakeMigrationsCommand
|
||||
from django.db.migrations.autodetector import MigrationAutodetector
|
||||
|
||||
|
||||
# decorate MigrationAutodetector.changes so we can silently remove wagtailcore changes
|
||||
def changes_decorator(func):
|
||||
def wrapper(self, graph, trim_to_apps=None, convert_apps=None, migration_name=None):
|
||||
changes = func(self, graph, trim_to_apps, convert_apps, migration_name)
|
||||
if 'wagtailcore' in changes:
|
||||
del changes['wagtailcore']
|
||||
return changes
|
||||
return wrapper
|
||||
|
||||
MigrationAutodetector.changes = changes_decorator(MigrationAutodetector.changes)
|
||||
|
||||
|
||||
class Command(MakeMigrationsCommand):
|
||||
help = "Creates new migration(s) for apps except wagtailcore."
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
from modeltranslation.management.commands.sync_translation_fields import Command as SyncTranslationsFieldsCommand
|
||||
from modeltranslation.translator import translator
|
||||
from wagtail.wagtailcore.models import Page
|
||||
|
||||
|
||||
old_get_registered_models = translator.get_registered_models
|
||||
|
||||
# Monkey patching, only return a model if it's Page
|
||||
def get_page_model(self, abstract=True):
|
||||
models = old_get_registered_models(abstract)
|
||||
return [x for x in models if x is Page]
|
||||
|
||||
|
||||
class Command(SyncTranslationsFieldsCommand):
|
||||
help = ("Detect new translatable fields or new available languages and"
|
||||
" sync Wagtail's Page database structure. Does not remove "
|
||||
" columns of removed languages or undeclared fields.")
|
||||
|
||||
def handle(self, *args, **options):
|
||||
translator.get_registered_models = get_page_model.__get__(translator)
|
||||
|
||||
try:
|
||||
super(Command, self).handle(*args, **options)
|
||||
|
||||
finally:
|
||||
translator.get_registered_models = old_get_registered_models
|
||||
|
|
@ -36,7 +36,6 @@ COMPOSED_PANEL_CLASSES = [MultiFieldPanel, FieldRowPanel] + CUSTOM_COMPOSED_PANE
|
|||
|
||||
class WagtailTranslator(object):
|
||||
_patched_models = []
|
||||
_page_fields_tables = []
|
||||
|
||||
def __init__(self, model):
|
||||
# Check if this class was already patched
|
||||
|
|
@ -52,10 +51,6 @@ class WagtailTranslator(object):
|
|||
|
||||
WagtailTranslator._patched_models.append(model)
|
||||
|
||||
# compile all tables that are holding page translated fields (title_xx, slug_xx, url_path_xx)
|
||||
options = translator.get_options_for_model(model)
|
||||
if 'url_path' in options.local_fields.keys() and model._meta.db_table is not 'wagtailcore_page':
|
||||
WagtailTranslator._page_fields_tables.append(model._meta.db_table)
|
||||
|
||||
def _patch_page_models(self, model):
|
||||
# PANEL PATCHING
|
||||
|
|
@ -425,40 +420,32 @@ def _patch_clean(model):
|
|||
def _localized_update_descendant_url_paths(self, old_url_path, new_url_path, language):
|
||||
localized_url_path = build_localized_fieldname('url_path', language)
|
||||
|
||||
for db_table in WagtailTranslator._page_fields_tables:
|
||||
cursor = connection.cursor()
|
||||
if connection.vendor == 'sqlite':
|
||||
update_statement = """
|
||||
UPDATE {db_table}
|
||||
SET {localized_url_path} = %s || substr({localized_url_path}, %s)
|
||||
WHERE EXISTS (SELECT * FROM wagtailcore_page AS p
|
||||
WHERE p.id = {db_table}.page_ptr_id AND p.path LIKE %s)
|
||||
AND page_ptr_id <> %s
|
||||
""".format(db_table=db_table, localized_url_path=localized_url_path)
|
||||
elif connection.vendor == 'mysql':
|
||||
update_statement = """
|
||||
UPDATE {db_table} t
|
||||
JOIN wagtailcore_page p ON p.id = t.page_ptr_id
|
||||
SET {localized_url_path}= CONCAT(%s, substring({localized_url_path}, %s))
|
||||
WHERE p.path LIKE %s AND t.page_ptr_id <> %s
|
||||
""".format(db_table=db_table, localized_url_path=localized_url_path)
|
||||
elif connection.vendor in ('mssql', 'microsoft'):
|
||||
update_statement = """
|
||||
UPDATE t
|
||||
SET {localized_url_path}= CONCAT(%s, (SUBSTRING({localized_url_path}, 0, %s)))
|
||||
FROM {db_table} t
|
||||
JOIN wagtailcore_page p
|
||||
ON p.id = t.page_ptr_id
|
||||
WHERE p.path LIKE %s AND t.page_ptr_id <> %s
|
||||
""".format(db_table=db_table, localized_url_path=localized_url_path)
|
||||
else:
|
||||
update_statement = """
|
||||
UPDATE {db_table} as t
|
||||
SET {localized_url_path} = %s || substring({localized_url_path} from %s)
|
||||
FROM wagtailcore_page AS p
|
||||
WHERE p.id = t.page_ptr_id AND p.path LIKE %s AND t.page_ptr_id <> %s
|
||||
""".format(db_table=db_table, localized_url_path=localized_url_path)
|
||||
cursor.execute(update_statement, [new_url_path, len(old_url_path) + 1, self.path + '%', self.page_ptr_id])
|
||||
cursor = connection.cursor()
|
||||
if connection.vendor == 'sqlite':
|
||||
update_statement = """
|
||||
UPDATE wagtailcore_page
|
||||
SET {localized_url_path} = %s || substr({localized_url_path}, %s)
|
||||
WHERE path LIKE %s AND id <> %s
|
||||
""".format(localized_url_path=localized_url_path)
|
||||
elif connection.vendor == 'mysql':
|
||||
update_statement = """
|
||||
UPDATE wagtailcore_page
|
||||
SET {localized_url_path}= CONCAT(%s, substring({localized_url_path}, %s))
|
||||
WHERE path LIKE %s AND id <> %s
|
||||
""".format(localized_url_path=localized_url_path)
|
||||
elif connection.vendor in ('mssql', 'microsoft'):
|
||||
update_statement = """
|
||||
UPDATE wagtailcore_page
|
||||
SET {localized_url_path}= CONCAT(%s, (SUBSTRING({localized_url_path}, 0, %s)))
|
||||
WHERE path LIKE %s AND id <> %s
|
||||
""".format(localized_url_path=localized_url_path)
|
||||
else:
|
||||
update_statement = """
|
||||
UPDATE wagtailcore_page
|
||||
SET {localized_url_path} = %s || substring({localized_url_path} from %s)
|
||||
WHERE path LIKE %s AND id <> %s
|
||||
""".format(localized_url_path=localized_url_path)
|
||||
cursor.execute(update_statement, [new_url_path, len(old_url_path) + 1, self.path + '%', self.id])
|
||||
|
||||
|
||||
class LocalizedSaveDescriptor(object):
|
||||
|
|
|
|||
|
|
@ -51,9 +51,20 @@ class WagtailModeltranslationTransactionTestBase(TransactionTestCase):
|
|||
imp.reload(translator)
|
||||
|
||||
# reload the translation module to register the Page model
|
||||
# and also edit_handlers so any patches made to Page are reapplied
|
||||
from wagtail_modeltranslation import translation as wag_translation, translator as wag_translator
|
||||
from wagtail.wagtailadmin import edit_handlers
|
||||
import sys
|
||||
del cls.cache.all_models['wagtailcore']
|
||||
sys.modules.pop('wagtail_modeltranslation.translation.pagetr', None)
|
||||
sys.modules.pop('wagtail.wagtailcore.models', None)
|
||||
imp.reload(wag_translation)
|
||||
imp.reload(wag_translator)
|
||||
imp.reload(edit_handlers) # so Page can be repatched by edit_handlers
|
||||
wagtailcore_args = []
|
||||
if django.VERSION < (1, 11):
|
||||
wagtailcore_args = [cls.cache.all_models['wagtailcore']]
|
||||
cls.cache.get_app_config('wagtailcore').import_models(*wagtailcore_args)
|
||||
|
||||
# Reload the patching class to update the imported translator
|
||||
# in order to include the newly registered models
|
||||
|
|
@ -64,10 +75,12 @@ class WagtailModeltranslationTransactionTestBase(TransactionTestCase):
|
|||
# have translation fields, but for languages previously defined. We want
|
||||
# to be sure that 'de' and 'en' are available)
|
||||
del cls.cache.all_models['tests']
|
||||
import sys
|
||||
sys.modules.pop('wagtail_modeltranslation.tests.models', None)
|
||||
sys.modules.pop('wagtail_modeltranslation.tests.translation', None)
|
||||
cls.cache.get_app_config('tests').import_models(cls.cache.all_models['tests'])
|
||||
tests_args = []
|
||||
if django.VERSION < (1, 11):
|
||||
tests_args = [cls.cache.all_models['tests']]
|
||||
cls.cache.get_app_config('tests').import_models(*tests_args)
|
||||
|
||||
# 4. Autodiscover
|
||||
from modeltranslation.models import handle_translation_registrations
|
||||
|
|
@ -75,14 +88,17 @@ class WagtailModeltranslationTransactionTestBase(TransactionTestCase):
|
|||
|
||||
# 5. makemigrations
|
||||
from django.db import connections, DEFAULT_DB_ALIAS
|
||||
call_command('makemigrations', verbosity=2, interactive=False,
|
||||
call_command('makemigrations_translation', verbosity=2, interactive=False,
|
||||
database=connections[DEFAULT_DB_ALIAS].alias)
|
||||
|
||||
# 6. Syncdb
|
||||
call_command('migrate', verbosity=0, migrate=False, interactive=False, run_syncdb=True,
|
||||
database=connections[DEFAULT_DB_ALIAS].alias, load_initial_data=False)
|
||||
|
||||
# 7. patch wagtail models
|
||||
# 7. Make sure Page translation fields are created
|
||||
call_command('sync_page_translation_fields', interactive=False, verbosity=0, database=connections[DEFAULT_DB_ALIAS].alias)
|
||||
|
||||
# 8. patch wagtail models
|
||||
from wagtail_modeltranslation.patch_wagtailadmin import patch_wagtail_models
|
||||
patch_wagtail_models()
|
||||
|
||||
|
|
@ -90,7 +106,7 @@ class WagtailModeltranslationTransactionTestBase(TransactionTestCase):
|
|||
# tests app has been added into INSTALLED_APPS and loaded
|
||||
# (that's why this is not imported in normal import section)
|
||||
global models, translation
|
||||
from wagtail_modeltranslation.tests import models, translation
|
||||
from wagtail_modeltranslation.tests import models, translation # NOQA
|
||||
|
||||
def setUp(self):
|
||||
self._old_language = get_language()
|
||||
|
|
@ -355,7 +371,7 @@ class WagtailModeltranslationTest(WagtailModeltranslationTestBase):
|
|||
trans_real.activate('en')
|
||||
|
||||
# fetches the correct Page using slug using non-default language
|
||||
page = Page.objects.filter(slug='test-slug-de').first()
|
||||
page = Page.objects.rewrite(False).filter(slug='test-slug-de').first()
|
||||
self.assertEqual(page.specific, root, 'The wrong page was retrieved from DB.')
|
||||
|
||||
# save the page 2 in the non-default language
|
||||
|
|
@ -369,13 +385,13 @@ class WagtailModeltranslationTest(WagtailModeltranslationTestBase):
|
|||
self.assertEqual(root2.slug_en, 'test-slug2-en', 'slug_en has the wrong value.')
|
||||
|
||||
# fetches the correct Page using slug using non-default language
|
||||
page = Page.objects.filter(slug='test-slug2-de').first()
|
||||
page = Page.objects.rewrite(False).filter(slug='test-slug2-de').first()
|
||||
self.assertEqual(page.specific, root2, 'The wrong page was retrieved from DB.')
|
||||
|
||||
trans_real.activate('de')
|
||||
|
||||
# fetches the correct Page using slug using default language
|
||||
page = Page.objects.filter(slug='test-slug2-de').first()
|
||||
page = Page.objects.rewrite(False).filter(slug='test-slug2-de').first()
|
||||
self.assertEqual(page.specific, root2, 'The wrong page was retrieved from DB.')
|
||||
|
||||
|
||||
|
|
@ -497,26 +513,10 @@ class WagtailModeltranslationTest(WagtailModeltranslationTestBase):
|
|||
self.assertEqual(child.url_path_en, '/child_en/')
|
||||
|
||||
# We should retrieve grandchild with the below command:
|
||||
# grandchild_new = models.TestSlugPage1.objects.get(id=grandchild.id)
|
||||
# but it's exhibiting strange behaviour during tests. See:
|
||||
# https://github.com/infoportugal/wagtail-modeltranslation/issues/103#issuecomment-352006610
|
||||
grandchild_new = models.TestSlugPage1._default_manager.raw("""
|
||||
SELECT page_ptr_id, url_path_en, url_path_de FROM {}
|
||||
WHERE page_ptr_id=%s LIMIT 1
|
||||
""".format(models.TestSlugPage1._meta.db_table), [grandchild.page_ptr_id])[0]
|
||||
grandchild_new = models.TestSlugPage1.objects.get(id=grandchild.id)
|
||||
self.assertEqual(grandchild_new.url_path_en, '/child_en/grandchild1_en/')
|
||||
self.assertEqual(grandchild_new.url_path_de, '/child/grandchild1/')
|
||||
|
||||
|
||||
def test_page_fields_tables(self):
|
||||
from wagtail_modeltranslation.patch_wagtailadmin import WagtailTranslator
|
||||
|
||||
self.assertIn(models.TestSlugPage1, WagtailTranslator._patched_models)
|
||||
self.assertIn('tests_testslugpage1', WagtailTranslator._page_fields_tables)
|
||||
self.assertIn(models.TestSlugPage1Subclass, WagtailTranslator._patched_models)
|
||||
self.assertNotIn('tests_testslugpage1subclass', WagtailTranslator._page_fields_tables)
|
||||
self.assertNotIn('wagtailcore_page', WagtailTranslator._page_fields_tables)
|
||||
|
||||
def test_fetch_translation_records(self):
|
||||
"""
|
||||
Assert that saved translation fields are retrieved correctly
|
||||
|
|
|
|||
|
|
@ -4,38 +4,40 @@ from modeltranslation.translator import translator, register, TranslationOptions
|
|||
from wagtail_modeltranslation.tests.models import TestRootPage, TestSlugPage1, TestSlugPage2, PatchTestPage, \
|
||||
PatchTestSnippet, FieldPanelPage, ImageChooserPanelPage, FieldRowPanelPage, MultiFieldPanelPage, InlinePanelPage, \
|
||||
FieldPanelSnippet, ImageChooserPanelSnippet, FieldRowPanelSnippet, MultiFieldPanelSnippet, PageInlineModel, \
|
||||
BaseInlineModel, StreamFieldPanelPage, StreamFieldPanelSnippet, SnippetInlineModel, InlinePanelSnippet, TestSlugPage1Subclass
|
||||
from wagtail_modeltranslation.translator import WagtailTranslationOptions
|
||||
BaseInlineModel, StreamFieldPanelPage, StreamFieldPanelSnippet, SnippetInlineModel, InlinePanelSnippet, \
|
||||
TestSlugPage1Subclass
|
||||
|
||||
from wagtail.wagtailcore.models import Page
|
||||
|
||||
|
||||
# Wagtail Models
|
||||
|
||||
@register(TestRootPage)
|
||||
class TestRootPagePageTranslationOptions(WagtailTranslationOptions):
|
||||
class TestRootPagePageTranslationOptions(TranslationOptions):
|
||||
fields = ()
|
||||
|
||||
|
||||
@register(TestSlugPage1)
|
||||
class TestSlugPage1TranslationOptions(WagtailTranslationOptions):
|
||||
class TestSlugPage1TranslationOptions(TranslationOptions):
|
||||
fields = ()
|
||||
|
||||
|
||||
@register(TestSlugPage2)
|
||||
class TestSlugPage2TranslationOptions(WagtailTranslationOptions):
|
||||
class TestSlugPage2TranslationOptions(TranslationOptions):
|
||||
fields = ()
|
||||
|
||||
|
||||
@register(TestSlugPage1Subclass)
|
||||
class TestSlugPage1SubclassTranslationOptions(WagtailTranslationOptions):
|
||||
class TestSlugPage1SubclassTranslationOptions(TranslationOptions):
|
||||
pass
|
||||
|
||||
|
||||
@register(PatchTestPage)
|
||||
class PatchTestPageTranslationOptions(WagtailTranslationOptions):
|
||||
class PatchTestPageTranslationOptions(TranslationOptions):
|
||||
fields = ('description',)
|
||||
|
||||
|
||||
class PatchTestSnippetTranslationOptions(WagtailTranslationOptions):
|
||||
class PatchTestSnippetTranslationOptions(TranslationOptions):
|
||||
fields = ('name',)
|
||||
|
||||
|
||||
|
|
@ -44,7 +46,7 @@ translator.register(PatchTestSnippet, PatchTestSnippetTranslationOptions)
|
|||
|
||||
# Panel Patching Models
|
||||
|
||||
class FieldPanelTranslationOptions(WagtailTranslationOptions):
|
||||
class FieldPanelTranslationOptions(TranslationOptions):
|
||||
fields = ('name',)
|
||||
|
||||
|
||||
|
|
@ -52,7 +54,7 @@ translator.register(FieldPanelPage, FieldPanelTranslationOptions)
|
|||
translator.register(FieldPanelSnippet, FieldPanelTranslationOptions)
|
||||
|
||||
|
||||
class ImageChooserPanelTranslationOptions(WagtailTranslationOptions):
|
||||
class ImageChooserPanelTranslationOptions(TranslationOptions):
|
||||
fields = ('image',)
|
||||
|
||||
|
||||
|
|
@ -60,7 +62,7 @@ translator.register(ImageChooserPanelPage, ImageChooserPanelTranslationOptions)
|
|||
translator.register(ImageChooserPanelSnippet, ImageChooserPanelTranslationOptions)
|
||||
|
||||
|
||||
class FieldRowPanelTranslationOptions(WagtailTranslationOptions):
|
||||
class FieldRowPanelTranslationOptions(TranslationOptions):
|
||||
fields = ('other_name',)
|
||||
|
||||
|
||||
|
|
@ -68,7 +70,7 @@ translator.register(FieldRowPanelPage, FieldRowPanelTranslationOptions)
|
|||
translator.register(FieldRowPanelSnippet, FieldRowPanelTranslationOptions)
|
||||
|
||||
|
||||
class StreamFieldPanelTranslationOptions(WagtailTranslationOptions):
|
||||
class StreamFieldPanelTranslationOptions(TranslationOptions):
|
||||
fields = ('body',)
|
||||
|
||||
|
||||
|
|
@ -76,7 +78,7 @@ translator.register(StreamFieldPanelPage, StreamFieldPanelTranslationOptions)
|
|||
translator.register(StreamFieldPanelSnippet, StreamFieldPanelTranslationOptions)
|
||||
|
||||
|
||||
class MultiFieldPanelTranslationOptions(WagtailTranslationOptions):
|
||||
class MultiFieldPanelTranslationOptions(TranslationOptions):
|
||||
fields = ()
|
||||
|
||||
|
||||
|
|
@ -84,14 +86,14 @@ translator.register(MultiFieldPanelPage, MultiFieldPanelTranslationOptions)
|
|||
translator.register(MultiFieldPanelSnippet, MultiFieldPanelTranslationOptions)
|
||||
|
||||
|
||||
class InlinePanelTranslationOptions(WagtailTranslationOptions):
|
||||
class InlinePanelTranslationOptions(TranslationOptions):
|
||||
fields = ('field_name', 'image_chooser', 'fieldrow_name',)
|
||||
|
||||
|
||||
translator.register(BaseInlineModel, InlinePanelTranslationOptions)
|
||||
|
||||
|
||||
class InlinePanelTranslationOptions(WagtailTranslationOptions):
|
||||
class InlinePanelTranslationOptions(TranslationOptions):
|
||||
fields = ()
|
||||
|
||||
|
||||
|
|
@ -100,7 +102,7 @@ translator.register(SnippetInlineModel, InlinePanelTranslationOptions)
|
|||
|
||||
|
||||
@register(InlinePanelPage)
|
||||
class InlinePanelModelTranslationOptions(WagtailTranslationOptions):
|
||||
class InlinePanelModelTranslationOptions(TranslationOptions):
|
||||
fields = ()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -7,4 +7,13 @@ from wagtail.wagtailcore.models import Page
|
|||
|
||||
@register(Page)
|
||||
class PageTR(TranslationOptions):
|
||||
pass
|
||||
class Meta:
|
||||
managed = False
|
||||
|
||||
fields = (
|
||||
'title',
|
||||
'slug',
|
||||
'seo_title',
|
||||
'search_description',
|
||||
'url_path',
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue