3 grudnia 2024
Kolejny problem z którym się zetknąłem i postanowiłem się podzielić rozwiązaniem z resztą świata. Rozwiązanie przyszło z trudem mimo przekopania stackoverflow i dokumentacji. Rozwiązanie to oczywiście bazuje na wiedzy z dokumentacji i stackoverflow ale musiało zostać dostosowane do mojego specyficznego problemu, a mianowicie kopiowanie obiektu modelu w django w przypadku wielokrotnego dziedziczenia.
Jak głosi dokumentacja django (doc) wystarczy ustawić pk na None:
blog = Blog(name='My blog', tagline='Blogging is easy')
blog.save() # blog.pk == 1
blog.pk = None
blog.save() # blog.pk == 2
I bardziej kompleksowy przykład dla dziedziczenia:
class ThemeBlog(Blog):
theme = models.CharField(max_length=200)
django_blog = ThemeBlog(name='Django', tagline='Django is easy', theme='python')
django_blog.save() # django_blog.pk == 3
Przede wszystkim w powyższym przykładzie z dokumentacji brakuje informacji "co dalej" jeśli mamy kolejne dziedziczenie.
W moim przypadku niestety rozwiązanie się nie sprawdziło, ponieważ dziedziczę kilkukrotnie. Posilę się tu przykładem który pozwolił mi zrozumieć "jak to działa" ze stackoverflow.
class ModelA(models.Model):
info1 = models.CharField(max_length=64)
class ModelB(ModelA):
info2 = models.CharField(max_length=64)
class ModelC(ModelB):
info3 = models.CharField(max_length=64)
I co wtedy?
Okazuje się że wszystkie pośrednie id również powinny być ustawiane na None. Wszystkie pola które mają _ptr
c.pk =None
c.id=None
c.modela_ptr_id=None
c.modelb_ptr_id=None
c.save()
Dopiero takie podejście do problemu powoduje utworzenie nowego id dla skopiowanego modelu. No dobrze, mamy sposób, ale co jeśli modeli z wieokrotnym dziedziczeniem jest 18 jak w moim przypadku, a każdy z wynikowych modeli ma różną ilość rodziców? Tak powstała prosta funkcja ustawiająca wszystko "po drodze" do docelowego modelu na None:
def clear_pks(new_model):
new_model.pk = None
new_model.id = None
for field in new_model._meta.fields:
if field.name.endswith('_ptr'):
setattr(mynew, field.name, None)
return mynew
Mam nadzieję, że ktoś trafi na moje rozwiązanie i skorzysta, oszczędzając przy tym wiele czasu.