Merge ~nataliabidart/django-adminaudit:django2 into django-adminaudit:master

Proposed by Natalia Bidart
Status: Work in progress
Proposed branch: ~nataliabidart/django-adminaudit:django2
Merge into: django-adminaudit:master
Diff against target: 920 lines (+163/-158)
16 files modified
.gitignore (+2/-0)
adminaudit/__init__.py (+3/-1)
adminaudit/admin.py (+5/-5)
adminaudit/management/commands/adminaudit_email_report.py (+1/-2)
adminaudit/management/commands/adminaudit_report.py (+1/-4)
adminaudit/models.py (+4/-6)
adminaudit/templates/admin/adminaudit/auditlog/change_form.html (+52/-36)
adminaudit/tests.py (+27/-26)
demo/settings.py (+31/-23)
demo/urls.py (+21/-10)
demo/wsgi.py (+1/-1)
dev/null (+0/-0)
example_app/migrations/0001_initial.py (+2/-2)
example_app/models.py (+7/-3)
setup.py (+4/-6)
tox.ini (+2/-33)
Reviewer Review Type Date Requested Status
Ubuntu One hackers Pending
Review via email: mp+390188@code.launchpad.net
To post a comment you must log in.
4574410... by Natalia Bidart

Update setup.py as per latest updates.

Unmerged commits

4574410... by Natalia Bidart

Update setup.py as per latest updates.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/.gitignore b/.gitignore
2index ecfc0d8..8b71146 100644
3--- a/.gitignore
4+++ b/.gitignore
5@@ -1,5 +1,6 @@
6 db.sqlite3
7 build/
8+dist/
9 django_adminaudit.egg-info/
10 doc/_build/*
11 .coverage
12@@ -7,3 +8,4 @@ coverage
13 MANIFEST
14 .tox
15 attachements
16+__pycache__/
17diff --git a/adminaudit/__init__.py b/adminaudit/__init__.py
18index 8af0d98..85b40e3 100644
19--- a/adminaudit/__init__.py
20+++ b/adminaudit/__init__.py
21@@ -7,7 +7,9 @@ def audit_install():
22 from django.contrib.admin import site
23 from .models import AdminAuditMixin, AuditLog
24
25- for model, model_admin in site._registry.items():
26+ # Directly iterating over site._registry.items() won't work, as we are
27+ # changing values on the go, which cannot be done in python 3+
28+ for model, model_admin in list(site._registry.items()):
29 if (model is AuditLog or isinstance(model_admin, AdminAuditMixin)):
30 # Do not mingle with our own model
31 continue
32diff --git a/adminaudit/admin.py b/adminaudit/admin.py
33index a550ef7..bdb3d48 100644
34--- a/adminaudit/admin.py
35+++ b/adminaudit/admin.py
36@@ -17,8 +17,11 @@ class AuditLogAdmin(admin.ModelAdmin):
37 def has_add_permission(self, request, obj=None):
38 return False
39
40- def delete_view(self, request, object_id, extra_context=None):
41- raise Http404
42+ def has_change_permission(self, request, obj=None):
43+ return False
44+
45+ def has_delete_permission(self, request, obj=None):
46+ return False
47
48 def change_view(self, request, object_id, extra_context=None):
49 if request.method == 'POST':
50@@ -46,8 +49,5 @@ class AuditLogAdmin(admin.ModelAdmin):
51 return super(AuditLogAdmin, self).change_view(
52 request, object_id, extra_context=extra_context)
53
54- def get_actions(self, request):
55- return []
56-
57
58 admin.site.register(AuditLog, AuditLogAdmin)
59diff --git a/adminaudit/management/commands/adminaudit_email_report.py b/adminaudit/management/commands/adminaudit_email_report.py
60index db62187..32c3133 100644
61--- a/adminaudit/management/commands/adminaudit_email_report.py
62+++ b/adminaudit/management/commands/adminaudit_email_report.py
63@@ -4,7 +4,6 @@
64 from operator import itemgetter
65
66 from django.conf import settings
67-from django.template import Context
68 from django.template.loader import get_template
69 from django.core.mail import send_mail
70
71@@ -38,6 +37,6 @@ class Command(AdminAuditBaseCommand):
72 for recipient in recipients:
73 context['recipient'] = recipient
74 self.stdout.write("Sending e-email to: %s\n" % recipient)
75- send_mail(subject, template.render(Context(context)),
76+ send_mail(subject, template.render(context),
77 from_email, [recipient], fail_silently=False)
78 self.stdout.write("done\n")
79diff --git a/adminaudit/management/commands/adminaudit_report.py b/adminaudit/management/commands/adminaudit_report.py
80index 5a216d6..bb71895 100644
81--- a/adminaudit/management/commands/adminaudit_report.py
82+++ b/adminaudit/management/commands/adminaudit_report.py
83@@ -1,7 +1,6 @@
84 # Copyright 2010-2012 Canonical Ltd. This software is licensed under
85 # the GNU Lesser General Public License version 3 (see the file LICENSE).
86
87-from django.template import Context
88 from django.template.loader import get_template
89
90 from adminaudit.management.commands import AdminAuditBaseCommand
91@@ -13,6 +12,4 @@ class Command(AdminAuditBaseCommand):
92
93 def handle(self, *args, **kwargs):
94 template = get_template("adminaudit/report.txt")
95- context = Context(self.context_data())
96-
97- self.stdout.write(template.render(context) + '\n')
98+ self.stdout.write(template.render(self.context_data()) + '\n')
99diff --git a/adminaudit/migrations/0002_auto__chg_field_auditlog_username__chg_field_auditlog_representation__.py b/adminaudit/migrations/0002_auto__chg_field_auditlog_username__chg_field_auditlog_representation__.py
100deleted file mode 100644
101index 158b225..0000000
102--- a/adminaudit/migrations/0002_auto__chg_field_auditlog_username__chg_field_auditlog_representation__.py
103+++ /dev/null
104@@ -1,46 +0,0 @@
105-# -*- coding: utf-8 -*-
106-import datetime
107-from south.db import db
108-from south.v2 import SchemaMigration
109-from django.db import models
110-
111-
112-class Migration(SchemaMigration):
113-
114- def forwards(self, orm):
115-
116- # Changing field 'AuditLog.username'
117- db.alter_column(u'adminaudit_auditlog', 'username', self.gf('django.db.models.fields.TextField')())
118-
119- # Changing field 'AuditLog.representation'
120- db.alter_column(u'adminaudit_auditlog', 'representation', self.gf('django.db.models.fields.TextField')())
121-
122- # Changing field 'AuditLog.model'
123- db.alter_column(u'adminaudit_auditlog', 'model', self.gf('django.db.models.fields.TextField')())
124-
125- def backwards(self, orm):
126-
127- # Changing field 'AuditLog.username'
128- db.alter_column(u'adminaudit_auditlog', 'username', self.gf('django.db.models.fields.CharField')(max_length=255))
129-
130- # Changing field 'AuditLog.representation'
131- db.alter_column(u'adminaudit_auditlog', 'representation', self.gf('django.db.models.fields.CharField')(max_length=255))
132-
133- # Changing field 'AuditLog.model'
134- db.alter_column(u'adminaudit_auditlog', 'model', self.gf('django.db.models.fields.CharField')(max_length=255))
135-
136- models = {
137- u'adminaudit.auditlog': {
138- 'Meta': {'object_name': 'AuditLog'},
139- 'change': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
140- 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
141- u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
142- 'model': ('django.db.models.fields.TextField', [], {}),
143- 'representation': ('django.db.models.fields.TextField', [], {}),
144- 'user_id': ('django.db.models.fields.IntegerField', [], {}),
145- 'username': ('django.db.models.fields.TextField', [], {}),
146- 'values': ('django.db.models.fields.TextField', [], {})
147- }
148- }
149-
150- complete_apps = ['adminaudit']
151\ No newline at end of file
152diff --git a/adminaudit/models.py b/adminaudit/models.py
153index a96bcbe..bd5792c 100644
154--- a/adminaudit/models.py
155+++ b/adminaudit/models.py
156@@ -1,14 +1,12 @@
157 # Copyright 2010-2012 Canonical Ltd. This software is licensed under
158 # the GNU Lesser General Public License version 3 (see the file LICENSE).
159
160+import base64
161 import json
162
163 import django
164
165-if django.VERSION < (1, 7):
166- from django.contrib.admin.util import NestedObjects
167-else:
168- from django.contrib.admin.utils import NestedObjects
169+from django.contrib.admin.utils import NestedObjects
170 from django.core import serializers
171 from django.db import models, router
172 from django.db.models.fields.files import FileField
173@@ -46,7 +44,7 @@ class AuditLog(models.Model):
174 field_name = file_field.name
175 fileobj = getattr(obj, field_name)
176 if fileobj.name:
177- content = fileobj.read().encode('base64')
178+ content = base64.b64encode(fileobj.read()).decode('utf-8')
179 json_content['files'][fileobj.name] = content
180 values_pretty = json.dumps(json_content, indent=2, sort_keys=True)
181 return cls.objects.create(
182@@ -54,7 +52,7 @@ class AuditLog(models.Model):
183 user_id=user.id,
184 model=str(obj._meta),
185 values=values_pretty,
186- representation=unicode(obj),
187+ representation=str(obj),
188 change=change,
189 )
190
191diff --git a/adminaudit/templates/admin/adminaudit/auditlog/change_form.html b/adminaudit/templates/admin/adminaudit/auditlog/change_form.html
192index 5b6eab0..49c164e 100644
193--- a/adminaudit/templates/admin/adminaudit/auditlog/change_form.html
194+++ b/adminaudit/templates/admin/adminaudit/auditlog/change_form.html
195@@ -3,38 +3,54 @@
196 {# Copyright 2010-2012 Canonical Ltd. This software is licensed under #}
197 {# the GNU Lesser General Public License version 3 (see the file LICENSE). #}
198
199-{% load i18n %}
200-
201-{% block title %}View audit log| {% trans 'Django site admin' %}{% endblock %}
202-{% block content_title %}<h1>View audit log</h1>{% endblock %}
203-
204-{% block content %}
205-<table>
206- <tbody>
207- <tr>
208- <th>User</th>
209- <td>{{ original.user }}</td>
210- </tr>
211- <tr>
212- <th>Model</th>
213- <td>{{ original.model }}</td>
214- </tr>
215- <tr>
216- <th>Representation:</th>
217- <td>{{ original.representation }}</td>
218- </tr>
219- <tr>
220- <th>Change</th>
221- <td>{{ original.change }}</td>
222- </tr>
223- <tr>
224- <th>Change date</th>
225- <td>{{ original.created_at }}</td>
226- </tr>
227- <tr>
228- <th>Change values</th>
229- <td>
230- <table>
231+{% block field_sets %}
232+<fieldset class="module aligned ">
233+ <div class="form-row field-username">
234+ <div>
235+ <label>Username:</label>
236+ <div class="readonly">{{ original.username }}</div>
237+ </div>
238+ </div>
239+
240+ <div class="form-row field-user_id">
241+ <div>
242+ <label>User ID:</label>
243+ <div class="readonly">{{ original.user_id }}</div>
244+ </div>
245+ </div>
246+
247+ <div class="form-row field-model">
248+ <div>
249+ <label>Model:</label>
250+ <div class="readonly">{{ original.model }}</div>
251+ </div>
252+ </div>
253+
254+ <div class="form-row field-representation">
255+ <div>
256+ <label>Representation:</label>
257+ <div class="readonly">{{ original.representation }}</div>
258+ </div>
259+ </div>
260+
261+ <div class="form-row field-change">
262+ <div>
263+ <label>Change:</label>
264+ <div class="readonly">{{ original.change }}</div>
265+ </div>
266+ </div>
267+
268+ <div class="form-row field-created_at">
269+ <div>
270+ <label>Change date:</label>
271+ <div class="readonly">{{ original.created_at }}</div>
272+ </div>
273+ </div>
274+
275+ <div class="form-row field-values">
276+ <div>
277+ <label>Change values:</label>
278+ <table class="readonly">
279 <tbody>
280 <tr>
281 <th>New values</th>
282@@ -46,8 +62,8 @@
283 </tr>
284 </tbody>
285 </table>
286- </td>
287- </tr>
288- </tbody>
289-</table>
290+ </div>
291+ </div>
292+
293+</fieldset>
294 {% endblock %}
295diff --git a/adminaudit/templates/admin/base_site.html b/adminaudit/templates/admin/base_site.html
296deleted file mode 100644
297index fa7bdd1..0000000
298--- a/adminaudit/templates/admin/base_site.html
299+++ /dev/null
300@@ -1,30 +0,0 @@
301-{% extends "admin/base.html" %}
302-
303-{# Copyright 2010-2012 Canonical Ltd. This software is licensed under #}
304-{# the GNU Lesser General Public License version 3 (see the file LICENSE). #}
305-
306-{% load i18n %}
307-
308-{% block title %}{{ title }} | {% trans 'Django site admin' %}{% endblock %}
309-
310-{% block footer %}
311-{{ block.super }}
312-<script type="text/javascript">
313- if (document.getElementsByClassName) {
314- var changelinks = document.getElementsByClassName("changelink");
315- for (var i = 0; i < changelinks.length; i += 1) {
316- var a = changelinks[i];
317- if (a.getAttribute("href") == "adminaudit/auditlog/") {
318- a.parentNode.removeChild(a);
319- break;
320- }
321- }
322- }
323-</script>
324-{% endblock %}
325-
326-{% block branding %}
327-<h1 id="site-name">{% trans 'Django administration' %}</h1>
328-{% endblock %}
329-
330-{% block nav-global %}{% endblock %}
331diff --git a/adminaudit/tests.py b/adminaudit/tests.py
332index 9b26ddd..8191388 100644
333--- a/adminaudit/tests.py
334+++ b/adminaudit/tests.py
335@@ -1,30 +1,32 @@
336 # Copyright 2010-2012 Canonical Ltd. This software is licensed under
337 # the GNU Lesser General Public License version 3 (see the file LICENSE).
338
339+import base64
340 import json
341 import os
342
343-from cStringIO import StringIO
344+from io import StringIO
345 from random import choice
346 from string import ascii_letters
347 from datetime import timedelta
348+from unittest.mock import Mock, patch
349
350 import django
351
352 from django.test import TestCase
353 from django.db.models import Model
354 from django.conf import settings
355+from django.contrib.admin.sites import AdminSite
356 from django.core import mail
357 from django.core.files import File
358 from django.core.management import call_command
359-from django.core.urlresolvers import reverse
360 from django.contrib.admin import ModelAdmin, site
361 from django.contrib.auth.models import User, Permission
362+from django.urls import reverse
363 from django.utils.timezone import now
364
365-from mock import Mock, patch
366-
367 from adminaudit import audit_install
368+from adminaudit.admin import AuditLogAdmin
369 from adminaudit.models import AuditLog
370 from adminaudit.management.commands.adminaudit_email_report import (
371 Command as EmailCommand)
372@@ -36,9 +38,6 @@ class BaseTestCase(TestCase):
373
374 def setUp(self):
375 super(BaseTestCase, self).setUp()
376- User.objects.filter(username='test').delete()
377- AuditLog.objects.all().delete()
378-
379 self.user = User.objects.create_superuser(
380 username='test', password='12345678', email='a@a.com')
381
382@@ -58,6 +57,7 @@ class BaseTestCase(TestCase):
383
384
385 class AuditLogTestCase(TestCase):
386+
387 def make_log_data(self, **overrides):
388 data = {
389 'username': 'test',
390@@ -123,7 +123,7 @@ class AuditAdminEmailReportTestCase(TestCase):
391 user = self.create_and_log_in_user(is_superuser=True)
392
393 auditlog_id = AuditLog.create(user, user, 'create').id
394- url = '/admin/adminaudit/auditlog/{0}/'.format(auditlog_id)
395+ url = '/admin/adminaudit/auditlog/{0}/change/'.format(auditlog_id)
396
397 r = self.client.post(url, {
398 'values': 'test',
399@@ -146,14 +146,13 @@ class AuditAdminEmailReportTestCase(TestCase):
400 r = self.client.post(url, {'post': 'yes'})
401
402 self.assertEqual(AuditLog.objects.filter(pk=auditlog_id).count(), 1)
403- self.assertEqual(404, r.status_code)
404-
405- def test_js_for_removing_change_link_from_index_page_is_present(self):
406- self.create_and_log_in_user(is_superuser=True)
407-
408- r = self.client.get(reverse('admin:index'))
409+ self.assertEqual(403, r.status_code)
410
411- self.assertContains(r, 'a.parentNode.removeChild(a)')
412+ def test_no_add_no_delete_no_change_permissions(self):
413+ auditlog_admin = AuditLogAdmin(AuditLog, AdminSite())
414+ self.assertFalse(auditlog_admin.has_add_permission(object()))
415+ self.assertFalse(auditlog_admin.has_change_permission(object()))
416+ self.assertFalse(auditlog_admin.has_delete_permission(object()))
417
418
419 class AdminIntegrationTestCase(BaseTestCase):
420@@ -176,7 +175,7 @@ class AdminIntegrationTestCase(BaseTestCase):
421 self.assertOneLogCreated('create')
422
423 def test_changing_object_via_admin_is_saved_by_audit(self):
424- r = self.client.get('/admin/auth/user/{0}/'.format(self.user.pk))
425+ r = self.client.get('/admin/auth/user/{0}/change/'.format(self.user.pk))
426 data = r.context['adminform'].form.initial
427 data.update({
428 'first_name': "First",
429@@ -186,7 +185,7 @@ class AdminIntegrationTestCase(BaseTestCase):
430 'date_joined_1': data['date_joined'].strftime("%H:%M:%S"),
431 })
432 r = self.client.post(
433- '/admin/auth/user/{0}/'.format(self.user.pk), data)
434+ '/admin/auth/user/{0}/change/'.format(self.user.pk), data)
435 self.assertEqual(302, r.status_code)
436 self.assertOneLogCreated('update')
437
438@@ -230,7 +229,7 @@ class AuditLogAdminChangeViewTestCase(BaseTestCase):
439 auditlog = AuditLog.create(self.user, self.user, 'create')
440
441 r = self.client.get(
442- '/admin/adminaudit/auditlog/{0}/'.format(auditlog.pk))
443+ '/admin/adminaudit/auditlog/{0}/change/'.format(auditlog.pk))
444
445 self.assertContains(r, 'test')
446 self.assertContains(r, 'auth.user')
447@@ -242,7 +241,7 @@ class AuditLogAdminChangeViewTestCase(BaseTestCase):
448 auditlog = AuditLog.create(self.user, self.user, 'update', self.user)
449
450 r = self.client.get(
451- '/admin/adminaudit/auditlog/{0}/'.format(auditlog.pk))
452+ '/admin/adminaudit/auditlog/{0}/change/'.format(auditlog.pk))
453
454 self.assertTrue(r.context['new'])
455 self.assertTrue(r.context['old'])
456@@ -251,7 +250,7 @@ class AuditLogAdminChangeViewTestCase(BaseTestCase):
457 auditlog = AuditLog.create(self.user, self.user, 'delete')
458
459 r = self.client.get(
460- '/admin/adminaudit/auditlog/{0}/'.format(auditlog.pk))
461+ '/admin/adminaudit/auditlog/{0}/change/'.format(auditlog.pk))
462
463 self.assertFalse(r.context['new'])
464 self.assertTrue(r.context['old'])
465@@ -345,7 +344,7 @@ class AdminAuditCLIReportTestCase(TestCase):
466 class FilesTestCase(BaseTestCase):
467
468 def create_post_with_attachement(self):
469- with open('adminaudit/test_data/file_1.dat') as f:
470+ with open('adminaudit/test_data/file_1.dat', 'rb') as f:
471 p = Post.objects.create(author=self.user, title='t', body='b')
472 p.attachement.save('file_1.dat', File(f))
473 p.save()
474@@ -355,9 +354,9 @@ class FilesTestCase(BaseTestCase):
475 def test_changing_file_in_the_admin_leaves_alone_old_file(self):
476 p = self.create_post_with_attachement()
477
478- with open('adminaudit/test_data/file_2.dat') as f:
479+ with open('adminaudit/test_data/file_2.dat', 'rb') as f:
480 response = self.client.post(
481- '/admin/example_app/post/{0}/'.format(p.pk),
482+ '/admin/example_app/post/{0}/change/'.format(p.pk),
483 data={
484 'author': self.user.pk,
485 'title': "Post title",
486@@ -392,8 +391,10 @@ class FilesTestCase(BaseTestCase):
487 self.assertEqual('delete', latest_log.change)
488
489 data = json.loads(latest_log.values)
490- content = data['files'][attachement_path].decode('base64')
491- original_content = open('adminaudit/test_data/file_1.dat').read()
492+ content = base64.b64decode(data['files'][attachement_path])
493+ # be sure to open (and close) the file in binary mode
494+ with open('adminaudit/test_data/file_1.dat', 'rb') as f:
495+ original_content = f.read()
496 self.assertEqual(original_content, content)
497
498
499@@ -415,4 +416,4 @@ class AuditInstallTestCase(TestCase):
500 audit_install()
501 audit_install()
502
503- mock_unregister.assert_called_once(model)
504+ mock_unregister.assert_called_once_with(model)
505diff --git a/demo/settings.py b/demo/settings.py
506index d97a212..042123c 100644
507--- a/demo/settings.py
508+++ b/demo/settings.py
509@@ -1,22 +1,24 @@
510 """
511 Django settings for demo project.
512
513+Generated by 'django-admin startproject' using Django 2.2.16.
514+
515 For more information on this file, see
516-https://docs.djangoproject.com/en/1.7/topics/settings/
517+https://docs.djangoproject.com/en/2.2/topics/settings/
518
519 For the full list of settings and their values, see
520-https://docs.djangoproject.com/en/1.7/ref/settings/
521+https://docs.djangoproject.com/en/2.2/ref/settings/
522 """
523
524-# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
525 import os
526 import django
527
528-BASE_DIR = os.path.dirname(os.path.dirname(__file__))
529+# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
530+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
531
532
533 # Quick-start development settings - unsuitable for production
534-# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/
535+# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
536
537 # SECURITY WARNING: keep the secret key used in production secret!
538 SECRET_KEY = 'ljq4=m7@(1#pkuyq8rza*^-526*etfuh(in8_d(g*5n=mwbod)'
539@@ -24,46 +26,56 @@ SECRET_KEY = 'ljq4=m7@(1#pkuyq8rza*^-526*etfuh(in8_d(g*5n=mwbod)'
540 # SECURITY WARNING: don't run with debug turned on in production!
541 DEBUG = True
542
543-TEMPLATE_DEBUG = True
544-
545 ALLOWED_HOSTS = []
546
547
548 # Application definition
549
550 INSTALLED_APPS = (
551+ 'django.contrib.admin',
552 'django.contrib.auth',
553 'django.contrib.contenttypes',
554 'django.contrib.sessions',
555 'django.contrib.messages',
556 'django.contrib.staticfiles',
557 'adminaudit',
558- 'django.contrib.admin',
559 'example_app',
560 )
561
562-MIDDLEWARE_CLASSES = [
563+MIDDLEWARE = [
564+ 'django.middleware.security.SecurityMiddleware',
565 'django.contrib.sessions.middleware.SessionMiddleware',
566 'django.middleware.common.CommonMiddleware',
567 'django.middleware.csrf.CsrfViewMiddleware',
568 'django.contrib.auth.middleware.AuthenticationMiddleware',
569- 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
570 'django.contrib.messages.middleware.MessageMiddleware',
571 'django.middleware.clickjacking.XFrameOptionsMiddleware',
572 ]
573
574-if django.VERSION < (1, 7):
575- INSTALLED_APPS += ('south',)
576- MIDDLEWARE_CLASSES.remove(
577- 'django.contrib.auth.middleware.SessionAuthenticationMiddleware')
578-
579 ROOT_URLCONF = 'demo.urls'
580
581+TEMPLATES = [
582+ {
583+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
584+ 'DIRS': [],
585+ 'APP_DIRS': True,
586+ 'OPTIONS': {
587+ 'debug': True,
588+ 'context_processors': [
589+ 'django.template.context_processors.debug',
590+ 'django.template.context_processors.request',
591+ 'django.contrib.auth.context_processors.auth',
592+ 'django.contrib.messages.context_processors.messages',
593+ ],
594+ },
595+ },
596+]
597+
598 WSGI_APPLICATION = 'demo.wsgi.application'
599
600
601 # Database
602-# https://docs.djangoproject.com/en/1.7/ref/settings/#databases
603+# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
604
605 DATABASES = {
606 'default': {
607@@ -72,8 +84,9 @@ DATABASES = {
608 }
609 }
610
611+
612 # Internationalization
613-# https://docs.djangoproject.com/en/1.7/topics/i18n/
614+# https://docs.djangoproject.com/en/2.2/topics/i18n/
615
616 LANGUAGE_CODE = 'en-us'
617
618@@ -85,11 +98,6 @@ USE_TZ = True
619
620
621 # Static files (CSS, JavaScript, Images)
622-# https://docs.djangoproject.com/en/1.7/howto/static-files/
623+# https://docs.djangoproject.com/en/2.2/howto/static-files/
624
625 STATIC_URL = '/static/'
626-
627-TEMPLATE_DIRS = (
628- os.path.join(os.path.dirname(django.__file__),
629- 'contrib/admin/templates/admin'),
630-)
631diff --git a/demo/urls.py b/demo/urls.py
632index fd96184..f1f71be 100644
633--- a/demo/urls.py
634+++ b/demo/urls.py
635@@ -1,19 +1,30 @@
636-# Copyright 2010-2012 Canonical Ltd. This software is licensed under
637+# Copyright 2010-2020 Canonical Ltd. This software is licensed under
638 # the GNU Lesser General Public License version 3 (see the file LICENSE).
639
640-from django.conf.urls import patterns, include, url
641+"""demo URL Configuration
642+
643+The `urlpatterns` list routes URLs to views. For more information please see:
644+ https://docs.djangoproject.com/en/2.2/topics/http/urls/
645+Examples:
646+Function views
647+ 1. Add an import: from my_app import views
648+ 2. Add a URL to urlpatterns: path('', views.home, name='home')
649+Class-based views
650+ 1. Add an import: from other_app.views import Home
651+ 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
652+Including another URLconf
653+ 1. Import the include() function: from django.urls import include, path
654+ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
655+"""
656+
657 from django.contrib import admin
658+from django.urls import path
659
660 from adminaudit import audit_install
661
662 admin.autodiscover()
663 audit_install()
664
665-urlpatterns = patterns(
666- '',
667- # Examples:
668- # url(r'^$', 'demo.views.home', name='home'),
669- # url(r'^blog/', include('blog.urls')),
670-
671- url(r'^admin/', include(admin.site.urls)),
672-)
673+urlpatterns = [
674+ path('admin/', admin.site.urls),
675+]
676diff --git a/demo/wsgi.py b/demo/wsgi.py
677index ef48754..4e0598d 100644
678--- a/demo/wsgi.py
679+++ b/demo/wsgi.py
680@@ -4,7 +4,7 @@ WSGI config for demo project.
681 It exposes the WSGI callable as a module-level variable named ``application``.
682
683 For more information on this file, see
684-https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/
685+https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/
686 """
687
688 import os
689diff --git a/django-adminaudit.recipe b/django-adminaudit.recipe
690deleted file mode 100644
691index d702acf..0000000
692--- a/django-adminaudit.recipe
693+++ /dev/null
694@@ -1,2 +0,0 @@
695-# bzr-builder format 0.2 deb-version {debupstream}~{revno}
696-lp:django-adminaudit
697diff --git a/example_app/migrations/0001_initial.py b/example_app/migrations/0001_initial.py
698index 0ef2ea7..96104c0 100644
699--- a/example_app/migrations/0001_initial.py
700+++ b/example_app/migrations/0001_initial.py
701@@ -21,8 +21,8 @@ class Migration(migrations.Migration):
702 ('attachement', models.FileField(upload_to=b'attachements')),
703 ('created_at', models.DateTimeField(auto_now_add=True)),
704 ('updated_at', models.DateTimeField(auto_now=True)),
705- ('author', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
706- ('related', models.ForeignKey(blank=True, to='example_app.Post', null=True)),
707+ ('author', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
708+ ('related', models.ForeignKey(blank=True, to='example_app.Post', null=True, on_delete=models.CASCADE)),
709 ],
710 ),
711 ]
712diff --git a/example_app/models.py b/example_app/models.py
713index e5babc8..a9501fe 100644
714--- a/example_app/models.py
715+++ b/example_app/models.py
716@@ -2,14 +2,18 @@
717 # the GNU Lesser General Public License version 3 (see the file LICENSE).
718
719 from django.db import models
720-from django.contrib.auth.models import User
721+from django.contrib.auth import get_user_model
722+
723+
724+User = get_user_model()
725
726
727 class Post(models.Model):
728- author = models.ForeignKey(User)
729+ author = models.ForeignKey(User, on_delete=models.CASCADE)
730 title = models.CharField(max_length=128)
731 body = models.TextField()
732 attachement = models.FileField(upload_to='attachements')
733 created_at = models.DateTimeField(auto_now_add=True)
734 updated_at = models.DateTimeField(auto_now=True)
735- related = models.ForeignKey('self', blank=True, null=True)
736+ related = models.ForeignKey(
737+ 'self', blank=True, null=True, on_delete=models.CASCADE)
738diff --git a/example_app/south_migrations/0001_initial.py b/example_app/south_migrations/0001_initial.py
739deleted file mode 100644
740index ec3f4d7..0000000
741--- a/example_app/south_migrations/0001_initial.py
742+++ /dev/null
743@@ -1,80 +0,0 @@
744-# -*- coding: utf-8 -*-
745-from south.utils import datetime_utils as datetime
746-from south.db import db
747-from south.v2 import SchemaMigration
748-from django.db import models
749-
750-
751-class Migration(SchemaMigration):
752-
753- def forwards(self, orm):
754- # Adding model 'Post'
755- db.create_table('example_app_post', (
756- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
757- ('author', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
758- ('title', self.gf('django.db.models.fields.CharField')(max_length=128)),
759- ('body', self.gf('django.db.models.fields.TextField')()),
760- ('attachement', self.gf('django.db.models.fields.files.FileField')(max_length=100)),
761- ('created_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
762- ('updated_at', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
763- ('related', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['example_app.Post'], null=True, blank=True)),
764- ))
765- db.send_create_signal('example_app', ['Post'])
766-
767-
768- def backwards(self, orm):
769- # Deleting model 'Post'
770- db.delete_table('example_app_post')
771-
772-
773- models = {
774- 'auth.group': {
775- 'Meta': {'object_name': 'Group'},
776- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
777- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
778- 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
779- },
780- 'auth.permission': {
781- 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
782- 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
783- 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
784- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
785- 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
786- },
787- 'auth.user': {
788- 'Meta': {'object_name': 'User'},
789- 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
790- 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
791- 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
792- 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
793- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
794- 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
795- 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
796- 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
797- 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
798- 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
799- 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
800- 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
801- 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
802- },
803- 'contenttypes.contenttype': {
804- 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
805- 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
806- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
807- 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
808- 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
809- },
810- 'example_app.post': {
811- 'Meta': {'object_name': 'Post'},
812- 'attachement': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
813- 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
814- 'body': ('django.db.models.fields.TextField', [], {}),
815- 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
816- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
817- 'related': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['example_app.Post']", 'null': 'True', 'blank': 'True'}),
818- 'title': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
819- 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
820- }
821- }
822-
823- complete_apps = ['example_app']
824\ No newline at end of file
825diff --git a/example_app/south_migrations/__init__.py b/example_app/south_migrations/__init__.py
826deleted file mode 100644
827index e69de29..0000000
828--- a/example_app/south_migrations/__init__.py
829+++ /dev/null
830diff --git a/setup.py b/setup.py
831index 2c8cbc0..c34bafb 100644
832--- a/setup.py
833+++ b/setup.py
834@@ -1,6 +1,6 @@
835 #!/usr/bin/env python
836 # -*- coding: utf-8 -*-
837-# Copyright 2016 Canonical Ltd. This software is licensed under the
838+# Copyright 2016-2020 Canonical Ltd. This software is licensed under the
839 # GNU Library General Public License version 3 (see the file LICENSE).
840 from setuptools import setup
841
842@@ -8,16 +8,16 @@ from setuptools import setup
843 setup(
844 # metadata
845 name='django-adminaudit',
846- version='0.5',
847+ version='2.0.0',
848 description="Extends Django's admin logging capabilities",
849 url='https://launchpad.net/django-adminaudit',
850 author='Ubuntu One Hackers',
851 author_email='natalia.bidart@ubuntu.com',
852 license='LGPLv3',
853 classifiers=[
854- 'Framework :: Django',
855+ 'Framework :: Django :: 2.2',
856 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
857- 'Programming Language :: Python :: 2.7',
858+ 'Programming Language :: Python :: 3 :: Only',
859 'Topic :: Software Development :: Libraries',
860 'Topic :: Software Development :: Libraries :: Python Modules',
861 ],
862@@ -28,12 +28,10 @@ setup(
863 'adminaudit.management',
864 'adminaudit.management.commands',
865 'adminaudit.migrations',
866- 'adminaudit.south_migrations',
867 ],
868 package_data={
869 'adminaudit': [
870 'templates/admin/adminaudit/auditlog/*.html',
871- 'templates/admin/*.html',
872 'templates/adminaudit/*.txt',
873 ],
874 },
875diff --git a/tox.ini b/tox.ini
876index 7fdc8d9..26b0a2f 100644
877--- a/tox.ini
878+++ b/tox.ini
879@@ -1,39 +1,8 @@
880 [tox]
881 envlist =
882- py27-django14, py27-django15, py27-django16, py27-django17, py27-django18
883+ py37, py38
884
885 [testenv]
886 commands = python manage.py test adminaudit
887 deps =
888- mock==1.0.1
889-
890-[testenv:py27]
891-basepython = python2.7
892-
893-[testenv:py27-django14]
894-deps =
895- {[testenv]deps}
896- django>=1.4,<1.5
897- south==1.0
898-
899-[testenv:py27-django15]
900-deps =
901- {[testenv]deps}
902- django>=1.5,<1.6
903- south==1.0
904-
905-[testenv:py27-django16]
906-deps =
907- {[testenv]deps}
908- django>=1.6,<1.7
909- south==1.0
910-
911-[testenv:py27-django17]
912-deps =
913- {[testenv]deps}
914- django>=1.7,<1.8
915-
916-[testenv:py27-django18]
917-deps =
918- {[testenv]deps}
919- django>=1.8,<1.9.0a
920+ django>=2.2,<3

Subscribers

People subscribed via source and target branches