1
0
mirror of https://github.com/django/django.git synced 2025-06-12 06:59:13 +00:00

Refs #34462 -- Removed ModelAdmin.log_deletion() and LogEntryManager.log_action() per deprecation timeline.

This commit is contained in:
Sarah Boyce 2024-12-12 16:55:42 +01:00
parent 817bc5800b
commit 6c120508b6
6 changed files with 4 additions and 240 deletions

View File

@ -1,5 +1,4 @@
import json import json
import warnings
from django.conf import settings from django.conf import settings
from django.contrib.admin.utils import quote from django.contrib.admin.utils import quote
@ -7,7 +6,6 @@ from django.contrib.contenttypes.models import ContentType
from django.db import models from django.db import models
from django.urls import NoReverseMatch, reverse from django.urls import NoReverseMatch, reverse
from django.utils import timezone from django.utils import timezone
from django.utils.deprecation import RemovedInDjango60Warning
from django.utils.text import get_text_list from django.utils.text import get_text_list
from django.utils.translation import gettext from django.utils.translation import gettext
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -26,56 +24,9 @@ ACTION_FLAG_CHOICES = [
class LogEntryManager(models.Manager): class LogEntryManager(models.Manager):
use_in_migrations = True use_in_migrations = True
def log_action(
self,
user_id,
content_type_id,
object_id,
object_repr,
action_flag,
change_message="",
):
warnings.warn(
"LogEntryManager.log_action() is deprecated. Use log_actions() instead.",
RemovedInDjango60Warning,
stacklevel=2,
)
if isinstance(change_message, list):
change_message = json.dumps(change_message)
return self.model.objects.create(
user_id=user_id,
content_type_id=content_type_id,
object_id=str(object_id),
object_repr=object_repr[:200],
action_flag=action_flag,
change_message=change_message,
)
def log_actions( def log_actions(
self, user_id, queryset, action_flag, change_message="", *, single_object=False self, user_id, queryset, action_flag, change_message="", *, single_object=False
): ):
# RemovedInDjango60Warning.
if type(self).log_action != LogEntryManager.log_action:
warnings.warn(
"The usage of log_action() is deprecated. Implement log_actions() "
"instead.",
RemovedInDjango60Warning,
stacklevel=2,
)
return [
self.log_action(
user_id=user_id,
content_type_id=ContentType.objects.get_for_model(
obj, for_concrete_model=False
).id,
object_id=obj.pk,
object_repr=str(obj),
action_flag=action_flag,
change_message=change_message,
)
for obj in queryset
]
if isinstance(change_message, list): if isinstance(change_message, list):
change_message = json.dumps(change_message) change_message = json.dumps(change_message)

View File

@ -2,7 +2,6 @@ import copy
import enum import enum
import json import json
import re import re
import warnings
from functools import partial, update_wrapper from functools import partial, update_wrapper
from urllib.parse import parse_qsl from urllib.parse import parse_qsl
from urllib.parse import quote as urlquote from urllib.parse import quote as urlquote
@ -56,7 +55,6 @@ from django.http.response import HttpResponseBase
from django.template.response import SimpleTemplateResponse, TemplateResponse from django.template.response import SimpleTemplateResponse, TemplateResponse
from django.urls import reverse from django.urls import reverse
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.deprecation import RemovedInDjango60Warning
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.http import urlencode from django.utils.http import urlencode
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
@ -967,28 +965,6 @@ class ModelAdmin(BaseModelAdmin):
single_object=True, single_object=True,
) )
def log_deletion(self, request, obj, object_repr):
"""
Log that an object will be deleted. Note that this method must be
called before the deletion.
The default implementation creates an admin LogEntry object.
"""
warnings.warn(
"ModelAdmin.log_deletion() is deprecated. Use log_deletions() instead.",
RemovedInDjango60Warning,
stacklevel=2,
)
from django.contrib.admin.models import DELETION, LogEntry
return LogEntry.objects.log_action(
user_id=request.user.pk,
content_type_id=get_content_type_for_model(obj).pk,
object_id=obj.pk,
object_repr=object_repr,
action_flag=DELETION,
)
def log_deletions(self, request, queryset): def log_deletions(self, request, queryset):
""" """
Log that objects will be deleted. Note that this method must be called Log that objects will be deleted. Note that this method must be called
@ -998,16 +974,6 @@ class ModelAdmin(BaseModelAdmin):
""" """
from django.contrib.admin.models import DELETION, LogEntry from django.contrib.admin.models import DELETION, LogEntry
# RemovedInDjango60Warning.
if type(self).log_deletion != ModelAdmin.log_deletion:
warnings.warn(
"The usage of log_deletion() is deprecated. Implement log_deletions() "
"instead.",
RemovedInDjango60Warning,
stacklevel=2,
)
return [self.log_deletion(request, obj, str(obj)) for obj in queryset]
return LogEntry.objects.log_actions( return LogEntry.objects.log_actions(
user_id=request.user.pk, user_id=request.user.pk,
queryset=queryset, queryset=queryset,

View File

@ -299,3 +299,6 @@ to remove usage of these features.
* ``django.urls.register_converter()`` no longer allows overriding existing * ``django.urls.register_converter()`` no longer allows overriding existing
converters. converters.
* The ``ModelAdmin.log_deletion()`` and ``LogEntryManager.log_action()``
methods are removed.

View File

@ -1,5 +1,4 @@
from django.contrib import admin from django.contrib import admin
from django.contrib.admin.models import LogEntry, LogEntryManager
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -87,26 +86,3 @@ class VehicleMixin(Vehicle):
class Car(VehicleMixin): class Car(VehicleMixin):
pass pass
class InheritedLogEntryManager(LogEntryManager):
model = LogEntry
def log_action(
self,
user_id,
content_type_id,
object_id,
object_repr,
action_flag,
change_message="",
):
return LogEntry.objects.create(
user_id=user_id,
content_type_id=content_type_id,
object_id=str(object_id),
# Changing actual repr to test repr
object_repr="Test Repr",
action_flag=action_flag,
change_message=change_message,
)

View File

@ -8,10 +8,9 @@ from django.contrib.contenttypes.models import ContentType
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from django.urls import reverse from django.urls import reverse
from django.utils import translation from django.utils import translation
from django.utils.deprecation import RemovedInDjango60Warning
from django.utils.html import escape from django.utils.html import escape
from .models import Article, ArticleProxy, Car, InheritedLogEntryManager, Site from .models import Article, ArticleProxy, Car, Site
@override_settings(ROOT_URLCONF="admin_utils.urls") @override_settings(ROOT_URLCONF="admin_utils.urls")
@ -236,22 +235,6 @@ class LogEntryTests(TestCase):
logentry = LogEntry.objects.first() logentry = LogEntry.objects.first()
self.assertEqual(repr(logentry), str(logentry.action_time)) self.assertEqual(repr(logentry), str(logentry.action_time))
# RemovedInDjango60Warning.
def test_log_action(self):
msg = "LogEntryManager.log_action() is deprecated. Use log_actions() instead."
content_type_val = ContentType.objects.get_for_model(Article).pk
with self.assertWarnsMessage(RemovedInDjango60Warning, msg) as ctx:
log_entry = LogEntry.objects.log_action(
self.user.pk,
content_type_val,
self.a1.pk,
repr(self.a1),
CHANGE,
change_message="Changed something else",
)
self.assertEqual(log_entry, LogEntry.objects.latest("id"))
self.assertEqual(ctx.filename, __file__)
def test_log_actions(self): def test_log_actions(self):
queryset = Article.objects.all().order_by("-id") queryset = Article.objects.all().order_by("-id")
msg = "Deleted Something" msg = "Deleted Something"
@ -289,46 +272,6 @@ class LogEntryTests(TestCase):
] ]
self.assertSequenceEqual(logs, expected_log_values) self.assertSequenceEqual(logs, expected_log_values)
# RemovedInDjango60Warning.
def test_log_action_fallback(self):
LogEntry.objects2 = InheritedLogEntryManager()
queryset = Article.objects.all().order_by("-id")
content_type = ContentType.objects.get_for_model(self.a1)
self.assertEqual(len(queryset), 3)
msg = (
"The usage of log_action() is deprecated. Implement log_actions() instead."
)
with (
self.assertNumQueries(3),
self.assertWarnsMessage(RemovedInDjango60Warning, msg) as ctx,
):
LogEntry.objects2.log_actions(self.user.pk, queryset, DELETION)
self.assertEqual(ctx.filename, __file__)
log_values = (
LogEntry.objects.filter(action_flag=DELETION)
.order_by("id")
.values_list(
"user",
"content_type",
"object_id",
"object_repr",
"action_flag",
"change_message",
)
)
expected_log_values = [
(
self.user.pk,
content_type.id,
str(obj.pk),
"Test Repr",
DELETION,
"",
)
for obj in queryset
]
self.assertSequenceEqual(log_values, expected_log_values)
def test_recentactions_without_content_type(self): def test_recentactions_without_content_type(self):
""" """
If a LogEntry is missing content_type it will not display it in span If a LogEntry is missing content_type it will not display it in span

View File

@ -21,7 +21,6 @@ from django.db import models
from django.forms.widgets import Select from django.forms.widgets import Select
from django.test import RequestFactory, SimpleTestCase, TestCase from django.test import RequestFactory, SimpleTestCase, TestCase
from django.test.utils import isolate_apps from django.test.utils import isolate_apps
from django.utils.deprecation import RemovedInDjango60Warning
from .models import Band, Concert, Song from .models import Band, Concert, Song
@ -891,80 +890,6 @@ class ModelAdminTests(TestCase):
] ]
self.assertSequenceEqual(logs, expected_log_values) self.assertSequenceEqual(logs, expected_log_values)
# RemovedInDjango60Warning.
def test_log_deletion(self):
ma = ModelAdmin(Band, self.site)
mock_request = MockRequest()
mock_request.user = User.objects.create(username="bill")
content_type = get_content_type_for_model(self.band)
msg = "ModelAdmin.log_deletion() is deprecated. Use log_deletions() instead."
with self.assertWarnsMessage(RemovedInDjango60Warning, msg) as ctx:
created = ma.log_deletion(mock_request, self.band, str(self.band))
self.assertEqual(ctx.filename, __file__)
fetched = LogEntry.objects.filter(action_flag=DELETION).latest("id")
self.assertEqual(created, fetched)
self.assertEqual(fetched.action_flag, DELETION)
self.assertEqual(fetched.content_type, content_type)
self.assertEqual(fetched.object_id, str(self.band.pk))
self.assertEqual(fetched.user, mock_request.user)
self.assertEqual(fetched.change_message, "")
self.assertEqual(fetched.object_repr, str(self.band))
# RemovedInDjango60Warning.
def test_log_deletion_fallback(self):
class InheritedModelAdmin(ModelAdmin):
def log_deletion(self, request, obj, object_repr):
return super().log_deletion(request, obj, object_repr)
ima = InheritedModelAdmin(Band, self.site)
mock_request = MockRequest()
mock_request.user = User.objects.create(username="akash")
content_type = get_content_type_for_model(self.band)
Band.objects.create(
name="The Beatles",
bio="A legendary rock band from Liverpool.",
sign_date=date(1962, 1, 1),
)
Band.objects.create(
name="Mohiner Ghoraguli",
bio="A progressive rock band from Calcutta.",
sign_date=date(1975, 1, 1),
)
queryset = Band.objects.all().order_by("-id")[:3]
self.assertEqual(len(queryset), 3)
msg = (
"The usage of log_deletion() is deprecated. Implement log_deletions() "
"instead."
)
with self.assertNumQueries(3):
with self.assertWarnsMessage(RemovedInDjango60Warning, msg) as ctx:
ima.log_deletions(mock_request, queryset)
self.assertEqual(ctx.filename, __file__)
logs = (
LogEntry.objects.filter(action_flag=DELETION)
.order_by("id")
.values_list(
"user_id",
"content_type",
"object_id",
"object_repr",
"action_flag",
"change_message",
)
)
expected_log_values = [
(
mock_request.user.id,
content_type.id,
str(obj.pk),
str(obj),
DELETION,
"",
)
for obj in queryset
]
self.assertSequenceEqual(logs, expected_log_values)
def test_get_autocomplete_fields(self): def test_get_autocomplete_fields(self):
class NameAdmin(ModelAdmin): class NameAdmin(ModelAdmin):
search_fields = ["name"] search_fields = ["name"]