Merge pull request #783 from taigaio/fixing-issue-when-multiple-attachment-deletion
Removing unnecesary advisory_lockremotes/origin/issue/4795/notification_even_they_are_disabled
commit
0064e6e82a
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
from django_pglocks import advisory_lock
|
from django_pglocks import advisory_lock
|
||||||
|
|
||||||
|
|
||||||
def detail_route(methods=['get'], **kwargs):
|
def detail_route(methods=['get'], **kwargs):
|
||||||
"""
|
"""
|
||||||
Used to mark a method on a ViewSet that should be routed for detail requests.
|
Used to mark a method on a ViewSet that should be routed for detail requests.
|
||||||
|
@ -51,12 +52,11 @@ def model_pk_lock(func):
|
||||||
"""
|
"""
|
||||||
def decorator(self, *args, **kwargs):
|
def decorator(self, *args, **kwargs):
|
||||||
from taiga.base.utils.db import get_typename_for_model_class
|
from taiga.base.utils.db import get_typename_for_model_class
|
||||||
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
|
|
||||||
pk = self.kwargs.get(self.pk_url_kwarg, None)
|
pk = self.kwargs.get(self.pk_url_kwarg, None)
|
||||||
tn = get_typename_for_model_class(self.get_queryset().model)
|
tn = get_typename_for_model_class(self.get_queryset().model)
|
||||||
key = "{0}:{1}".format(tn, pk)
|
key = "{0}:{1}".format(tn, pk)
|
||||||
|
|
||||||
with advisory_lock(key) as acquired_key_lock:
|
with advisory_lock(key):
|
||||||
return func(self, *args, **kwargs)
|
return func(self, *args, **kwargs)
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
|
@ -26,6 +26,8 @@ from django.http import Http404
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from django_pglocks import advisory_lock
|
||||||
|
|
||||||
from taiga.base import filters
|
from taiga.base import filters
|
||||||
from taiga.base import exceptions as exc
|
from taiga.base import exceptions as exc
|
||||||
from taiga.base import response
|
from taiga.base import response
|
||||||
|
@ -214,20 +216,22 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin,
|
||||||
if not template_description:
|
if not template_description:
|
||||||
raise response.BadRequest(_("Not valid template description"))
|
raise response.BadRequest(_("Not valid template description"))
|
||||||
|
|
||||||
template_slug = slugify_uniquely(template_name, models.ProjectTemplate)
|
with advisory_lock("create-project-template") as acquired_key_lock:
|
||||||
|
template_slug = slugify_uniquely(template_name, models.ProjectTemplate)
|
||||||
|
|
||||||
project = self.get_object()
|
project = self.get_object()
|
||||||
|
|
||||||
self.check_permissions(request, 'create_template', project)
|
self.check_permissions(request, 'create_template', project)
|
||||||
|
|
||||||
template = models.ProjectTemplate(
|
template = models.ProjectTemplate(
|
||||||
name=template_name,
|
name=template_name,
|
||||||
slug=template_slug,
|
slug=template_slug,
|
||||||
description=template_description,
|
description=template_description,
|
||||||
)
|
)
|
||||||
|
|
||||||
template.load_data_from_project(project)
|
template.load_data_from_project(project)
|
||||||
template.save()
|
|
||||||
|
template.save()
|
||||||
return response.Created(serializers.ProjectTemplateSerializer(template).data)
|
return response.Created(serializers.ProjectTemplateSerializer(template).data)
|
||||||
|
|
||||||
@detail_route(methods=['POST'])
|
@detail_route(methods=['POST'])
|
||||||
|
|
|
@ -74,7 +74,7 @@ class BaseAttachmentViewSet(HistoryResourceMixin, WatchedResourceMixin,
|
||||||
# NOTE: When destroy an attachment, the content_object change
|
# NOTE: When destroy an attachment, the content_object change
|
||||||
# after and not before
|
# after and not before
|
||||||
self.persist_history_snapshot(obj, delete=True)
|
self.persist_history_snapshot(obj, delete=True)
|
||||||
super().pre_delete(obj)
|
super().post_delete(obj)
|
||||||
|
|
||||||
def get_object_for_snapshot(self, obj):
|
def get_object_for_snapshot(self, obj):
|
||||||
return obj.content_object
|
return obj.content_object
|
||||||
|
|
|
@ -62,7 +62,7 @@ class HistoryResourceMixin(object):
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
|
|
||||||
sobj = self.get_object_for_snapshot(obj)
|
sobj = self.get_object_for_snapshot(obj)
|
||||||
if sobj != obj and delete:
|
if sobj != obj:
|
||||||
delete = False
|
delete = False
|
||||||
|
|
||||||
notifications_services.analize_object_for_watchers(obj, comment, user)
|
notifications_services.analize_object_for_watchers(obj, comment, user)
|
||||||
|
|
|
@ -312,7 +312,7 @@ def take_snapshot(obj: object, *, comment: str="", user=None, delete: bool=False
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = make_key_from_model_object(obj)
|
key = make_key_from_model_object(obj)
|
||||||
with advisory_lock(key):
|
with advisory_lock("history-"+key):
|
||||||
typename = get_typename_for_model_class(obj.__class__)
|
typename = get_typename_for_model_class(obj.__class__)
|
||||||
|
|
||||||
new_fobj = freeze_model_instance(obj)
|
new_fobj = freeze_model_instance(obj)
|
||||||
|
|
|
@ -16,9 +16,8 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from django.apps import apps
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Prefetch, Count
|
from django.db.models import Count
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
@ -28,7 +27,7 @@ from django.utils.functional import cached_property
|
||||||
from taiga.base.utils.slug import slugify_uniquely
|
from taiga.base.utils.slug import slugify_uniquely
|
||||||
from taiga.base.utils.dicts import dict_sum
|
from taiga.base.utils.dicts import dict_sum
|
||||||
from taiga.projects.notifications.mixins import WatchedModelMixin
|
from taiga.projects.notifications.mixins import WatchedModelMixin
|
||||||
from taiga.projects.userstories.models import UserStory
|
from django_pglocks import advisory_lock
|
||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
import datetime
|
import datetime
|
||||||
|
@ -84,9 +83,11 @@ class Milestone(WatchedModelMixin, models.Model):
|
||||||
if not self._importing or not self.modified_date:
|
if not self._importing or not self.modified_date:
|
||||||
self.modified_date = timezone.now()
|
self.modified_date = timezone.now()
|
||||||
if not self.slug:
|
if not self.slug:
|
||||||
self.slug = slugify_uniquely(self.name, self.__class__)
|
with advisory_lock("milestone-creation-{}".format(self.project_id)):
|
||||||
|
self.slug = slugify_uniquely(self.name, self.__class__)
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
else:
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def cached_user_stories(self):
|
def cached_user_stories(self):
|
||||||
|
|
|
@ -16,27 +16,23 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import itertools
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.postgres.fields import ArrayField
|
from django.contrib.postgres.fields import ArrayField
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import signals, Q
|
from django.db.models import Q
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.conf import settings
|
|
||||||
from django.dispatch import receiver
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
|
from django_pglocks import advisory_lock
|
||||||
|
|
||||||
from django_pgjson.fields import JsonField
|
from django_pgjson.fields import JsonField
|
||||||
|
|
||||||
from taiga.projects.tagging.models import TaggedMixin
|
from taiga.projects.tagging.models import TaggedMixin
|
||||||
from taiga.projects.tagging.models import TagsColorsdMixin
|
from taiga.projects.tagging.models import TagsColorsdMixin
|
||||||
from taiga.base.utils.dicts import dict_sum
|
|
||||||
from taiga.base.utils.files import get_file_path
|
from taiga.base.utils.files import get_file_path
|
||||||
from taiga.base.utils.sequence import arithmetic_progression
|
from taiga.base.utils.sequence import arithmetic_progression
|
||||||
from taiga.base.utils.slug import slugify_uniquely
|
from taiga.base.utils.slug import slugify_uniquely
|
||||||
|
@ -270,16 +266,6 @@ class Project(ProjectDefaults, TaggedMixin, TagsColorsdMixin, models.Model):
|
||||||
if not self._importing or not self.modified_date:
|
if not self._importing or not self.modified_date:
|
||||||
self.modified_date = timezone.now()
|
self.modified_date = timezone.now()
|
||||||
|
|
||||||
if not self.slug:
|
|
||||||
base_name = "{}-{}".format(self.owner.username, self.name)
|
|
||||||
base_slug = slugify_uniquely(base_name, self.__class__)
|
|
||||||
slug = base_slug
|
|
||||||
for i in arithmetic_progression():
|
|
||||||
if not type(self).objects.filter(slug=slug).exists() or i > 100:
|
|
||||||
break
|
|
||||||
slug = "{}-{}".format(base_slug, i)
|
|
||||||
self.slug = slug
|
|
||||||
|
|
||||||
if not self.is_backlog_activated:
|
if not self.is_backlog_activated:
|
||||||
self.total_milestones = None
|
self.total_milestones = None
|
||||||
self.total_story_points = None
|
self.total_story_points = None
|
||||||
|
@ -290,13 +276,25 @@ class Project(ProjectDefaults, TaggedMixin, TagsColorsdMixin, models.Model):
|
||||||
if not self.is_looking_for_people:
|
if not self.is_looking_for_people:
|
||||||
self.looking_for_people_note = ""
|
self.looking_for_people_note = ""
|
||||||
|
|
||||||
if self.anon_permissions == None:
|
if self.anon_permissions is None:
|
||||||
self.anon_permissions = []
|
self.anon_permissions = []
|
||||||
|
|
||||||
if self.public_permissions == None:
|
if self.public_permissions is None:
|
||||||
self.public_permissions = []
|
self.public_permissions = []
|
||||||
|
|
||||||
super().save(*args, **kwargs)
|
if not self.slug:
|
||||||
|
with advisory_lock("project-creation"):
|
||||||
|
base_name = "{}-{}".format(self.owner.username, self.name)
|
||||||
|
base_slug = slugify_uniquely(base_name, self.__class__)
|
||||||
|
slug = base_slug
|
||||||
|
for i in arithmetic_progression():
|
||||||
|
if not type(self).objects.filter(slug=slug).exists() or i > 100:
|
||||||
|
break
|
||||||
|
slug = "{}-{}".format(base_slug, i)
|
||||||
|
self.slug = slug
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
else:
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
def refresh_totals(self, save=True):
|
def refresh_totals(self, save=True):
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
|
|
|
@ -59,7 +59,7 @@ def update_role_points_when_create_or_edit_us(sender, instance, **kwargs):
|
||||||
|
|
||||||
def update_milestone_of_tasks_when_edit_us(sender, instance, created, **kwargs):
|
def update_milestone_of_tasks_when_edit_us(sender, instance, created, **kwargs):
|
||||||
if not created:
|
if not created:
|
||||||
instance.tasks.update(milestone=instance.milestone)
|
instance.tasks.exclude(milestone=instance.milestone).update(milestone=instance.milestone)
|
||||||
for task in instance.tasks.all():
|
for task in instance.tasks.all():
|
||||||
take_snapshot(task)
|
take_snapshot(task)
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,8 @@ from django.contrib.contenttypes.fields import GenericRelation
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from django_pglocks import advisory_lock
|
||||||
|
|
||||||
from taiga.base.utils.slug import slugify_uniquely_for_queryset
|
from taiga.base.utils.slug import slugify_uniquely_for_queryset
|
||||||
from taiga.projects.notifications.mixins import WatchedModelMixin
|
from taiga.projects.notifications.mixins import WatchedModelMixin
|
||||||
from taiga.projects.occ import OCCModelMixin
|
from taiga.projects.occ import OCCModelMixin
|
||||||
|
@ -84,7 +86,9 @@ class WikiLink(models.Model):
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if not self.href:
|
if not self.href:
|
||||||
wl_qs = self.project.wiki_links.all()
|
with advisory_lock("wiki-page-creation-{}".format(self.project_id)):
|
||||||
self.href = slugify_uniquely_for_queryset(self.title, wl_qs, slugfield="href")
|
wl_qs = self.project.wiki_links.all()
|
||||||
|
self.href = slugify_uniquely_for_queryset(self.title, wl_qs, slugfield="href")
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
else:
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
|
@ -35,6 +35,7 @@ from django.utils import timezone
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from django_pgjson.fields import JsonField
|
from django_pgjson.fields import JsonField
|
||||||
|
from django_pglocks import advisory_lock
|
||||||
|
|
||||||
from taiga.auth.tokens import get_token_for_user
|
from taiga.auth.tokens import get_token_for_user
|
||||||
from taiga.base.utils.slug import slugify_uniquely
|
from taiga.base.utils.slug import slugify_uniquely
|
||||||
|
@ -265,20 +266,21 @@ class User(AbstractBaseUser, PermissionsMixin):
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
self.username = slugify_uniquely("deleted-user", User, slugfield="username")
|
with advisory_lock("delete-user"):
|
||||||
self.email = "{}@taiga.io".format(self.username)
|
self.username = slugify_uniquely("deleted-user", User, slugfield="username")
|
||||||
self.is_active = False
|
self.email = "{}@taiga.io".format(self.username)
|
||||||
self.full_name = "Deleted user"
|
self.is_active = False
|
||||||
self.color = ""
|
self.full_name = "Deleted user"
|
||||||
self.bio = ""
|
self.color = ""
|
||||||
self.lang = ""
|
self.bio = ""
|
||||||
self.theme = ""
|
self.lang = ""
|
||||||
self.timezone = ""
|
self.theme = ""
|
||||||
self.colorize_tags = True
|
self.timezone = ""
|
||||||
self.token = None
|
self.colorize_tags = True
|
||||||
self.set_unusable_password()
|
self.token = None
|
||||||
self.photo = None
|
self.set_unusable_password()
|
||||||
self.save()
|
self.photo = None
|
||||||
|
self.save()
|
||||||
self.auth_data.all().delete()
|
self.auth_data.all().delete()
|
||||||
|
|
||||||
# Blocking all owned projects
|
# Blocking all owned projects
|
||||||
|
|
Loading…
Reference in New Issue