Merge ~nataliabidart/django-adminaudit:django2 into django-adminaudit:master
- Git
- lp:~nataliabidart/django-adminaudit
- django2
- Merge into 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) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu One hackers | Pending | ||
Review via email: mp+390188@code.launchpad.net |
Commit message
Description of the change
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
1 | diff --git a/.gitignore b/.gitignore |
2 | index 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__/ |
17 | diff --git a/adminaudit/__init__.py b/adminaudit/__init__.py |
18 | index 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 |
32 | diff --git a/adminaudit/admin.py b/adminaudit/admin.py |
33 | index 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) |
59 | diff --git a/adminaudit/management/commands/adminaudit_email_report.py b/adminaudit/management/commands/adminaudit_email_report.py |
60 | index 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") |
79 | diff --git a/adminaudit/management/commands/adminaudit_report.py b/adminaudit/management/commands/adminaudit_report.py |
80 | index 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') |
99 | diff --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 |
100 | deleted file mode 100644 |
101 | index 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 |
152 | diff --git a/adminaudit/models.py b/adminaudit/models.py |
153 | index 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 | |
191 | diff --git a/adminaudit/templates/admin/adminaudit/auditlog/change_form.html b/adminaudit/templates/admin/adminaudit/auditlog/change_form.html |
192 | index 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 %} |
295 | diff --git a/adminaudit/templates/admin/base_site.html b/adminaudit/templates/admin/base_site.html |
296 | deleted file mode 100644 |
297 | index 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 %} |
331 | diff --git a/adminaudit/tests.py b/adminaudit/tests.py |
332 | index 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) |
505 | diff --git a/demo/settings.py b/demo/settings.py |
506 | index 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 | -) |
631 | diff --git a/demo/urls.py b/demo/urls.py |
632 | index 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 | +] |
676 | diff --git a/demo/wsgi.py b/demo/wsgi.py |
677 | index 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 |
689 | diff --git a/django-adminaudit.recipe b/django-adminaudit.recipe |
690 | deleted file mode 100644 |
691 | index 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 |
697 | diff --git a/example_app/migrations/0001_initial.py b/example_app/migrations/0001_initial.py |
698 | index 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 | ] |
712 | diff --git a/example_app/models.py b/example_app/models.py |
713 | index 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) |
738 | diff --git a/example_app/south_migrations/0001_initial.py b/example_app/south_migrations/0001_initial.py |
739 | deleted file mode 100644 |
740 | index 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 |
825 | diff --git a/example_app/south_migrations/__init__.py b/example_app/south_migrations/__init__.py |
826 | deleted file mode 100644 |
827 | index e69de29..0000000 |
828 | --- a/example_app/south_migrations/__init__.py |
829 | +++ /dev/null |
830 | diff --git a/setup.py b/setup.py |
831 | index 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 | }, |
875 | diff --git a/tox.ini b/tox.ini |
876 | index 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 |