Open hallpower opened 2 years ago
Hey @hallpower,
I... cannot reproduce this locally. See https://youtu.be/uWctc6HyOIY using a barebones Wagtail project (as in wagtail start projectname
) with a HomePage
and a Partners
model simply inheriting from Page
.
adding the traceback from the Slack thread here for reference:
a few things to try: check in the django shell that the page gets created. e.g.
# replace ID with the page id you are trying to translate. e.g. 14 from /admin/localize/submit/page/14/ in the traceback
for translation in Partner.objects.get(pk=ID).get_translations(inclusive=True):
print(translation, translation.pk, translation.locale)
@zerolab, sorry for slow response; I was off on family vacation.
Thank you so much for supporting in troubleshooting this. I really appreciate it!
I figured it out! Of course it now makes perfect sense. I had a unique constraint on a few fields in my models. When I tried to create the translation, it failed to create the translation page due to the constraint violation. The issue can be reproduced by putting a unique=True,
constraint in a charfield in a model. You can test it with this snippet model, which will throw the same error:
@register_snippet
class Amenity(TranslatableMixin, models.Model):
"""Type model for amenities."""
title = models.CharField(
max_length=100,
blank=False,
null=True,
unique=True,
)
panels = [
MultiFieldPanel(
[
FieldPanel("title"),
],
heading="Amenity",
)
]
def __str__(self):
"""String representation of this Class."""
return (
f"{self.title}"
)
class Meta:
verbose_name = "Amenity"
verbose_name_plural = "Amenities"
ordering = ["title"]
unique_together = ("translation_key", "locale")
The workaround would be to add a tuple in the class Meta: unique_together to ensure the constraint. However, this isn't handled very elegantly when it fails, causing an IntegrityError.
In my view, ideally Wagtail-Localize should recognize the unique constraint on a field and automatically interpret the unique constraint in conjunction with the locale. It would also be nice if it failed more elegantly.
@hallpower Hi Mark,
I've come across this on my own site, and needed an additional pair in the unique_together
with the unique field. In your case, probably
unique_together = ('translation_key', 'locale'), ('locale', 'parent_amenity')
Then I added some extra code around limiting new instances to the default locale to deal with the integrity error issue.
https://enzedonline.com/en/tech-blog/dealing-with-unique-fields-on-a-multi-lingual-site/
The field on mine was an editable slug-field rather than an ID. Maybe some of the steps will be redundant since your field is auto-generated and is already unique.
Thank you for pitching in, @enzedonline!
We could improve documentation as a starting point.
The unique_together = ("translation_key", "locale")
constraint comes from core via TranslatableMixin
. I feel that TranslatableMixin
should perhaps be wiser in its check - https://github.com/wagtail/wagtail/blob/5994cc43dfc5cc1ed891ab78eff3a3bcf56f6830/wagtail/models/i18n.py#L103-L127
@zerolab glad this was useful
I think the biggest part of this problem is that unique
is in the standard validation (so an integrity error is picked up even if not handled) while unique_together
isn't - it just fails silently while looking like it has succeeded in translating.
This means you have to write all the code to check integrity in a custom clean method each time. Gets messy.
raise self.model.DoesNotExist( artisan_information.models.Artisan.DoesNotExist: Artisan matching query does not exist.
How can I solve the problem of
raise self.model.DoesNotExist( artisan_information.models.Artisan.DoesNotExist: Artisan matching query does not exist.
artisan = Artisan.objects.get(user=user), user is defined in my models.py.
@RoyTea please join the Wagtail Slack and ask there. The two lines you shared do not give enough context, other than Artisan.objects.get(user=user)
can fail if you do not have and Artisan
instance with the given user. But that is very much unrelated to wagtail-localize
I had this exact problem but with a totally different cause. There was a ForeignKey
field which had a poorly set ID (from porting the content in from a different CMS). Something that Django does badly is tell you which field is the issue.
I wrote a management command (based on modelcluster) to find the field in my model Activity
which subclasses Wagtail Page
.
import json
from django.conf import settings
from django.core.management.base import CommandError, BaseCommand
from django.core.exceptions import FieldDoesNotExist
from django.db.models.fields.related import ForeignObjectRel
from wagtail.models import Locale
from wagtail_localize.models import TranslatableObject, TranslationSource
from activities.models import Activity
import logging
logger = logging.getLogger(__name__)
class Command(BaseCommand):
"""
Add all activities and other content into wagtail
"""
help = 'Check through all activities and check for missing pdfs in translation files, and sync to DB if missing'
def add_arguments(self, parser):
parser.add_argument("-c", "--code", dest='code', type=str, help="Fix specific activity by code")
parser.add_argument("-a", "--all", dest='all', action='store_true', help="Fix all activities")
parser.add_argument("-d", "--diagnostic", dest='diagnostic', action='store_true', help="Show the issue without changing DB")
def handle(self, *args, **options):
en = Locale.objects.get(language_code='en')
if options['code']:
activities = Activity.objects.filter(code=options['code'], locale=en)
elif options['all']:
activities = Activity.objects.filter(locale=en)
else:
logger.error('Select either --code or --all to run this command.')
return
for a in activities:
try:
transobj = TranslatableObject.objects.get(translation_key=a.translation_key)
except TranslatableObject.DoesNotExist:
logger.error(f'No activity translation for {a.code}')
continue
try:
ts = TranslationSource.objects.get(object=transobj)
except:
logger.error(f'Error with {a.code}')
continue
data = json.loads(ts.content_json)
pk_field = Activity._meta.pk
kwargs = {}
# If model is a child via multitable inheritance, we need to set ptr_id fields all the way up
# to the main PK field, as Django won't populate these for us automatically.
while pk_field.remote_field and pk_field.remote_field.parent_link:
kwargs[pk_field.attname] = data['pk']
pk_field = pk_field.remote_field.model._meta.pk
kwargs[pk_field.attname] = data['pk']
for field_name, field_value in data.items():
try:
field = Activity._meta.get_field(field_name)
except FieldDoesNotExist:
continue
# Filter out reverse relations
if isinstance(field, ForeignObjectRel):
continue
if field.remote_field and isinstance(field.remote_field, models.ManyToManyRel):
related_objects = field.remote_field.model._default_manager.filter(pk__in=field_value)
kwargs[field.attname] = list(related_objects)
elif field.remote_field and isinstance(field.remote_field, models.ManyToOneRel):
if field_value is None:
kwargs[field.attname] = None
else:
try:
clean_value = field.remote_field.model._meta.get_field(field.remote_field.field_name).to_python(field_value)
print("All OK with {}".format(a.title))
except:
if options['diagnostic']:
print('Issue with {} in {}'.format(field_name, a.title))
continue
else:
ts.update_from_db()
print('Synced {} from DB'.format(a.title))
continue
As discussed with @zerolab in the Wagtail Slack support channel, I'm getting a ' DoesNotExist matching query does not exist' error while trying to create translations for some pages.
I have an existing wagtail project that has been upgraded to Wagtail 3.0, running wagtail-localize 1.2.
The issue is described in: https://wagtailcms.slack.com/archives/C81FGJR2S/p1654081344988459?thread_ts=1654035708.629839&cid=C81FGJR2S
There is a 3 min demo video of the process to generate the issue here: https://www.youtube.com/watch?v=ihxmNtvY5OA
Some code and configurations are available here: