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:
parent
817bc5800b
commit
6c120508b6
@ -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)
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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.
|
||||||
|
@ -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,
|
|
||||||
)
|
|
||||||
|
@ -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
|
||||||
|
@ -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"]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user