Closes #2292: Remove the deprecated UserAction model

This commit is contained in:
Jeremy Stretch 2018-08-14 12:13:43 -04:00
parent 980d62d579
commit 5f66710fcd
11 changed files with 27 additions and 200 deletions

View File

@ -1,13 +1,8 @@
from django import forms from django import forms
from django.contrib import admin from django.contrib import admin
from django.utils.safestring import mark_safe
from utilities.forms import LaxURLField from utilities.forms import LaxURLField
from .constants import OBJECTCHANGE_ACTION_CREATE, OBJECTCHANGE_ACTION_DELETE, OBJECTCHANGE_ACTION_UPDATE from .models import CustomField, CustomFieldChoice, Graph, ExportTemplate, TopologyMap, Webhook
from .models import (
ConfigContext, CustomField, CustomFieldChoice, Graph, ExportTemplate, ObjectChange, TopologyMap, UserAction,
Webhook,
)
def order_content_types(field): def order_content_types(field):
@ -123,16 +118,3 @@ class TopologyMapAdmin(admin.ModelAdmin):
prepopulated_fields = { prepopulated_fields = {
'slug': ['name'], 'slug': ['name'],
} }
#
# User actions
#
@admin.register(UserAction)
class UserActionAdmin(admin.ModelAdmin):
actions = None
list_display = ['user', 'action', 'content_type', 'object_id', '_message']
def _message(self, obj):
return mark_safe(obj.message)

View File

@ -8,7 +8,7 @@ from dcim.api.serializers import (
) )
from dcim.models import Device, DeviceRole, Platform, Rack, Region, Site from dcim.models import Device, DeviceRole, Platform, Rack, Region, Site
from extras.models import ( from extras.models import (
ConfigContext, ExportTemplate, Graph, ImageAttachment, ObjectChange, ReportResult, TopologyMap, UserAction, ConfigContext, ExportTemplate, Graph, ImageAttachment, ObjectChange, ReportResult, TopologyMap,
) )
from extras.constants import * from extras.constants import *
from tenancy.api.serializers import NestedTenantSerializer, NestedTenantGroupSerializer from tenancy.api.serializers import NestedTenantSerializer, NestedTenantGroupSerializer
@ -238,16 +238,3 @@ class ObjectChangeSerializer(serializers.ModelSerializer):
context = {'request': self.context['request']} context = {'request': self.context['request']}
data = serializer(obj.changed_object, context=context).data data = serializer(obj.changed_object, context=context).data
return data return data
#
# User actions
#
class UserActionSerializer(serializers.ModelSerializer):
user = NestedUserSerializer()
action = ChoiceField(choices=ACTION_CHOICES)
class Meta:
model = UserAction
fields = ['id', 'time', 'user', 'action', 'message']

View File

@ -41,8 +41,5 @@ router.register(r'reports', views.ReportViewSet, base_name='report')
# Change logging # Change logging
router.register(r'object-changes', views.ObjectChangeViewSet) router.register(r'object-changes', views.ObjectChangeViewSet)
# Recent activity
router.register(r'recent-activity', views.RecentActivityViewSet)
app_name = 'extras-api' app_name = 'extras-api'
urlpatterns = router.urls urlpatterns = router.urls

View File

@ -11,7 +11,6 @@ from taggit.models import Tag
from extras import filters from extras import filters
from extras.models import ( from extras.models import (
ConfigContext, CustomField, ExportTemplate, Graph, ImageAttachment, ObjectChange, ReportResult, TopologyMap, ConfigContext, CustomField, ExportTemplate, Graph, ImageAttachment, ObjectChange, ReportResult, TopologyMap,
UserAction,
) )
from extras.reports import get_report, get_reports from extras.reports import get_report, get_reports
from utilities.api import FieldChoicesViewSet, IsAuthenticatedOrLoginNotRequired, ModelViewSet from utilities.api import FieldChoicesViewSet, IsAuthenticatedOrLoginNotRequired, ModelViewSet
@ -230,16 +229,3 @@ class ObjectChangeViewSet(ReadOnlyModelViewSet):
queryset = ObjectChange.objects.select_related('user') queryset = ObjectChange.objects.select_related('user')
serializer_class = serializers.ObjectChangeSerializer serializer_class = serializers.ObjectChangeSerializer
filter_class = filters.ObjectChangeFilter filter_class = filters.ObjectChangeFilter
#
# User activity
#
class RecentActivityViewSet(ReadOnlyModelViewSet):
"""
DEPRECATED: List all UserActions to provide a log of recent activity.
"""
queryset = UserAction.objects.all()
serializer_class = serializers.UserActionSerializer
filter_class = filters.UserActionFilter

View File

@ -1,5 +1,4 @@
import django_filters import django_filters
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db.models import Q from django.db.models import Q
from taggit.models import Tag from taggit.models import Tag
@ -7,7 +6,7 @@ from taggit.models import Tag
from dcim.models import DeviceRole, Platform, Region, Site from dcim.models import DeviceRole, Platform, Region, Site
from tenancy.models import Tenant, TenantGroup from tenancy.models import Tenant, TenantGroup
from .constants import CF_FILTER_DISABLED, CF_FILTER_EXACT, CF_TYPE_BOOLEAN, CF_TYPE_SELECT from .constants import CF_FILTER_DISABLED, CF_FILTER_EXACT, CF_TYPE_BOOLEAN, CF_TYPE_SELECT
from .models import ConfigContext, CustomField, Graph, ExportTemplate, ObjectChange, TopologyMap, UserAction from .models import ConfigContext, CustomField, Graph, ExportTemplate, ObjectChange, TopologyMap
class CustomFieldFilter(django_filters.Filter): class CustomFieldFilter(django_filters.Filter):
@ -227,15 +226,3 @@ class ObjectChangeFilter(django_filters.FilterSet):
Q(user_name__icontains=value) | Q(user_name__icontains=value) |
Q(object_repr__icontains=value) Q(object_repr__icontains=value)
) )
class UserActionFilter(django_filters.FilterSet):
username = django_filters.ModelMultipleChoiceFilter(
name='user__username',
queryset=User.objects.all(),
to_field_name='username',
)
class Meta:
model = UserAction
fields = ['user']

View File

@ -0,0 +1,24 @@
# Generated by Django 2.0.8 on 2018-08-14 16:10
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('extras', '0014_configcontexts'),
]
operations = [
migrations.RemoveField(
model_name='useraction',
name='content_type',
),
migrations.RemoveField(
model_name='useraction',
name='user',
),
migrations.DeleteModel(
name='UserAction',
),
]

View File

@ -12,7 +12,6 @@ from django.db.models import Q
from django.http import HttpResponse from django.http import HttpResponse
from django.template import Template, Context from django.template import Template, Context
from django.urls import reverse from django.urls import reverse
from django.utils.safestring import mark_safe
from dcim.constants import CONNECTION_STATUS_CONNECTED from dcim.constants import CONNECTION_STATUS_CONNECTED
from utilities.utils import foreground_color from utilities.utils import foreground_color
@ -842,100 +841,3 @@ class ObjectChange(models.Model):
self.object_repr, self.object_repr,
self.object_data, self.object_data,
) )
#
# User actions
#
class UserActionManager(models.Manager):
# Actions affecting a single object
def log_action(self, user, obj, action, message):
self.model.objects.create(
content_type=ContentType.objects.get_for_model(obj),
object_id=obj.pk,
user=user,
action=action,
message=message,
)
def log_create(self, user, obj, message=''):
self.log_action(user, obj, ACTION_CREATE, message)
def log_edit(self, user, obj, message=''):
self.log_action(user, obj, ACTION_EDIT, message)
def log_delete(self, user, obj, message=''):
self.log_action(user, obj, ACTION_DELETE, message)
# Actions affecting multiple objects
def log_bulk_action(self, user, content_type, action, message):
self.model.objects.create(
content_type=content_type,
user=user,
action=action,
message=message,
)
def log_import(self, user, content_type, message=''):
self.log_bulk_action(user, content_type, ACTION_IMPORT, message)
def log_bulk_create(self, user, content_type, message=''):
self.log_bulk_action(user, content_type, ACTION_BULK_CREATE, message)
def log_bulk_edit(self, user, content_type, message=''):
self.log_bulk_action(user, content_type, ACTION_BULK_EDIT, message)
def log_bulk_delete(self, user, content_type, message=''):
self.log_bulk_action(user, content_type, ACTION_BULK_DELETE, message)
# TODO: Remove UserAction, which has been replaced by ObjectChange.
class UserAction(models.Model):
"""
DEPRECATED: A record of an action (add, edit, or delete) performed on an object by a User.
"""
time = models.DateTimeField(
auto_now_add=True,
editable=False
)
user = models.ForeignKey(
to=User,
on_delete=models.CASCADE,
related_name='actions'
)
content_type = models.ForeignKey(
to=ContentType,
on_delete=models.CASCADE
)
object_id = models.PositiveIntegerField(
blank=True,
null=True
)
action = models.PositiveSmallIntegerField(
choices=ACTION_CHOICES
)
message = models.TextField(
blank=True
)
objects = UserActionManager()
class Meta:
ordering = ['-time']
def __str__(self):
if self.message:
return '{} {}'.format(self.user, self.message)
return '{} {} {}'.format(self.user, self.get_action_display(), self.content_type)
def icon(self):
if self.action in [ACTION_CREATE, ACTION_BULK_CREATE, ACTION_IMPORT]:
return mark_safe('<i class="glyphicon glyphicon-plus text-success"></i>')
elif self.action in [ACTION_EDIT, ACTION_BULK_EDIT]:
return mark_safe('<i class="glyphicon glyphicon-pencil text-warning"></i>')
elif self.action in [ACTION_DELETE, ACTION_BULK_DELETE]:
return mark_safe('<i class="glyphicon glyphicon-remove text-danger"></i>')
else:
return ''

View File

@ -21,9 +21,6 @@
<li{% ifequal active_tab "userkey" %} class="active"{% endifequal %}> <li{% ifequal active_tab "userkey" %} class="active"{% endifequal %}>
<a href="{% url 'user:userkey' %}">User Key</a> <a href="{% url 'user:userkey' %}">User Key</a>
</li> </li>
<li{% ifequal active_tab "recent_activity" %} class="active"{% endifequal %}>
<a href="{% url 'user:recent_activity' %}">Recent Activity</a>
</li>
</ul> </ul>
</div> </div>
<div class="col-sm-9 col-md-6"> <div class="col-sm-9 col-md-6">

View File

@ -1,22 +0,0 @@
{% extends 'users/_user.html' %}
{% block title %}Recent Activity{% endblock %}
{% block usercontent %}
<table class="table table-hover">
<thead>
<tr>
<th>Time</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for action in recent_activity %}
<tr>
<td>{{ action.time|date:'SHORT_DATETIME_FORMAT' }}</td>
<td>{{ action.icon }} {{ action.message|safe }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@ -14,6 +14,5 @@ urlpatterns = [
url(r'^user-key/$', views.UserKeyView.as_view(), name='userkey'), url(r'^user-key/$', views.UserKeyView.as_view(), name='userkey'),
url(r'^user-key/edit/$', views.UserKeyEditView.as_view(), name='userkey_edit'), url(r'^user-key/edit/$', views.UserKeyEditView.as_view(), name='userkey_edit'),
url(r'^session-key/delete/$', views.SessionKeyDeleteView.as_view(), name='sessionkey_delete'), url(r'^session-key/delete/$', views.SessionKeyDeleteView.as_view(), name='sessionkey_delete'),
url(r'^recent-activity/$', views.RecentActivityView.as_view(), name='recent_activity'),
] ]

View File

@ -196,18 +196,6 @@ class SessionKeyDeleteView(LoginRequiredMixin, View):
}) })
@method_decorator(login_required, name='dispatch')
class RecentActivityView(View):
template_name = 'users/recent_activity.html'
def get(self, request):
return render(request, self.template_name, {
'recent_activity': request.user.actions.all()[:50],
'active_tab': 'recent_activity',
})
# #
# API tokens # API tokens
# #