Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cloning a model moves a translation #348

Open
igorsantos07 opened this issue Dec 8, 2017 · 1 comment
Open

Cloning a model moves a translation #348

igorsantos07 opened this issue Dec 8, 2017 · 1 comment

Comments

@igorsantos07
Copy link

Not sure if this is a bug or me doing something nasty while I don't understand the inner workings of HVAD (should I?), but we're trying to implement a "duplicate entry" action in Django Admin.

Given a small google query, usually, models can be easily duplicated by saving them with the PK set to None, but this seems to make HVAD move the first translation into the newly-created entry.

Using copy or deepcopy yields the same results, so I'm not using these on the example code below:

model = MyModel.objects.first()
print('original: %i' % model.id)
model.pk = None
model.save()
print('new: %i' % model.id)

This is enough for you to have a fresh copy of MyModel on the database, but you'll also notice that from the three translations you had on the *_translation table, one of these (the first one) moved into the new model's master_id.

After duplicating the model we were iterating through list(model.translations.all()), defined before changing the original model (so we kept in memory a list of all original translations), and cloning these as well... And that started yielding errors, as there was already an entry for the first language + the new model.
The solution found was to first save all translations back with the original master, then clone them onto the new master, like this:

for tr in translations:
    tr.master = model
    tr.save()                    
                                 
    new_tr = copy.copy(tr)      
    new_tr.pk = None            
    new_tr.master = new_model  # cloned using copy() as well
    new_tr.save()               
@spectras
Copy link
Collaborator

spectras commented Dec 11, 2017

Hello,
Thanks for reporting here.

It is indeed a consequence of the internals of hvad. I am sorry this is not very easy to find in the documentation, but the way hvad handles saves is:

  • Save main model.
  • Copy main model's id into cached translation.
  • Save cached translation.

It has to be done that way for many reasons, such as newly created objects not having an id until that point. In your case, however, it bites because the translation that is currently loaded will move, as you noticed.

There are two ways to fix this.

  • The dirty way would be to reach out for cached translation's id and delete it: get_cached_translation(obj).pk = None. [No actually it would copy the translation, which will make your translation copying loop fail, it's even worse an idea than I though.]

  • A cleaner way would be not to load the translation at all in the first place: MyModel.objects.untranslated().first() should do the trick.

As a rule of thumb, code that is translation-aware and manipulates translations explicitly should load untranslated() models to skip hvad special processing of loaded translation.

I am sorry this hit you, and hope the explanation will help you swiftly solve the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants