Merge pull request #679 from taigaio/signalkraft-enhancement/usermodel

Refactored occurrences of users.User to get_user_model(), which allowsa custom user model
remotes/origin/issue/4795/notification_even_they_are_disabled
David Barragán Merino 2016-03-29 19:34:01 +02:00
commit 878f2773f2
35 changed files with 132 additions and 118 deletions

View File

@ -28,6 +28,7 @@ answer newbie questions, and generally made taiga that much better:
- Joe Letts - Joe Letts
- Julien Palard - Julien Palard
- luyikei <luyikei.qmltu@gmail.com> - luyikei <luyikei.qmltu@gmail.com>
- Motius GmbH <mail@motius.de>
- Ricky Posner <e@eposner.com> - Ricky Posner <e@eposner.com>
- Yamila Moreno <yamila.moreno@kaleidos.net> - Yamila Moreno <yamila.moreno@kaleidos.net>
- Yaser Alraddadi <yaser@yr.sa> - Yaser Alraddadi <yaser@yr.sa>

View File

@ -25,6 +25,7 @@ not uses clasess and uses simple functions.
""" """
from django.apps import apps from django.apps import apps
from django.contrib.auth import get_user_model
from django.db import transaction as tx from django.db import transaction as tx
from django.db import IntegrityError from django.db import IntegrityError
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
@ -69,7 +70,7 @@ def is_user_already_registered(*, username:str, email:str) -> (bool, str):
and in case he does whats the duplicated attribute and in case he does whats the duplicated attribute
""" """
user_model = apps.get_model("users", "User") user_model = get_user_model()
if user_model.objects.filter(username=username): if user_model.objects.filter(username=username):
return (True, _("Username is already in use.")) return (True, _("Username is already in use."))
@ -110,7 +111,7 @@ def public_register(username:str, password:str, email:str, full_name:str):
if is_registered: if is_registered:
raise exc.WrongArguments(reason) raise exc.WrongArguments(reason)
user_model = apps.get_model("users", "User") user_model = get_user_model()
user = user_model(username=username, user = user_model(username=username,
email=email, email=email,
full_name=full_name) full_name=full_name)
@ -159,7 +160,7 @@ def private_register_for_new_user(token:str, username:str, email:str,
if is_registered: if is_registered:
raise exc.WrongArguments(reason) raise exc.WrongArguments(reason)
user_model = apps.get_model("users", "User") user_model = get_user_model()
user = user_model(username=username, user = user_model(username=username,
email=email, email=email,
full_name=full_name) full_name=full_name)

View File

@ -14,7 +14,7 @@
# #
# 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.contrib.auth import get_user_model
from taiga.base import exceptions as exc from taiga.base import exceptions as exc
from django.apps import apps from django.apps import apps
@ -47,7 +47,7 @@ def get_user_for_token(token, scope, max_age=None):
except signing.BadSignature: except signing.BadSignature:
raise exc.NotAuthenticated(_("Invalid token")) raise exc.NotAuthenticated(_("Invalid token"))
model_cls = apps.get_model("users", "User") model_cls = get_user_model()
try: try:
user = model_cls.objects.get(pk=data["user_%s_id" % (scope)]) user = model_cls.objects.get(pk=data["user_%s_id" % (scope)])

View File

@ -20,6 +20,7 @@ import datetime
from optparse import make_option from optparse import make_option
from django.apps import apps from django.apps import apps
from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.utils import timezone from django.utils import timezone
@ -28,7 +29,6 @@ from taiga.base.mails import mail_builder
from taiga.projects.models import Project, Membership from taiga.projects.models import Project, Membership
from taiga.projects.history.models import HistoryEntry from taiga.projects.history.models import HistoryEntry
from taiga.projects.history.services import get_history_queryset_by_model_instance from taiga.projects.history.services import get_history_queryset_by_model_instance
from taiga.users.models import User
class Command(BaseCommand): class Command(BaseCommand):
@ -50,7 +50,7 @@ class Command(BaseCommand):
# Register email # Register email
context = {"lang": locale, context = {"lang": locale,
"user": User.objects.all().order_by("?").first(), "user": get_user_model().objects.all().order_by("?").first(),
"cancel_token": "cancel-token"} "cancel_token": "cancel-token"}
email = mail_builder.registered_user(test_email, context) email = mail_builder.registered_user(test_email, context)
@ -58,7 +58,7 @@ class Command(BaseCommand):
# Membership invitation # Membership invitation
membership = Membership.objects.order_by("?").filter(user__isnull=True).first() membership = Membership.objects.order_by("?").filter(user__isnull=True).first()
membership.invited_by = User.objects.all().order_by("?").first() membership.invited_by = get_user_model().objects.all().order_by("?").first()
membership.invitation_extra_text = "Text example, Text example,\nText example,\n\nText example" membership.invitation_extra_text = "Text example, Text example,\nText example,\n\nText example"
context = {"lang": locale, "membership": membership} context = {"lang": locale, "membership": membership}
@ -88,19 +88,19 @@ class Command(BaseCommand):
email.send() email.send()
# Password recovery # Password recovery
context = {"lang": locale, "user": User.objects.all().order_by("?").first()} context = {"lang": locale, "user": get_user_model().objects.all().order_by("?").first()}
email = mail_builder.password_recovery(test_email, context) email = mail_builder.password_recovery(test_email, context)
email.send() email.send()
# Change email # Change email
context = {"lang": locale, "user": User.objects.all().order_by("?").first()} context = {"lang": locale, "user": get_user_model().objects.all().order_by("?").first()}
email = mail_builder.change_email(test_email, context) email = mail_builder.change_email(test_email, context)
email.send() email.send()
# Export/Import emails # Export/Import emails
context = { context = {
"lang": locale, "lang": locale,
"user": User.objects.all().order_by("?").first(), "user": get_user_model().objects.all().order_by("?").first(),
"project": Project.objects.all().order_by("?").first(), "project": Project.objects.all().order_by("?").first(),
"error_subject": "Error generating project dump", "error_subject": "Error generating project dump",
"error_message": "Error generating project dump", "error_message": "Error generating project dump",
@ -109,7 +109,7 @@ class Command(BaseCommand):
email.send() email.send()
context = { context = {
"lang": locale, "lang": locale,
"user": User.objects.all().order_by("?").first(), "user": get_user_model().objects.all().order_by("?").first(),
"error_subject": "Error importing project dump", "error_subject": "Error importing project dump",
"error_message": "Error importing project dump", "error_message": "Error importing project dump",
} }
@ -120,7 +120,7 @@ class Command(BaseCommand):
context = { context = {
"lang": locale, "lang": locale,
"url": "http://dummyurl.com", "url": "http://dummyurl.com",
"user": User.objects.all().order_by("?").first(), "user": get_user_model().objects.all().order_by("?").first(),
"project": Project.objects.all().order_by("?").first(), "project": Project.objects.all().order_by("?").first(),
"deletion_date": deletion_date, "deletion_date": deletion_date,
} }
@ -129,7 +129,7 @@ class Command(BaseCommand):
context = { context = {
"lang": locale, "lang": locale,
"user": User.objects.all().order_by("?").first(), "user": get_user_model().objects.all().order_by("?").first(),
"project": Project.objects.all().order_by("?").first(), "project": Project.objects.all().order_by("?").first(),
} }
email = mail_builder.load_dump(test_email, context) email = mail_builder.load_dump(test_email, context)
@ -157,9 +157,9 @@ class Command(BaseCommand):
context = { context = {
"lang": locale, "lang": locale,
"project": Project.objects.all().order_by("?").first(), "project": Project.objects.all().order_by("?").first(),
"changer": User.objects.all().order_by("?").first(), "changer": get_user_model().objects.all().order_by("?").first(),
"history_entries": HistoryEntry.objects.all().order_by("?")[0:5], "history_entries": HistoryEntry.objects.all().order_by("?")[0:5],
"user": User.objects.all().order_by("?").first(), "user": get_user_model().objects.all().order_by("?").first(),
} }
for notification_email in notification_emails: for notification_email in notification_emails:

View File

@ -21,6 +21,7 @@ import os
from collections import OrderedDict from collections import OrderedDict
from django.apps import apps from django.apps import apps
from django.contrib.auth import get_user_model
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
@ -263,7 +264,7 @@ class WatcheableObjectModelSerializer(serializers.ModelSerializer):
adding_watcher_emails = list(new_watcher_emails.difference(old_watcher_emails)) adding_watcher_emails = list(new_watcher_emails.difference(old_watcher_emails))
removing_watcher_emails = list(old_watcher_emails.difference(new_watcher_emails)) removing_watcher_emails = list(old_watcher_emails.difference(new_watcher_emails))
User = apps.get_model("users", "User") User = get_user_model()
adding_users = User.objects.filter(email__in=adding_watcher_emails) adding_users = User.objects.filter(email__in=adding_watcher_emails)
removing_users = User.objects.filter(email__in=removing_watcher_emails) removing_users = User.objects.filter(email__in=removing_watcher_emails)

View File

@ -16,6 +16,7 @@
# 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.apps import apps
from django.contrib.auth import get_user_model
from taiga.front.templatetags.functions import resolve from taiga.front.templatetags.functions import resolve
@ -24,7 +25,7 @@ from .base import Sitemap
class UsersSitemap(Sitemap): class UsersSitemap(Sitemap):
def items(self): def items(self):
user_model = apps.get_model("users", "User") user_model = get_user_model()
# Only active users and not system users # Only active users and not system users
queryset = user_model.objects.filter(is_active=True, queryset = user_model.objects.filter(is_active=True,

View File

@ -17,10 +17,10 @@
import uuid import uuid
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.conf import settings from django.conf import settings
from taiga.users.models import User
from taiga.base.utils.urls import get_absolute_url from taiga.base.utils.urls import get_absolute_url
@ -43,4 +43,4 @@ def get_or_generate_config(project):
def get_bitbucket_user(user_id): def get_bitbucket_user(user_id):
return User.objects.get(is_system=True, username__startswith="bitbucket") return get_user_model().objects.get(is_system=True, username__startswith="bitbucket")

View File

@ -17,9 +17,9 @@
import uuid import uuid
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from taiga.users.models import User
from taiga.users.models import AuthData from taiga.users.models import AuthData
from taiga.base.utils.urls import get_absolute_url from taiga.base.utils.urls import get_absolute_url
@ -49,6 +49,6 @@ def get_github_user(github_id):
pass pass
if user is None: if user is None:
user = User.objects.get(is_system=True, username__startswith="github") user = get_user_model().objects.get(is_system=True, username__startswith="github")
return user return user

View File

@ -17,10 +17,10 @@
import uuid import uuid
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.conf import settings from django.conf import settings
from taiga.users.models import User
from taiga.base.utils.urls import get_absolute_url from taiga.base.utils.urls import get_absolute_url
@ -47,11 +47,11 @@ def get_gitlab_user(user_email):
if user_email: if user_email:
try: try:
user = User.objects.get(email=user_email) user = get_user_model().objects.get(email=user_email)
except User.DoesNotExist: except get_user_model().DoesNotExist:
pass pass
if user is None: if user is None:
user = User.objects.get(is_system=True, username__startswith="gitlab") user = get_user_model().objects.get(is_system=True, username__startswith="gitlab")
return user return user

View File

@ -22,13 +22,12 @@
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE. # THE SOFTWARE.
from django.contrib.auth import get_user_model
from markdown.extensions import Extension from markdown.extensions import Extension
from markdown.inlinepatterns import Pattern from markdown.inlinepatterns import Pattern
from markdown.util import etree, AtomicString from markdown.util import etree, AtomicString
from taiga.users.models import User
class MentionsExtension(Extension): class MentionsExtension(Extension):
def extendMarkdown(self, md, md_globals): def extendMarkdown(self, md, md_globals):
@ -43,8 +42,8 @@ class MentionsPattern(Pattern):
username = m.group(3) username = m.group(3)
try: try:
user = User.objects.get(username=username) user = get_user_model().objects.get(username=username)
except User.DoesNotExist: except get_user_model().DoesNotExist:
return "@{}".format(username) return "@{}".format(username)
url = "/profile/{}".format(username) url = "/profile/{}".format(username)

View File

@ -19,10 +19,10 @@ from contextlib import suppress
from functools import partial from functools import partial
from django.apps import apps from django.apps import apps
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from taiga.base.utils.urls import get_absolute_url
from taiga.base.utils.iterators import as_tuple from taiga.base.utils.iterators import as_tuple
from taiga.base.utils.iterators import as_dict from taiga.base.utils.iterators import as_dict
from taiga.mdrender.service import render as mdrender from taiga.mdrender.service import render as mdrender
@ -49,7 +49,7 @@ def _get_generic_values(ids:tuple, *, typename=None, attr:str="name") -> tuple:
@as_dict @as_dict
def _get_users_values(ids:set) -> dict: def _get_users_values(ids:set) -> dict:
user_model = apps.get_model("users", "User") user_model = get_user_model()
ids = filter(lambda x: x is not None, ids) ids = filter(lambda x: x is not None, ids)
qs = user_model.objects.filter(pk__in=tuple(ids)) qs = user_model.objects.filter(pk__in=tuple(ids))

View File

@ -14,12 +14,10 @@
import uuid import uuid
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone from django.utils import timezone
from django.db import models from django.db import models
from django.apps import apps from django.contrib.auth import get_user_model
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.conf import settings
from django_pgjson.fields import JsonField from django_pgjson.fields import JsonField
from taiga.mdrender.service import get_diff_of_htmls from taiga.mdrender.service import get_diff_of_htmls
@ -96,7 +94,7 @@ class HistoryEntry(models.Model):
@cached_property @cached_property
def owner(self): def owner(self):
pk = self.user["pk"] pk = self.user["pk"]
model = apps.get_model("users", "User") model = get_user_model()
try: try:
return model.objects.get(pk=pk) return model.objects.get(pk=pk)
except model.DoesNotExist: except model.DoesNotExist:

View File

@ -16,25 +16,21 @@
# 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.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.db.models import Q
from django.http import HttpResponse from django.http import HttpResponse
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
from taiga.base.decorators import detail_route, list_route from taiga.base.decorators import list_route
from taiga.base.api import ModelCrudViewSet, ModelListViewSet from taiga.base.api import ModelCrudViewSet, ModelListViewSet
from taiga.base.api.mixins import BlockedByProjectMixin from taiga.base.api.mixins import BlockedByProjectMixin
from taiga.base.api.utils import get_object_or_404 from taiga.base.api.utils import get_object_or_404
from taiga.users.models import User
from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin
from taiga.projects.occ import OCCResourceMixin from taiga.projects.occ import OCCResourceMixin
from taiga.projects.history.mixins import HistoryResourceMixin from taiga.projects.history.mixins import HistoryResourceMixin
from taiga.projects.models import Project, IssueStatus, Severity, Priority, IssueType from taiga.projects.models import Project, IssueStatus, Severity, Priority, IssueType
from taiga.projects.milestones.models import Milestone
from taiga.projects.votes.mixins.viewsets import VotedResourceMixin, VotersViewSetMixin from taiga.projects.votes.mixins.viewsets import VotedResourceMixin, VotersViewSetMixin
from . import models from . import models

View File

@ -16,16 +16,14 @@
# 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 taiga.base.api import serializers from django.contrib.auth import get_user_model
from taiga.base.fields import TagsField
from taiga.users.models import User from taiga.base.api import serializers
from taiga.users.services import get_photo_or_gravatar_url
class FanSerializer(serializers.ModelSerializer): class FanSerializer(serializers.ModelSerializer):
full_name = serializers.CharField(source='get_full_name', required=False) full_name = serializers.CharField(source='get_full_name', required=False)
class Meta: class Meta:
model = User model = get_user_model()
fields = ('id', 'username', 'full_name') fields = ('id', 'username', 'full_name')

View File

@ -14,15 +14,11 @@
# #
# 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 functools import partial from functools import partial
from operator import is_not from operator import is_not
from django.apps import apps from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.utils.translation import ugettext_lazy as _
from taiga.base import response from taiga.base import response
from taiga.base.decorators import detail_route from taiga.base.decorators import detail_route
@ -34,8 +30,6 @@ from taiga.projects.notifications.utils import (attach_watchers_to_queryset,
attach_is_watcher_to_queryset, attach_is_watcher_to_queryset,
attach_total_watchers_to_queryset) attach_total_watchers_to_queryset)
from taiga.users.models import User
from . import models
from . serializers import WatcherSerializer from . serializers import WatcherSerializer
@ -224,9 +218,9 @@ class EditableWatchedResourceModelSerializer(WatchedResourceModelSerializer):
adding_watcher_ids = list(new_watcher_ids.difference(old_watcher_ids)) adding_watcher_ids = list(new_watcher_ids.difference(old_watcher_ids))
removing_watcher_ids = list(old_watcher_ids.difference(new_watcher_ids)) removing_watcher_ids = list(old_watcher_ids.difference(new_watcher_ids))
User = apps.get_model("users", "User") User = get_user_model()
adding_users = User.objects.filter(id__in=adding_watcher_ids) adding_users = get_user_model().objects.filter(id__in=adding_watcher_ids)
removing_users = User.objects.filter(id__in=removing_watcher_ids) removing_users = get_user_model().objects.filter(id__in=removing_watcher_ids)
for user in adding_users: for user in adding_users:
services.add_watcher(obj, user) services.add_watcher(obj, user)

View File

@ -33,7 +33,7 @@ class NotifyPolicy(models.Model):
project user notifications preference. project user notifications preference.
""" """
project = models.ForeignKey("projects.Project", related_name="notify_policies") project = models.ForeignKey("projects.Project", related_name="notify_policies")
user = models.ForeignKey("users.User", related_name="notify_policies") user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="notify_policies")
notify_level = models.SmallIntegerField(choices=NOTIFY_LEVEL_CHOICES) notify_level = models.SmallIntegerField(choices=NOTIFY_LEVEL_CHOICES)
created_at = models.DateTimeField(default=timezone.now) created_at = models.DateTimeField(default=timezone.now)
@ -57,7 +57,7 @@ class HistoryChangeNotification(models.Model):
or updated when an object requires notifications. or updated when an object requires notifications.
""" """
key = models.CharField(max_length=255, unique=False, editable=False) key = models.CharField(max_length=255, unique=False, editable=False)
owner = models.ForeignKey("users.User", null=False, blank=False, owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=False, blank=False,
verbose_name=_("owner"), related_name="+") verbose_name=_("owner"), related_name="+")
created_datetime = models.DateTimeField(null=False, blank=False, auto_now_add=True, created_datetime = models.DateTimeField(null=False, blank=False, auto_now_add=True,
verbose_name=_("created date time")) verbose_name=_("created date time"))
@ -66,7 +66,7 @@ class HistoryChangeNotification(models.Model):
history_entries = models.ManyToManyField("history.HistoryEntry", history_entries = models.ManyToManyField("history.HistoryEntry",
verbose_name=_("history entries"), verbose_name=_("history entries"),
related_name="+") related_name="+")
notify_users = models.ManyToManyField("users.User", notify_users = models.ManyToManyField(settings.AUTH_USER_MODEL,
verbose_name=_("notify users"), verbose_name=_("notify users"),
related_name="+") related_name="+")
project = models.ForeignKey("projects.Project", null=False, blank=False, project = models.ForeignKey("projects.Project", null=False, blank=False,

View File

@ -15,13 +15,10 @@
# 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 json
from taiga.base.api import serializers from taiga.base.api import serializers
from taiga.users.models import User from taiga.users.models import get_user_model_safe
from . import models from . import models
from . import choices
class NotifyPolicySerializer(serializers.ModelSerializer): class NotifyPolicySerializer(serializers.ModelSerializer):
@ -39,5 +36,5 @@ class WatcherSerializer(serializers.ModelSerializer):
full_name = serializers.CharField(source='get_full_name', required=False) full_name = serializers.CharField(source='get_full_name', required=False)
class Meta: class Meta:
model = User model = get_user_model_safe()
fields = ('id', 'username', 'full_name') fields = ('id', 'username', 'full_name')

View File

@ -20,7 +20,6 @@ import datetime
from functools import partial from functools import partial
from django.apps import apps from django.apps import apps
from django.db.transaction import atomic
from django.db import IntegrityError, transaction from django.db import IntegrityError, transaction
from django.db.models import Q from django.db.models import Q
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
@ -37,7 +36,6 @@ from taiga.projects.history.services import (make_key_from_model_object,
get_last_snapshot_for_key, get_last_snapshot_for_key,
get_model_from_key) get_model_from_key)
from taiga.permissions.service import user_has_perm from taiga.permissions.service import user_has_perm
from taiga.users.models import User
from .models import HistoryChangeNotification, Watched from .models import HistoryChangeNotification, Watched
@ -214,7 +212,7 @@ def send_notifications(obj, *, history):
return None return None
key = make_key_from_model_object(obj) key = make_key_from_model_object(obj)
owner = User.objects.get(pk=history.user["pk"]) owner = get_user_model().objects.get(pk=history.user["pk"])
notification, created = (HistoryChangeNotification.objects.select_for_update() notification, created = (HistoryChangeNotification.objects.select_for_update()
.get_or_create(key=key, .get_or_create(key=key,
owner=owner, owner=owner,

View File

@ -22,8 +22,6 @@ import datetime
import copy import copy
import collections import collections
from taiga.projects.history.models import HistoryEntry
from taiga.projects.userstories.models import RolePoints
def _count_status_object(status_obj, counting_storage): def _count_status_object(status_obj, counting_storage):
if status_obj.id in counting_storage: if status_obj.id in counting_storage:
@ -228,6 +226,7 @@ def _get_milestones_stats_for_backlog(project, milestones):
def get_stats_for_project(project): def get_stats_for_project(project):
# Let's fetch all the estimations related to a project with all the necesary # Let's fetch all the estimations related to a project with all the necesary
# related data # related data
RolePoints = apps.get_model('userstories', 'RolePoints')
role_points = RolePoints.objects.filter( role_points = RolePoints.objects.filter(
user_story__project = project, user_story__project = project,
).prefetch_related( ).prefetch_related(
@ -378,6 +377,7 @@ def _get_wiki_changes_per_member_stats(project):
# Wiki changes # Wiki changes
wiki_changes = {} wiki_changes = {}
wiki_page_keys = ["wiki.wikipage:%s"%id for id in project.wiki_pages.values_list("id", flat=True)] wiki_page_keys = ["wiki.wikipage:%s"%id for id in project.wiki_pages.values_list("id", flat=True)]
HistoryEntry = apps.get_model('history', 'HistoryEntry')
history_entries = HistoryEntry.objects.filter(key__in=wiki_page_keys).values('user') history_entries = HistoryEntry.objects.filter(key__in=wiki_page_keys).values('user')
for entry in history_entries: for entry in history_entries:
editions = wiki_changes.get(entry["user"]["pk"], 0) editions = wiki_changes.get(entry["user"]["pk"], 0)

View File

@ -16,16 +16,14 @@
# 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 taiga.base.api import serializers from django.contrib.auth import get_user_model
from taiga.base.fields import TagsField
from taiga.users.models import User from taiga.base.api import serializers
from taiga.users.services import get_photo_or_gravatar_url
class VoterSerializer(serializers.ModelSerializer): class VoterSerializer(serializers.ModelSerializer):
full_name = serializers.CharField(source='get_full_name', required=False) full_name = serializers.CharField(source='get_full_name', required=False)
class Meta: class Meta:
model = User model = get_user_model()
fields = ('id', 'username', 'full_name') fields = ('id', 'username', 'full_name')

View File

@ -14,6 +14,7 @@
from django.apps import apps from django.apps import apps
from django.contrib.auth import get_user_model
from django.db.models import Count from django.db.models import Count
from django.db.models import Q from django.db.models import Q
from django.utils import timezone from django.utils import timezone
@ -27,7 +28,7 @@ from collections import OrderedDict
########################################################################### ###########################################################################
def get_users_public_stats(): def get_users_public_stats():
model = apps.get_model("users", "User") model = get_user_model()
queryset = model.objects.filter(is_active=True, is_system=False) queryset = model.objects.filter(is_active=True, is_system=False)
stats = OrderedDict() stats = OrderedDict()

View File

@ -14,7 +14,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.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.apps import apps from django.apps import apps
@ -49,7 +50,7 @@ class TimelineViewSet(ReadOnlyListViewSet):
page = self.paginate_queryset(queryset) page = self.paginate_queryset(queryset)
if page is not None: if page is not None:
user_ids = list(set([obj.data.get("user", {}).get("id", None) for obj in page.object_list])) user_ids = list(set([obj.data.get("user", {}).get("id", None) for obj in page.object_list]))
User = apps.get_model("users", "User") User = get_user_model()
users = {u.id: u for u in User.objects.filter(id__in=user_ids)} users = {u.id: u for u in User.objects.filter(id__in=user_ids)}
for obj in page.object_list: for obj in page.object_list:
@ -99,7 +100,7 @@ class TimelineViewSet(ReadOnlyListViewSet):
class ProfileTimeline(TimelineViewSet): class ProfileTimeline(TimelineViewSet):
content_type = "users.user" content_type = settings.AUTH_USER_MODEL.lower()
permission_classes = (permissions.UserTimelinePermission,) permission_classes = (permissions.UserTimelinePermission,)
def get_timeline(self, user): def get_timeline(self, user):
@ -107,7 +108,7 @@ class ProfileTimeline(TimelineViewSet):
class UserTimeline(TimelineViewSet): class UserTimeline(TimelineViewSet):
content_type = "users.user" content_type = settings.AUTH_USER_MODEL.lower()
permission_classes = (permissions.UserTimelinePermission,) permission_classes = (permissions.UserTimelinePermission,)
def get_timeline(self, user): def get_timeline(self, user):

View File

@ -17,6 +17,7 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.apps import apps from django.apps import apps
from django.contrib.auth import get_user_model
from django.db.models import signals from django.db.models import signals
@ -35,4 +36,4 @@ class TimelineAppConfig(AppConfig):
signals.post_delete.connect(handlers.delete_membership_push_to_timeline, signals.post_delete.connect(handlers.delete_membership_push_to_timeline,
sender=apps.get_model("projects", "Membership")) sender=apps.get_model("projects", "Membership"))
signals.post_save.connect(handlers.create_user_push_to_timeline, signals.post_save.connect(handlers.create_user_push_to_timeline,
sender=apps.get_model("users", "User")) sender=get_user_model())

View File

@ -18,24 +18,22 @@
# Examples: # Examples:
# python manage.py rebuild_timeline_for_user_creation --settings=settings.local_timeline # python manage.py rebuild_timeline_for_user_creation --settings=settings.local_timeline
from django.conf import settings from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.db.models import Model from django.db.models import Model
from django.db import reset_queries
from django.test.utils import override_settings from django.test.utils import override_settings
from taiga.timeline.service import (_get_impl_key_from_model, from taiga.timeline.service import (_get_impl_key_from_model,
_timeline_impl_map, extract_user_info) _timeline_impl_map, extract_user_info)
from taiga.timeline.models import Timeline from taiga.timeline.models import Timeline
from taiga.timeline.signals import _push_to_timelines from taiga.timeline.signals import _push_to_timelines
from taiga.users.models import User
from unittest.mock import patch from unittest.mock import patch
import gc import gc
class BulkCreator(object): class BulkCreator(object):
def __init__(self): def __init__(self):
self.timeline_objects = [] self.timeline_objects = []
@ -75,7 +73,7 @@ def custom_add_to_object_timeline(obj:object, instance:object, event_type:str, c
def generate_timeline(): def generate_timeline():
with patch('taiga.timeline.service._add_to_object_timeline', new=custom_add_to_object_timeline): with patch('taiga.timeline.service._add_to_object_timeline', new=custom_add_to_object_timeline):
# Users api wasn't a HistoryResourceMixin so we can't interate on the HistoryEntries in this case # Users api wasn't a HistoryResourceMixin so we can't interate on the HistoryEntries in this case
users = User.objects.order_by("date_joined") users = get_user_model().objects.order_by("date_joined")
for user in users.iterator(): for user in users.iterator():
print("User:", user.date_joined) print("User:", user.date_joined)
extra_data = { extra_data = {
@ -87,6 +85,7 @@ def generate_timeline():
bulk_creator.flush() bulk_creator.flush()
class Command(BaseCommand): class Command(BaseCommand):
help = 'Regenerate project timeline' help = 'Regenerate project timeline'

View File

@ -20,24 +20,17 @@
# python manage.py rebuild_timeline --settings=settings.local_timeline --purge # python manage.py rebuild_timeline --settings=settings.local_timeline --purge
# python manage.py rebuild_timeline --settings=settings.local_timeline --initial_date 2014-10-02 # python manage.py rebuild_timeline --settings=settings.local_timeline --initial_date 2014-10-02
from django.conf import settings
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.db.models import Model from django.db.models import Model
from django.db import reset_queries
from django.test.utils import override_settings from django.test.utils import override_settings
from taiga.projects.models import Project from taiga.projects.models import Project
from taiga.projects.history import services as history_services
from taiga.projects.history.choices import HistoryType
from taiga.projects.history.models import HistoryEntry from taiga.projects.history.models import HistoryEntry
from taiga.timeline.models import Timeline from taiga.timeline.models import Timeline
from taiga.timeline.service import (_add_to_object_timeline, _get_impl_key_from_model, from taiga.timeline.service import _get_impl_key_from_model,_timeline_impl_map, extract_user_info
_timeline_impl_map, extract_user_info)
from taiga.timeline.signals import on_new_history_entry, _push_to_timelines from taiga.timeline.signals import on_new_history_entry, _push_to_timelines
from taiga.users.models import User
from unittest.mock import patch from unittest.mock import patch
from optparse import make_option from optparse import make_option

View File

@ -16,6 +16,7 @@
# 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.apps import apps
from django.contrib.auth import get_user_model
from django.forms import widgets from django.forms import widgets
from taiga.base.api import serializers from taiga.base.api import serializers
@ -37,7 +38,7 @@ class TimelineSerializer(serializers.ModelSerializer):
if hasattr(obj, "_prefetched_user"): if hasattr(obj, "_prefetched_user"):
user = obj._prefetched_user user = obj._prefetched_user
else: else:
User = apps.get_model("users", "User") User = get_user_model()
userData = obj.data.get("user", None) userData = obj.data.get("user", None)
try: try:
user = User.objects.get(id=userData["id"]) user = User.objects.get(id=userData["id"])

View File

@ -16,14 +16,12 @@
# 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.conf import settings from django.conf import settings
from django.contrib.auth import get_user_model
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from taiga.projects.history import services as history_services from taiga.projects.history import services as history_services
from taiga.projects.models import Project
from taiga.users.models import User
from taiga.projects.history.choices import HistoryType from taiga.projects.history.choices import HistoryType
from taiga.projects.notifications import services as notifications_services
from taiga.timeline.service import (push_to_timeline, from taiga.timeline.service import (push_to_timeline,
build_user_namespace, build_user_namespace,
build_project_namespace, build_project_namespace,
@ -93,7 +91,7 @@ def on_new_history_entry(sender, instance, created, **kwargs):
elif instance.type == HistoryType.delete: elif instance.type == HistoryType.delete:
event_type = "delete" event_type = "delete"
user = User.objects.get(id=instance.user["pk"]) user = get_user_model().objects.get(id=instance.user["pk"])
values_diff = instance.values_diff values_diff = instance.values_diff
_clean_description_fields(values_diff) _clean_description_fields(values_diff)

View File

@ -15,19 +15,22 @@
# 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 importlib import import_module
import random import random
import re import re
import uuid
from django.apps import apps from django.apps import apps
from django.apps.config import MODELS_MODULE_NAME
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import UserManager, AbstractBaseUser
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core import validators
from django.core.exceptions import AppRegistryNotReady
from django.db import models from django.db import models
from django.dispatch import receiver from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import UserManager, AbstractBaseUser
from django.core import validators
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from django_pgjson.fields import JsonField from django_pgjson.fields import JsonField
from djorm_pgarray.fields import TextArrayField from djorm_pgarray.fields import TextArrayField
@ -39,7 +42,42 @@ from taiga.permissions.permissions import MEMBERS_PERMISSIONS
from taiga.projects.choices import BLOCKED_BY_OWNER_LEAVING from taiga.projects.choices import BLOCKED_BY_OWNER_LEAVING
from taiga.projects.notifications.choices import NotifyLevel from taiga.projects.notifications.choices import NotifyLevel
from easy_thumbnails.files import get_thumbnailer
def get_user_model_safe():
"""
Fetches the user model using the app registry.
This doesn't require that an app with the given app label exists,
which makes it safe to call when the registry is being populated.
All other methods to access models might raise an exception about the
registry not being ready yet.
Raises LookupError if model isn't found.
Based on: https://github.com/django-oscar/django-oscar/blob/1.0/oscar/core/loading.py#L310-L340
Ongoing Django issue: https://code.djangoproject.com/ticket/22872
"""
user_app, user_model = settings.AUTH_USER_MODEL.split('.')
try:
return apps.get_model(user_app, user_model)
except AppRegistryNotReady:
if apps.apps_ready and not apps.models_ready:
# If this function is called while `apps.populate()` is
# loading models, ensure that the module that defines the
# target model has been imported and try looking the model up
# in the app registry. This effectively emulates
# `from path.to.app.models import Model` where we use
# `Model = get_model('app', 'Model')` instead.
app_config = apps.get_app_config(user_app)
# `app_config.import_models()` cannot be used here because it
# would interfere with `apps.populate()`.
import_module('%s.%s' % (app_config.name, MODELS_MODULE_NAME))
# In order to account for case-insensitivity of model_name,
# look up the model through a private API of the app registry.
return apps.get_registered_model(user_app, user_model)
else:
# This must be a different case (e.g. the model really doesn't
# exist). We just re-raise the exception.
raise
def generate_random_hex_color(): def generate_random_hex_color():
@ -281,7 +319,7 @@ class Role(models.Model):
class AuthData(models.Model): class AuthData(models.Model):
user = models.ForeignKey("users.User", related_name="auth_data") user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="auth_data")
key = models.SlugField(max_length=50) key = models.SlugField(max_length=50)
value = models.CharField(max_length=300) value = models.CharField(max_length=300)
extra = JsonField() extra = JsonField()

View File

@ -20,6 +20,7 @@ This model contains a domain logic for users application.
""" """
from django.apps import apps from django.apps import apps
from django.contrib.auth import get_user_model
from django.db.models import Q from django.db.models import Q
from django.db import connection from django.db import connection
from django.conf import settings from django.conf import settings
@ -40,7 +41,7 @@ from .gravatar import get_gravatar_url
def get_user_by_username_or_email(username_or_email): def get_user_by_username_or_email(username_or_email):
user_model = apps.get_model("users", "User") user_model = get_user_model()
qs = user_model.objects.filter(Q(username__iexact=username_or_email) | qs = user_model.objects.filter(Q(username__iexact=username_or_email) |
Q(email__iexact=username_or_email)) Q(email__iexact=username_or_email))

View File

@ -27,8 +27,10 @@ def connect_webhooks_signals():
dispatch_uid="webhooks") dispatch_uid="webhooks")
def disconnect_webhooks_signals(): def disconnect_webhooks_signals():
signals.post_save.disconnect(sender=HistoryEntry, dispatch_uid="webhooks") signals.post_save.disconnect(sender=apps.get_model("history", "HistoryEntry"), dispatch_uid="webhooks")
class WebhooksAppConfig(AppConfig): class WebhooksAppConfig(AppConfig):

View File

@ -165,7 +165,7 @@ class WikiAttachmentFactory(Factory):
class UserFactory(Factory): class UserFactory(Factory):
class Meta: class Meta:
model = "users.User" model = settings.AUTH_USER_MODEL
strategy = factory.CREATE_STRATEGY strategy = factory.CREATE_STRATEGY
username = factory.Sequence(lambda n: "user{}".format(n)) username = factory.Sequence(lambda n: "user{}".format(n))

View File

@ -132,7 +132,6 @@ def test_valid_project_with_enough_public_projects_slots(client):
client.login(user) client.login(user)
response = client.json.post(url, json.dumps(data)) response = client.json.post(url, json.dumps(data))
print(response.content)
assert response.status_code == 201 assert response.status_code == 201
assert Project.objects.filter(slug="public-project-with-slots").count() == 1 assert Project.objects.filter(slug="public-project-with-slots").count() == 1

View File

@ -109,7 +109,6 @@ def test_get_task_is_watcher(client):
assert response.data['is_watcher'] == False assert response.data['is_watcher'] == False
response = client.post(url_watch) response = client.post(url_watch)
print(response.data)
assert response.status_code == 200 assert response.status_code == 200
response = client.get(url_detail) response = client.get(url_detail)

View File

@ -16,9 +16,9 @@
# 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 taiga.projects.models import Project from django.contrib.auth import get_user_model
from taiga.users.models import User
from taiga.projects.models import Project
from taiga.base.utils.slug import slugify from taiga.base.utils.slug import slugify
import pytest import pytest
@ -38,7 +38,7 @@ def test_slugify_3():
def test_project_slug_with_special_chars(): def test_project_slug_with_special_chars():
user = User.objects.create(username="test") user = get_user_model().objects.create(username="test")
project = Project.objects.create(name="漢字", description="漢字", owner=user) project = Project.objects.create(name="漢字", description="漢字", owner=user)
project.save() project.save()
@ -46,7 +46,7 @@ def test_project_slug_with_special_chars():
def test_project_with_existing_name_slug_with_special_chars(): def test_project_with_existing_name_slug_with_special_chars():
user = User.objects.create(username="test") user = get_user_model().objects.create(username="test")
Project.objects.create(name="漢字", description="漢字", owner=user) Project.objects.create(name="漢字", description="漢字", owner=user)
project = Project.objects.create(name="漢字", description="漢字", owner=user) project = Project.objects.create(name="漢字", description="漢字", owner=user)

View File

@ -18,19 +18,18 @@
from unittest.mock import patch, call from unittest.mock import patch, call
from django.core.exceptions import ValidationError from django.contrib.auth import get_user_model
from taiga.timeline import service from taiga.timeline import service
from taiga.timeline.models import Timeline from taiga.timeline.models import Timeline
from taiga.projects.models import Project from taiga.projects.models import Project
from taiga.users.models import User
import pytest import pytest
def test_push_to_timeline_many_objects(): def test_push_to_timeline_many_objects():
with patch("taiga.timeline.service._add_to_object_timeline") as mock: with patch("taiga.timeline.service._add_to_object_timeline") as mock:
users = [User(), User(), User()] users = [get_user_model(), get_user_model(), get_user_model()]
project = Project() project = Project()
service.push_to_timeline(users, project, "test", project.created_date) service.push_to_timeline(users, project, "test", project.created_date)
assert mock.call_count == 3 assert mock.call_count == 3
@ -45,7 +44,7 @@ def test_push_to_timeline_many_objects():
def test_add_to_objects_timeline(): def test_add_to_objects_timeline():
with patch("taiga.timeline.service._add_to_object_timeline") as mock: with patch("taiga.timeline.service._add_to_object_timeline") as mock:
users = [User(), User(), User()] users = [get_user_model(), get_user_model(), get_user_model()]
project = Project() project = Project()
service._add_to_objects_timeline(users, project, "test", project.created_date) service._add_to_objects_timeline(users, project, "test", project.created_date)
assert mock.call_count == 3 assert mock.call_count == 3