From 51111309aef2b4385f9d91850a04e256176d083f Mon Sep 17 00:00:00 2001 From: Nazar Kotjuk Date: Fri, 29 Nov 2013 10:02:09 +0200 Subject: [PATCH] Function that can copy django objects with many to many realtions and translations --- article/models.py | 57 +++++++++++- conference/models.py | 64 ++++++++++++- exposition/admin.py | 13 ++- exposition/models.py | 52 ++++++++++- functions/views_help.py | 12 +++ news/models.py | 57 +++++++++++- place_conference/models.py | 62 ++++++++++++- place_exposition/models.py | 59 +++++++++++- seminar/models.py | 64 ++++++++++++- .../admin/exposition/exposition_all.html | 44 +++++---- theme/models.py | 93 ++++++++++++++++++- webinar/models.py | 64 ++++++++++++- 12 files changed, 602 insertions(+), 39 deletions(-) create mode 100644 functions/views_help.py diff --git a/article/models.py b/article/models.py index 32b55780..d41a853b 100644 --- a/article/models.py +++ b/article/models.py @@ -1,7 +1,16 @@ # -*- coding: utf-8 -*- from django.db import models -from hvad.models import TranslatableModel, TranslatedFields +from hvad.models import TranslatableModel, TranslatedFields, TranslationManager from django.template.defaultfilters import slugify +import copy + +class ArticleManager(TranslationManager): + def safe_get(self, **kwargs): + model = self.model + try: + return model.objects.get(**kwargs) + except: + return None class Article(TranslatableModel): """ @@ -10,6 +19,9 @@ class Article(TranslatableModel): Uses hvad.TranslatableModel which is child of django.db.models class """ + #set manager of this model + objects = ArticleManager + url = models.SlugField(unique=True) theme = models.ManyToManyField('theme.Theme') tag = models.ManyToManyField('theme.Tag', related_name='tags',blank=True, null=True) @@ -30,3 +42,46 @@ class Article(TranslatableModel): def __unicode__(self): return self.lazy_translation_getter('main_title', self.pk) + def clone(self): + """ + Return an identical copy of the instance with a new ID. + """ + if not self.pk: + raise ValueError('Instance must be saved before it can be cloned.') + + duplicate = copy.copy(self) + # Setting pk to None. Django thinking this is a new object. + duplicate.pk = None + # url must be unique + duplicate.url += '_copy' + if Article.objects.safe_get(url=duplicate.url): + #already has copy this instance + return + + duplicate.save() + # but lost all ManyToMany relations and Translations. + + # copy relations + for field in self._meta.many_to_many: + source = getattr(self, field.attname) + destination = getattr(duplicate, field.attname) + for item in source.all(): + destination.add(item) + + # copy translations + languages = self.get_available_languages() + ignore_fields = ['id', 'master', 'language_code'] + + for code in languages: + duplicate.translate(code) + tr = self._meta.translations_model.objects.get(language_code = code,master__id=self.pk) + for field in duplicate._translated_field_names: + + if field in ignore_fields: + continue + + setattr(duplicate, field, getattr(tr, field)) + + duplicate.save() + + return duplicate \ No newline at end of file diff --git a/conference/models.py b/conference/models.py index 99efff1b..c64ced91 100644 --- a/conference/models.py +++ b/conference/models.py @@ -1,11 +1,22 @@ # -*- coding: utf-8 -*- from django.db import models -from hvad.models import TranslatableModel, TranslatedFields +from hvad.models import TranslatableModel, TranslatedFields, TranslationManager +import copy #custom functions from functions.custom_fields import EnumField +class ConferenceManager(TranslationManager): + def safe_get(self, **kwargs): + model = self.model + try: + return model.objects.get(**kwargs) + except: + return None + + + CURRENCY = ('RUB', 'USD', 'EUR') class Conference(TranslatableModel): @@ -14,6 +25,9 @@ class Conference(TranslatableModel): Uses hvad.TranslatableModel which is child of django.db.models class """ + #set manager of this model + objects = ConferenceManager() + url = models.SlugField(unique=True) data_begin = models.DateField(verbose_name='Дата начала') data_end = models.DateField(verbose_name='Дата окончания') @@ -73,6 +87,54 @@ class Conference(TranslatableModel): def cancel(self): self.canceled_by_administrator = True + def clone(self): + """ + Return an identical copy of the instance with a new ID. + """ + if not self.pk: + raise ValueError('Instance must be saved before it can be cloned.') + + duplicate = copy.copy(self) + # Setting pk to None. Django thinking this is a new object. + duplicate.pk = None + # url must be unique + duplicate.url += '_copy' + if Conference.objects.safe_get(url=duplicate.url): + #already has copy this instance + return + + # duplicate should not be published + duplicate.is_published = False + duplicate.cancel_by_administrator = False + + duplicate.save() + # but lost all ManyToMany relations and Translations. + + # copy relations + for field in self._meta.many_to_many: + source = getattr(self, field.attname) + destination = getattr(duplicate, field.attname) + for item in source.all(): + destination.add(item) + + # copy translations + languages = self.get_available_languages() + ignore_fields = ['id', 'master', 'language_code'] + + for code in languages: + duplicate.translate(code) + tr = self._meta.translations_model.objects.get(language_code = code,master__id=self.pk) + for field in duplicate._translated_field_names: + + if field in ignore_fields: + continue + + setattr(duplicate, field, getattr(tr, field)) + + duplicate.save() + + return duplicate + class TimeTable(TranslatableModel): """ diff --git a/exposition/admin.py b/exposition/admin.py index 0b963609..4e7b36e2 100644 --- a/exposition/admin.py +++ b/exposition/admin.py @@ -18,6 +18,7 @@ from file.forms import FileModelForm import random #custom views from functions.custom_views import objects_list, delete_object +from functions.views_help import get_referer def exposition_all(request): @@ -51,18 +52,16 @@ def exposition_switch(request, url, action): return HttpResponse('error') + @login_required def exposition_copy(request, url): + exposition = Exposition.objects.safe_get(url=url) if not exposition: - return HttpResponse('error') + return HttpResponseRedirect(get_referer(request)) else: - exposition.pk = None - exposition.url += '_copy' - exposition.is_published = False - exposition.cancel_by_administrator = False - exposition.save() - return HttpResponse('success') + exposition.clone() + return HttpResponseRedirect(get_referer(request)) @login_required def exposition_add(request): diff --git a/exposition/models.py b/exposition/models.py index 6a6dbc92..9b0f9a49 100644 --- a/exposition/models.py +++ b/exposition/models.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from django.db import models from hvad.models import TranslatableModel, TranslatedFields, TranslationManager +import copy # from functions.custom_fields import EnumField @@ -110,6 +111,54 @@ class Exposition(TranslatableModel): self.canceled_by_administrator = True self.save() + def clone(self): + """ + Return an identical copy of the instance with a new ID. + """ + if not self.pk: + raise ValueError('Instance must be saved before it can be cloned.') + + duplicate = copy.copy(self) + # Setting pk to None. Django thinking this is a new object. + duplicate.pk = None + # url must be unique + duplicate.url += '_copy' + if Exposition.objects.safe_get(url=duplicate.url): + #already has copy this instance + return + + # duplicate should not be published + duplicate.is_published = False + duplicate.cancel_by_administrator = False + + duplicate.save() + # but lost all ManyToMany relations and Translations. + + # copy relations + for field in self._meta.many_to_many: + source = getattr(self, field.attname) + destination = getattr(duplicate, field.attname) + for item in source.all(): + destination.add(item) + + # copy translations + languages = self.get_available_languages() + ignore_fields = ['id', 'master', 'language_code'] + + for code in languages: + duplicate.translate(code) + tr = self._meta.translations_model.objects.get(language_code = code,master__id=self.pk) + for field in duplicate._translated_field_names: + + if field in ignore_fields: + continue + + setattr(duplicate, field, getattr(tr, field)) + + duplicate.save() + + return duplicate + class TimeTable(TranslatableModel): """ @@ -122,5 +171,4 @@ class TimeTable(TranslatableModel): #translated fields translations = TranslatedFields( name = models.CharField(verbose_name='Название', max_length=255) - ) - + ) \ No newline at end of file diff --git a/functions/views_help.py b/functions/views_help.py new file mode 100644 index 00000000..17a28243 --- /dev/null +++ b/functions/views_help.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +import re + +def get_referer(request, default=None): + referer = request.META.get('HTTP_REFERER') + if not referer: + return default + # remove the protocol and split the url at the slashes + referer = re.sub('^https?:\/\/', '', referer).split('/') + # add the slash at the relative path's view and finished + referer = u'/' + u'/'.join(referer[1:]) + return referer \ No newline at end of file diff --git a/news/models.py b/news/models.py index e45c1a82..7c9069e0 100644 --- a/news/models.py +++ b/news/models.py @@ -1,14 +1,25 @@ # -*- coding: utf-8 -*- from django.db import models -from hvad.models import TranslatableModel, TranslatedFields +from hvad.models import TranslatableModel, TranslatedFields, TranslationManager from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes import generic +import copy #functions from functions.custom_fields import EnumField +class NewsManager(TranslationManager): + def safe_get(self, **kwargs): + model = self.model + try: + return model.objects.get(**kwargs) + except: + return None TYPES = ('announcement', 'news', 'overview') class News(TranslatableModel): + #set manager of this model + objects = NewsManager + content_type = models.ForeignKey(ContentType, null=True) object_id = models.PositiveIntegerField(blank=True, null=True) object = generic.GenericForeignKey(content_type, object_id) @@ -37,3 +48,47 @@ class News(TranslatableModel): def __unicode__(self): return self.lazy_translation_getter('main_title', self.pk) + + def clone(self): + """ + Return an identical copy of the instance with a new ID. + """ + if not self.pk: + raise ValueError('Instance must be saved before it can be cloned.') + + duplicate = copy.copy(self) + # Setting pk to None. Django thinking this is a new object. + duplicate.pk = None + # url must be unique + duplicate.url += '_copy' + if News.objects.safe_get(url=duplicate.url): + #already has copy this instance + return + + duplicate.save() + # but lost all ManyToMany relations and Translations. + + # copy relations + for field in self._meta.many_to_many: + source = getattr(self, field.attname) + destination = getattr(duplicate, field.attname) + for item in source.all(): + destination.add(item) + + # copy translations + languages = self.get_available_languages() + ignore_fields = ['id', 'master', 'language_code'] + + for code in languages: + duplicate.translate(code) + tr = self._meta.translations_model.objects.get(language_code = code,master__id=self.pk) + for field in duplicate._translated_field_names: + + if field in ignore_fields: + continue + + setattr(duplicate, field, getattr(tr, field)) + + duplicate.save() + + return duplicate \ No newline at end of file diff --git a/place_conference/models.py b/place_conference/models.py index 2829c1bd..018d9e67 100644 --- a/place_conference/models.py +++ b/place_conference/models.py @@ -1,9 +1,19 @@ # -*- coding: utf-8 -*- from django.db import models from django.contrib.contenttypes import generic -from hvad.models import TranslatableModel, TranslatedFields +from hvad.models import TranslatableModel, TranslatedFields, TranslationManager from functions.custom_fields import EnumField from functions.custom_fields import LocationField +import copy + +class PlaceConferenceManager(TranslationManager): + def safe_get(self, **kwargs): + model = self.model + try: + return model.objects.get(**kwargs) + except: + return None + CONFERENCE_TYPE = (('Convention centre', 'Конгресс-центр'), ('Exposition centre', 'Конференц зал'),) @@ -15,6 +25,9 @@ class PlaceConference(TranslatableModel): Uses hvad.TranslatableModel which is child of django.db.models class """ + #set manager of this model + objects = PlaceConferenceManager() + url = models.SlugField(unique=True) country = models.ForeignKey('country.Country', on_delete=models.PROTECT) city = models.ForeignKey('city.City', on_delete=models.PROTECT) @@ -60,6 +73,53 @@ class PlaceConference(TranslatableModel): def __unicode__(self): return self.lazy_translation_getter('name', unicode(self.pk)) + def clone(self): + """ + Return an identical copy of the instance with a new ID. + """ + if not self.pk: + raise ValueError('Instance must be saved before it can be cloned.') + + duplicate = copy.copy(self) + # Setting pk to None. Django thinking this is a new object. + duplicate.pk = None + # url must be unique + duplicate.url += '_copy' + if PlaceConference.objects.safe_get(url=duplicate.url): + #already has copy this instance + return + + # duplicate should not be published + duplicate.is_published = False + duplicate.cancel_by_administrator = False + + duplicate.save() + # but lost all Translations and Halls. + + # copy translations + languages = self.get_available_languages() + ignore_fields = ['id', 'master', 'language_code'] + + for code in languages: + duplicate.translate(code) + tr = self._meta.translations_model.objects.get(language_code = code,master__id=self.pk) + for field in duplicate._translated_field_names: + + if field in ignore_fields: + continue + + setattr(duplicate, field, getattr(tr, field)) + + duplicate.save() + # copy halls + halls = Hall.objects.filter(place_conference=getattr(self, 'id')) + for hall in halls: + duplicate_hall = copy.copy(hall) + duplicate_hall.place_conference = duplicate + duplicate_hall.save() + + return duplicate + class Hall(models.Model): """ Create Hall model which saves information about halls in PlaceConference diff --git a/place_exposition/models.py b/place_exposition/models.py index 71f0fb25..9f8de541 100644 --- a/place_exposition/models.py +++ b/place_exposition/models.py @@ -1,8 +1,18 @@ # -*- coding: utf-8 -*- from django.db import models from django.contrib.contenttypes import generic -from hvad.models import TranslatableModel, TranslatedFields +from hvad.models import TranslatableModel, TranslatedFields, TranslationManager from functions.custom_fields import EnumField, LocationField +import copy + +class PlaceExpositionManager(TranslationManager): + def safe_get(self, **kwargs): + model = self.model + try: + return model.objects.get(**kwargs) + except: + return None + EXPOSITION_TYPE = (('Exposition complex', 'Выставочный комплекс'), ('Convention centre', 'Конгессо-выставочный центр'), @@ -15,6 +25,9 @@ class PlaceExposition(TranslatableModel): Uses hvad.TranslatableModel which is child of django.db.models class """ + #set manager of this model + objects = PlaceExpositionManager() + url = models.SlugField(unique=True) country = models.ForeignKey('country.Country', on_delete=models.PROTECT) city = models.ForeignKey('city.City', on_delete=models.PROTECT) @@ -69,6 +82,50 @@ class PlaceExposition(TranslatableModel): def __unicode__(self): return self.lazy_translation_getter('name', unicode(self.pk)) + def clone(self): + """ + Return an identical copy of the instance with a new ID. + """ + if not self.pk: + raise ValueError('Instance must be saved before it can be cloned.') + + duplicate = copy.copy(self) + # Setting pk to None. Django thinking this is a new object. + duplicate.pk = None + # url must be unique + duplicate.url += '_copy' + if PlaceExposition.objects.safe_get(url=duplicate.url): + #already has copy this instance + return + + duplicate.save() + # but lost all Translations and Halls. + + # copy translations + languages = self.get_available_languages() + ignore_fields = ['id', 'master', 'language_code'] + + for code in languages: + duplicate.translate(code) + tr = self._meta.translations_model.objects.get(language_code = code,master__id=self.pk) + for field in duplicate._translated_field_names: + + if field in ignore_fields: + continue + + setattr(duplicate, field, getattr(tr, field)) + + duplicate.save() + # copy halls + halls = Hall.objects.filter(place_exposition=getattr(self, 'id')) + for hall in halls: + duplicate_hall = copy.copy(hall) + duplicate_hall.exposition = duplicate + duplicate_hall.save() + + return duplicate + + class Hall(models.Model): """ Create Hall model which saves information about halls in PlaceExposition diff --git a/seminar/models.py b/seminar/models.py index 397455aa..20fc6bcb 100644 --- a/seminar/models.py +++ b/seminar/models.py @@ -1,10 +1,19 @@ # -*- coding: utf-8 -*- from django.db import models -from hvad.models import TranslatableModel, TranslatedFields +from hvad.models import TranslatableModel, TranslatedFields, TranslationManager +import copy # from functions.custom_fields import EnumField from functions.custom_fields import LocationField +class SeminarManager(TranslationManager): + def safe_get(self, **kwargs): + model = self.model + try: + return model.objects.get(**kwargs) + except: + return None + CURRENCY = ('RUB', 'USD', 'EUR') @@ -14,6 +23,9 @@ class Seminar(TranslatableModel): Uses hvad.TranslatableModel which is child of django.db.models class """ + #set manager of this model + objects = SeminarManager() + url = models.SlugField(unique=True) data_begin = models.DateTimeField(verbose_name='Дата начала') data_end = models.DateTimeField(verbose_name='Дата окончания') @@ -69,4 +81,52 @@ class Seminar(TranslatableModel): return self.lazy_translation_getter('name', unicode(self.pk)) def cancel(self): - self.canceled_by_administrator = True \ No newline at end of file + self.canceled_by_administrator = True + + def clone(self): + """ + Return an identical copy of the instance with a new ID. + """ + if not self.pk: + raise ValueError('Instance must be saved before it can be cloned.') + + duplicate = copy.copy(self) + # Setting pk to None. Django thinking this is a new object. + duplicate.pk = None + # url must be unique + duplicate.url += '_copy' + if Seminar.objects.safe_get(url=duplicate.url): + #already has copy this instance + return + + # duplicate should not be published + duplicate.is_published = False + duplicate.cancel_by_administrator = False + + duplicate.save() + # but lost all ManyToMany relations and Translations. + + # copy relations + for field in self._meta.many_to_many: + source = getattr(self, field.attname) + destination = getattr(duplicate, field.attname) + for item in source.all(): + destination.add(item) + + # copy translations + languages = self.get_available_languages() + ignore_fields = ['id', 'master', 'language_code'] + + for code in languages: + duplicate.translate(code) + tr = self._meta.translations_model.objects.get(language_code = code,master__id=self.pk) + for field in duplicate._translated_field_names: + + if field in ignore_fields: + continue + + setattr(duplicate, field, getattr(tr, field)) + + duplicate.save() + + return duplicate \ No newline at end of file diff --git a/templates/admin/exposition/exposition_all.html b/templates/admin/exposition/exposition_all.html index aebed633..e3caa6f4 100644 --- a/templates/admin/exposition/exposition_all.html +++ b/templates/admin/exposition/exposition_all.html @@ -3,28 +3,28 @@ {% block scripts %}