diff --git a/readthedocs/analytics/models.py b/readthedocs/analytics/models.py index 455a3eeb5d1..4871ce56fdf 100644 --- a/readthedocs/analytics/models.py +++ b/readthedocs/analytics/models.py @@ -102,9 +102,6 @@ class Meta: ), ] - def __str__(self): - return f"PageView: [{self.project.slug}] - {self.full_path or self.path} for {self.date}" - @classmethod def top_viewed_pages( cls, project, since=None, limit=10, status=200, per_version=False diff --git a/readthedocs/builds/models.py b/readthedocs/builds/models.py index c6cef2b9587..bcadba3cdb8 100644 --- a/readthedocs/builds/models.py +++ b/readthedocs/builds/models.py @@ -11,7 +11,6 @@ from django.db import models from django.urls import reverse from django.utils import timezone -from django.utils.translation import gettext from django.utils.translation import gettext_lazy as _ from django_extensions.db.models import TimeStampedModel from polymorphic.models import PolymorphicModel @@ -210,13 +209,7 @@ class Meta: ordering = ["-verbose_name"] def __str__(self): - return gettext( - "Version {version} of {project} ({pk})".format( - version=self.verbose_name, - project=self.project, - pk=self.pk, - ), - ) + return self.verbose_name @property def is_private(self): @@ -940,17 +933,6 @@ def save(self, *args, **kwargs): # noqa super().save(*args, **kwargs) self._config_changed = False - def __str__(self): - return gettext( - "Build {project} for {usernames} ({pk})".format( - project=self.project, - usernames=" ".join( - self.project.users.all().values_list("username", flat=True), - ), - pk=self.pk, - ), - ) - def get_absolute_url(self): return reverse("builds_detail", args=[self.project.slug, self.pk]) @@ -1159,11 +1141,6 @@ class Meta: objects = RelatedBuildQuerySet.as_manager() - def __str__(self): - return gettext("Build command {pk} for build {build}").format( - pk=self.pk, build=self.build - ) - @property def run_time(self): """Total command runtime in seconds.""" @@ -1347,11 +1324,7 @@ def get_edit_url(self): def __str__(self): class_name = self.__class__.__name__ - return ( - f"({self.priority}) " - f"{class_name}/{self.get_action_display()} " - f"for {self.project.slug}:{self.get_version_type_display()}" - ) + return f"({self.priority}) {class_name}/{self.get_action_display()}" class RegexAutomationRule(VersionAutomationRule): diff --git a/readthedocs/builds/querysets.py b/readthedocs/builds/querysets.py index 3ab46436171..006185cca76 100644 --- a/readthedocs/builds/querysets.py +++ b/readthedocs/builds/querysets.py @@ -13,6 +13,7 @@ EXTERNAL, ) from readthedocs.core.permissions import AdminPermission +from readthedocs.core.querysets import NoReprQuerySet from readthedocs.core.utils.extend import SettingsOverrideObject from readthedocs.projects import constants from readthedocs.projects.models import Project @@ -23,7 +24,7 @@ __all__ = ["VersionQuerySet", "BuildQuerySet", "RelatedBuildQuerySet"] -class VersionQuerySetBase(models.QuerySet): +class VersionQuerySetBase(NoReprQuerySet, models.QuerySet): """Versions take into account their own privacy_level setting.""" @@ -145,7 +146,7 @@ class VersionQuerySet(SettingsOverrideObject): _default_class = VersionQuerySetBase -class BuildQuerySet(models.QuerySet): +class BuildQuerySet(NoReprQuerySet, models.QuerySet): """ Build objects that are privacy aware. @@ -269,7 +270,7 @@ def concurrent(self, project): return (limit_reached, concurrent, max_concurrent) -class RelatedBuildQuerySet(models.QuerySet): +class RelatedBuildQuerySet(NoReprQuerySet, models.QuerySet): """ For models with association to a project through :py:class:`Build`. diff --git a/readthedocs/core/models.py b/readthedocs/core/models.py index 5cab8ae43f1..29e286377a8 100644 --- a/readthedocs/core/models.py +++ b/readthedocs/core/models.py @@ -4,7 +4,6 @@ from django.contrib.auth.models import User from django.db import models from django.urls import reverse -from django.utils.translation import gettext from django.utils.translation import gettext_lazy as _ from django_extensions.db.models import TimeStampedModel from simple_history import register @@ -46,9 +45,6 @@ class UserProfile(TimeStampedModel): # Model history history = ExtraHistoricalRecords() - def __str__(self): - return gettext("%(username)s's profile") % {"username": self.user.username} - def get_absolute_url(self): return reverse( "profiles_profile_detail", diff --git a/readthedocs/core/querysets.py b/readthedocs/core/querysets.py new file mode 100644 index 00000000000..a7d4deaa107 --- /dev/null +++ b/readthedocs/core/querysets.py @@ -0,0 +1,15 @@ +class NoReprQuerySet: + + """ + Basic queryset to override `__repr__` function due to logging issues. + + This may be a temporary solution for now and it can be improved to detect + if we are under DEBUG and/or on an interactive shell. + + https://github.com/readthedocs/readthedocs.org/issues/10954 + https://github.com/readthedocs/readthedocs.org/issues/10954#issuecomment-2057596044 + https://github.com/readthedocs/readthedocs.org/issues/10954#issuecomment-2057951300 + """ + + def __repr__(self): + return self.__class__.__name__ diff --git a/readthedocs/integrations/models.py b/readthedocs/integrations/models.py index 06a1d153f73..f360760d9df 100644 --- a/readthedocs/integrations/models.py +++ b/readthedocs/integrations/models.py @@ -309,9 +309,7 @@ class Integration(TimeStampedModel): has_sync = False def __str__(self): - return _("{0} for {1}").format( - self.get_integration_type_display(), self.project.name - ) + return self.get_integration_type_display() def save(self, *args, **kwargs): if not self.secret: diff --git a/readthedocs/invitations/models.py b/readthedocs/invitations/models.py index 83a8aee34ee..7495296ed08 100644 --- a/readthedocs/invitations/models.py +++ b/readthedocs/invitations/models.py @@ -45,10 +45,11 @@ def invite(self, from_user, obj, to_user=None, to_email=None, request=None): """ if not to_user and not to_email: raise ValueError("A user or email must be provided") - fields = dict( - content_type=ContentType.objects.get_for_model(obj), - object_id=obj.pk, - ) + + fields = { + "content_type": ContentType.objects.get_for_model(obj), + "object_id": obj.pk, + } if to_user: fields["to_user"] = to_user else: @@ -241,6 +242,3 @@ def create_audit_log(self, action, request, user=None): user=user, **kwargs, ) - - def __str__(self): - return f"Invitation for {self.username} to join {self.object}" diff --git a/readthedocs/notifications/querysets.py b/readthedocs/notifications/querysets.py index 81a4000963f..354f623cd67 100644 --- a/readthedocs/notifications/querysets.py +++ b/readthedocs/notifications/querysets.py @@ -4,11 +4,12 @@ from django.utils import timezone from readthedocs.core.permissions import AdminPermission +from readthedocs.core.querysets import NoReprQuerySet from .constants import CANCELLED, READ, UNREAD -class NotificationQuerySet(models.QuerySet): +class NotificationQuerySet(NoReprQuerySet, models.QuerySet): def add(self, *args, **kwargs): """ Create a notification without duplicating it. diff --git a/readthedocs/oauth/models.py b/readthedocs/oauth/models.py index 8a9a74ba27a..7e0bb83c41d 100644 --- a/readthedocs/oauth/models.py +++ b/readthedocs/oauth/models.py @@ -61,7 +61,7 @@ class Meta: db_table = "oauth_remoteorganization_2020" def __str__(self): - return "Remote organization: {name}".format(name=self.slug) + return self.slug def get_remote_organization_relation(self, user, social_account): """Return RemoteOrganizationRelation object for the remote organization.""" @@ -96,9 +96,6 @@ class Meta: "account", ) - def __str__(self): - return f"{self.user.username} <-> {self.remote_organization.name}" - class RemoteRepository(TimeStampedModel): @@ -189,7 +186,7 @@ class Meta: db_table = "oauth_remoterepository_2020" def __str__(self): - return "Remote repository: {}".format(self.html_url) + return self.html_url def matches(self, user): """Existing projects connected to this RemoteRepository.""" @@ -250,6 +247,3 @@ class Meta: "remote_repository", "account", ) - - def __str__(self): - return f"{self.user.username} <-> {self.remote_repository.full_name}" diff --git a/readthedocs/oauth/querysets.py b/readthedocs/oauth/querysets.py index 5a2cfb4ef02..6270a70512c 100644 --- a/readthedocs/oauth/querysets.py +++ b/readthedocs/oauth/querysets.py @@ -2,8 +2,10 @@ from django.db import models +from readthedocs.core.querysets import NoReprQuerySet -class RelatedUserQuerySet(models.QuerySet): + +class RelatedUserQuerySet(NoReprQuerySet, models.QuerySet): """For models with relations through :py:class:`User`.""" diff --git a/readthedocs/organizations/models.py b/readthedocs/organizations/models.py index c254f4a6f37..69f7fa80872 100644 --- a/readthedocs/organizations/models.py +++ b/readthedocs/organizations/models.py @@ -214,12 +214,6 @@ class OrganizationOwner(models.Model): on_delete=models.CASCADE, ) - def __str__(self): - return _("{org} owner {owner}").format( - org=self.organization.name, - owner=self.owner.username, - ) - class Team(models.Model): @@ -325,10 +319,7 @@ class Meta: unique_together = ("team", "email") def __str__(self): - return "{email} to {team}".format( - email=self.email, - team=self.team, - ) + return self.email def save(self, *args, **kwargs): hash_ = salted_hmac( @@ -399,16 +390,6 @@ class Meta: objects = TeamMemberManager() - def __str__(self): - state = "" - if self.is_invite: - state = " (pending)" - return "{username} to {team}{state}".format( - username=self.username, - team=self.team, - state=state, - ) - @property def username(self): """Return member username or invite email as username.""" diff --git a/readthedocs/organizations/querysets.py b/readthedocs/organizations/querysets.py index ed6dac1adb1..ed0016c597f 100644 --- a/readthedocs/organizations/querysets.py +++ b/readthedocs/organizations/querysets.py @@ -8,11 +8,12 @@ from django.utils import timezone from djstripe.enums import InvoiceStatus, SubscriptionStatus +from readthedocs.core.querysets import NoReprQuerySet from readthedocs.core.utils.extend import SettingsOverrideObject from readthedocs.subscriptions.constants import DISABLE_AFTER_DAYS -class BaseOrganizationQuerySet(models.QuerySet): +class BaseOrganizationQuerySet(NoReprQuerySet, models.QuerySet): """Organizations queryset.""" diff --git a/readthedocs/projects/models.py b/readthedocs/projects/models.py index 61ba7154367..2785b203aba 100644 --- a/readthedocs/projects/models.py +++ b/readthedocs/projects/models.py @@ -111,9 +111,6 @@ class ProjectRelationship(models.Model): objects = ChildRelatedProjectQuerySet.as_manager() - def __str__(self): - return "{} -> {}".format(self.parent, self.child) - def save(self, *args, **kwargs): if not self.alias: self.alias = self.child.slug @@ -1506,9 +1503,6 @@ def get_absolute_url(self): filename=self.path, ) - def __str__(self): - return "{}: {}".format(self.name, self.project) - class HTMLFile(ImportedFile): @@ -1696,9 +1690,6 @@ def sign_payload(self, payload): ) return digest.hexdigest() - def __str__(self): - return f"{self.project.slug} {self.url}" - class Domain(TimeStampedModel): @@ -1790,10 +1781,7 @@ class Meta: ordering = ("-canonical", "-machine", "domain") def __str__(self): - return "{domain} pointed at {project}".format( - domain=self.domain, - project=self.project.name, - ) + return self.domain @property def is_valid(self): @@ -1864,7 +1852,7 @@ class HTTPHeader(TimeStampedModel, models.Model): ) def __str__(self): - return f"HttpHeader: {self.name} on {self.domain.domain}" + return self.name class Feature(models.Model): @@ -2033,9 +2021,7 @@ def add_features(sender, **kwargs): objects = FeatureQuerySet.as_manager() def __str__(self): - return "{} feature".format( - self.get_feature_display(), - ) + return self.get_feature_display() def get_feature_display(self): """ diff --git a/readthedocs/projects/querysets.py b/readthedocs/projects/querysets.py index cd612797df8..5725af100e1 100644 --- a/readthedocs/projects/querysets.py +++ b/readthedocs/projects/querysets.py @@ -3,12 +3,13 @@ from django.db.models import Count, OuterRef, Prefetch, Q, Subquery from readthedocs.core.permissions import AdminPermission +from readthedocs.core.querysets import NoReprQuerySet from readthedocs.core.utils.extend import SettingsOverrideObject from readthedocs.projects import constants from readthedocs.subscriptions.products import get_feature -class ProjectQuerySetBase(models.QuerySet): +class ProjectQuerySetBase(NoReprQuerySet, models.QuerySet): """Projects take into account their own privacy_level setting.""" @@ -169,7 +170,7 @@ class ProjectQuerySet(SettingsOverrideObject): _default_class = ProjectQuerySetBase -class RelatedProjectQuerySet(models.QuerySet): +class RelatedProjectQuerySet(NoReprQuerySet, models.QuerySet): """ Useful for objects that relate to Project and its permissions. @@ -220,7 +221,7 @@ class ChildRelatedProjectQuerySet(RelatedProjectQuerySet): use_for_related_fields = True -class FeatureQuerySet(models.QuerySet): +class FeatureQuerySet(NoReprQuerySet, models.QuerySet): use_for_related_fields = True def for_project(self, project): diff --git a/readthedocs/redirects/querysets.py b/readthedocs/redirects/querysets.py index 9b7fd6cd77b..c97655b884f 100644 --- a/readthedocs/redirects/querysets.py +++ b/readthedocs/redirects/querysets.py @@ -6,6 +6,7 @@ from django.db.models import CharField, F, Q, Value from readthedocs.core.permissions import AdminPermission +from readthedocs.core.querysets import NoReprQuerySet from readthedocs.redirects.constants import ( CLEAN_URL_TO_HTML_REDIRECT, EXACT_REDIRECT, @@ -16,7 +17,7 @@ log = structlog.get_logger(__name__) -class RedirectQuerySet(models.QuerySet): +class RedirectQuerySet(NoReprQuerySet, models.QuerySet): """Redirects take into account their own privacy_level setting.""" diff --git a/readthedocs/rtd_tests/tests/test_footer.py b/readthedocs/rtd_tests/tests/test_footer.py index f6cb4b91d83..f4572de0308 100644 --- a/readthedocs/rtd_tests/tests/test_footer.py +++ b/readthedocs/rtd_tests/tests/test_footer.py @@ -343,7 +343,7 @@ def setUp(self): def test_highest_version_from_stable(self): base_version = self.pip.get_stable_version() valid_data = { - "project": "Version 0.8.1 of Pip (19)", + "project": "0.8.1", "url": "https://pip.readthedocs.io/en/0.8.1/", "slug": "0.8.1", "version": "0.8.1", @@ -355,7 +355,7 @@ def test_highest_version_from_stable(self): def test_highest_version_from_lower(self): base_version = self.pip.versions.get(slug="0.8") valid_data = { - "project": "Version 0.8.1 of Pip (19)", + "project": "0.8.1", "url": "https://pip.readthedocs.io/en/0.8.1/", "slug": "0.8.1", "version": "0.8.1", @@ -368,7 +368,7 @@ def test_highest_version_from_latest(self): self.pip.versions.filter(slug=LATEST).update(built=True) base_version = self.pip.versions.get(slug=LATEST) valid_data = { - "project": "Version 0.8.1 of Pip (19)", + "project": "0.8.1", "url": "https://pip.readthedocs.io/en/0.8.1/", "slug": "0.8.1", "version": "0.8.1", @@ -397,7 +397,7 @@ def test_highest_version_over_branches(self): base_version = self.pip.versions.get(slug="0.8.1") valid_data = { - "project": "Version 1.0.0 of Pip ({})".format(version.pk), + "project": "1.0.0", "url": "https://pip.readthedocs.io/en/1.0.0/", "slug": "1.0.0", "version": "1.0.0", @@ -411,7 +411,7 @@ def test_highest_version_without_tags(self): base_version = self.pip.versions.get(slug="0.8.1") valid_data = { - "project": "Version 0.8.1 of Pip (19)", + "project": "0.8.1", "url": "https://pip.readthedocs.io/en/0.8.1/", "slug": "0.8.1", "version": "0.8.1", @@ -422,7 +422,7 @@ def test_highest_version_without_tags(self): base_version = self.pip.versions.get(slug="0.8") valid_data = { - "project": "Version 0.8.1 of Pip (19)", + "project": "0.8.1", "url": "https://pip.readthedocs.io/en/0.8.1/", "slug": "0.8.1", "version": "0.8.1", @@ -440,7 +440,7 @@ def test_highest_version_without_tags(self): built=True, ) valid_data = { - "project": "Version 2.0.0 of Pip ({})".format(version.pk), + "project": "2.0.0", "url": "https://pip.readthedocs.io/en/2.0.0/", "slug": "2.0.0", "version": "2.0.0", @@ -461,7 +461,7 @@ def test_private_highest_version(self): returned_data = get_version_compare_data(self.pip, base_version, user=self.user) valid_data = { - "project": "Version 0.8.1 of Pip (19)", + "project": "0.8.1", "url": "https://pip.readthedocs.io/en/0.8.1/", "slug": "0.8.1", "version": "0.8.1", diff --git a/readthedocs/search/models.py b/readthedocs/search/models.py index f0c59fd2382..39ea10c8277 100644 --- a/readthedocs/search/models.py +++ b/readthedocs/search/models.py @@ -5,7 +5,6 @@ from django.db.models.functions import TruncDate from django.utils import timezone from django.utils.translation import gettext_lazy as _ - from django_extensions.db.models import TimeStampedModel from readthedocs.builds.models import Version @@ -44,7 +43,7 @@ class Meta: verbose_name_plural = "Search queries" def __str__(self): - return f"[{self.project.slug}:{self.version.slug}]: {self.query}" + return self.query @classmethod def generate_queries_count_of_one_month(cls, project_slug): diff --git a/readthedocs/sso/models.py b/readthedocs/sso/models.py index aca6d767144..a8fda22ec48 100644 --- a/readthedocs/sso/models.py +++ b/readthedocs/sso/models.py @@ -54,9 +54,7 @@ class SSOIntegration(models.Model): ) def __str__(self): - if self.name: - return f'"{self.name}" for "{self.organization}" ({self.provider})' - return f"{self.organization} ({self.provider})" + return self.name or self.provider class SSODomain(models.Model): @@ -67,4 +65,4 @@ class SSODomain(models.Model): ) def __str__(self): - return f"{self.domain}" + return self.domain diff --git a/readthedocs/subscriptions/querysets.py b/readthedocs/subscriptions/querysets.py index 82ca0eaa5b9..fc880140e14 100644 --- a/readthedocs/subscriptions/querysets.py +++ b/readthedocs/subscriptions/querysets.py @@ -10,8 +10,10 @@ from django.utils import timezone from djstripe.enums import SubscriptionStatus +from readthedocs.core.querysets import NoReprQuerySet -class StripeSubscriptionQueryset(models.QuerySet): + +class StripeSubscriptionQueryset(NoReprQuerySet, models.QuerySet): """Manager for the djstripe Subscription model.""" diff --git a/readthedocs/telemetry/models.py b/readthedocs/telemetry/models.py index c22a025ea80..e99d3c3ab5b 100644 --- a/readthedocs/telemetry/models.py +++ b/readthedocs/telemetry/models.py @@ -114,9 +114,3 @@ class Meta: data = models.JSONField() objects = BuildDataManager() - - def __str__(self): - build_id = self.data.get("build", {}).get("id") - project_slug = self.data.get("project", {}).get("slug") - version_slug = self.data.get("version", {}).get("slug") - return f"BuildData for {project_slug}:{version_slug} build={build_id}"