From 2408809617db2f81d1554d9a712c2339bc63dbef Mon Sep 17 00:00:00 2001 From: Jose Soares Date: Mon, 5 Dec 2016 12:02:45 -0500 Subject: [PATCH 1/3] [-] Various updates - explicit admin urls - django 1.9 compatibility - get_formsets was removed - admin views needs extra args (args, kwargs) - more specific obj-data url --- genericadmin/admin.py | 70 ++++++++++++------- .../static/genericadmin/js/genericadmin.js | 67 +++++++++--------- 2 files changed, 79 insertions(+), 58 deletions(-) diff --git a/genericadmin/admin.py b/genericadmin/admin.py index e9f5fc4..3daf035 100644 --- a/genericadmin/admin.py +++ b/genericadmin/admin.py @@ -1,6 +1,7 @@ import json from functools import update_wrapper +from django.core.urlresolvers import reverse, NoReverseMatch from django.contrib import admin from django.conf.urls import url from django.conf import settings @@ -12,7 +13,7 @@ from django.contrib.contenttypes.models import ContentType try: - from django.utils.encoding import force_text + from django.utils.encoding import force_text except ImportError: from django.utils.encoding import force_unicode as force_text from django.utils.text import capfirst @@ -24,7 +25,7 @@ from django.contrib.admin.options import IS_POPUP_VAR from django.core.exceptions import ObjectDoesNotExist -JS_PATH = getattr(settings, 'GENERICADMIN_JS', 'genericadmin/js/') +JS_PATH = getattr(settings, 'GENERICADMIN_JS', 'genericadmin/js/') class BaseGenericModelAdmin(object): class Media: @@ -34,7 +35,7 @@ class Media: generic_fk_fields = [] content_type_blacklist = [] content_type_whitelist = [] - + def __init__(self, model, admin_site): try: media = list(self.Media.js) @@ -42,10 +43,10 @@ def __init__(self, model, admin_site): media = [] media.append(JS_PATH + 'genericadmin.js') self.Media.js = tuple(media) - + self.content_type_whitelist = [s.lower() for s in self.content_type_whitelist] - self.content_type_blacklist = [s.lower() for s in self.content_type_blacklist] - + self.content_type_blacklist = [s.lower() for s in self.content_type_blacklist] + super(BaseGenericModelAdmin, self).__init__(model, admin_site) def get_generic_field_list(self, request, prefix=''): @@ -53,7 +54,7 @@ def get_generic_field_list(self, request, prefix=''): exclude = [self.ct_field, self.ct_fk_field] else: exclude = [] - + field_list = [] if hasattr(self, 'generic_fk_fields') and self.generic_fk_fields: for fields in self.generic_fk_fields: @@ -62,19 +63,26 @@ def get_generic_field_list(self, request, prefix=''): fields['inline'] = prefix != '' fields['prefix'] = prefix field_list.append(fields) - else: + else: for field in self.model._meta.virtual_fields: if isinstance(field, GenericForeignKey) and \ field.ct_field not in exclude and field.fk_field not in exclude: field_list.append({ - 'ct_field': field.ct_field, + 'ct_field': field.ct_field, 'fk_field': field.fk_field, 'inline': prefix != '', 'prefix': prefix, }) - + if hasattr(self, 'inlines') and len(self.inlines) > 0: - for FormSet, inline in zip(self.get_formsets(request), self.get_inline_instances(request)): + try: + # Django < 1.9 + formsets = zip(self.get_formsets(request), self.get_inline_instances(request)) + except (AttributeError, ): + # Django >= 1.9 + formsets = self.get_formsets_with_inlines(request) + + for FormSet, inline in formsets: if hasattr(inline, 'get_generic_field_list'): prefix = FormSet.get_default_prefix() field_list = field_list + inline.get_generic_field_list(request, prefix) @@ -86,26 +94,36 @@ def wrap(view): def wrapper(*args, **kwargs): return self.admin_site.admin_view(view)(*args, **kwargs) return update_wrapper(wrapper, view) - + custom_urls = [ - url(r'^obj-data/$', wrap(self.generic_lookup), name='admin_genericadmin_obj_lookup'), - url(r'^genericadmin-init/$', wrap(self.genericadmin_js_init), name='admin_genericadmin_init'), + url(r'^(.*)genericadmin-obj-data/', wrap( + self.generic_lookup), name='admin_genericadmin_obj_lookup'), + url(r'^(.*)genericadmin-init/', wrap( + self.genericadmin_js_init), name='admin_genericadmin_init'), ] return custom_urls + super(BaseGenericModelAdmin, self).get_urls() - - def genericadmin_js_init(self, request): + + def genericadmin_js_init(self, request, *args, **kwargs): if request.method == 'GET': obj_dict = {} for c in ContentType.objects.all(): val = force_text('%s/%s' % (c.app_label, c.model)) params = self.content_type_lookups.get('%s.%s' % (c.app_label, c.model), {}) params = url_params_from_lookup_dict(params) + + try: + # Reverse the admin changelist url + url = reverse('admin:%s_%s_changelist' % ( + c.app_label, c.model)) + except (NoReverseMatch, ): + continue + if self.content_type_whitelist: if val in self.content_type_whitelist: - obj_dict[c.id] = (val, params) + obj_dict[c.id] = (val, url, params) elif val not in self.content_type_blacklist: - obj_dict[c.id] = (val, params) - + obj_dict[c.id] = (val, url, params) + data = { 'url_array': obj_dict, 'fields': self.get_generic_field_list(request), @@ -114,15 +132,15 @@ def genericadmin_js_init(self, request): resp = json.dumps(data, ensure_ascii=False) return HttpResponse(resp, content_type='application/json') return HttpResponseNotAllowed(['GET']) - - def generic_lookup(self, request): + + def generic_lookup(self, request, *args, **kwargs): if request.method != 'GET': return HttpResponseNotAllowed(['GET']) - + if 'content_type' in request.GET and 'object_id' in request.GET: content_type_id = request.GET['content_type'] object_id = request.GET['object_id'] - + obj_dict = { 'content_type_id': content_type_id, 'object_id': object_id, @@ -136,12 +154,12 @@ def generic_lookup(self, request): obj_dict["object_text"] = capfirst(force_text(obj)) except ObjectDoesNotExist: raise Http404 - + resp = json.dumps(obj_dict, ensure_ascii=False) else: resp = '' return HttpResponse(resp, content_type='application/json') - + class GenericAdminModelAdmin(BaseGenericModelAdmin, admin.ModelAdmin): @@ -149,7 +167,7 @@ class GenericAdminModelAdmin(BaseGenericModelAdmin, admin.ModelAdmin): class GenericTabularInline(BaseGenericModelAdmin, GenericTabularInline): - """Model admin for generic tabular inlines. """ + """Model admin for generic tabular inlines. """ class GenericStackedInline(BaseGenericModelAdmin, GenericStackedInline): diff --git a/genericadmin/static/genericadmin/js/genericadmin.js b/genericadmin/static/genericadmin/js/genericadmin.js index 529dbb9..c26744e 100755 --- a/genericadmin/static/genericadmin/js/genericadmin.js +++ b/genericadmin/static/genericadmin/js/genericadmin.js @@ -12,10 +12,10 @@ var GenericAdmin = { url_array: null, fields: null, - obj_url: "../obj-data/", + obj_url: "../genericadmin-obj-data/", admin_media_url: window.__admin_media_prefix__, popup: '_popup', - + prepareSelect: function(select) { var that = this, opt_keys = [], @@ -30,7 +30,7 @@ if (this.value) { if (that.url_array[this.value]) { key = that.url_array[this.value][0].split('/')[0]; - + opt = $(this).clone(); opt.text(that.capFirst(opt.text())); if ($.inArray(key, opt_keys) < 0) { @@ -47,7 +47,7 @@ } }); select.empty().append(no_value); - + opt_keys = opt_keys.sort(); $.each(opt_keys, function(index, key) { @@ -61,8 +61,9 @@ return select; }, + getLookupUrlParams: function(cID) { - var q = this.url_array[cID][1] || {}, + var q = this.url_array[cID][2] || {}, str = []; for(var p in q) { str.push(encodeURIComponent(p) + "=" + encodeURIComponent(q[p])); @@ -71,11 +72,13 @@ url = x ? ("?" + x) : ""; return url; }, - - getLookupUrl: function(cID) { - return '../../../' + this.url_array[cID][0] + '/' + this.getLookupUrlParams(cID); + + getLookupUrl: function(cID, with_params) { + var url = this.url_array[cID][1]; + if(with_params){url = url + this.getLookupUrlParams(cID);} + return url; }, - + getFkId: function() { if (this.fields.inline === false) { return 'id_' + this.fields.fk_field; @@ -83,7 +86,7 @@ return ['id_', this.fields.prefix, '-', this.fields.number, '-', this.fields.fk_field].join(''); } }, - + getCtId: function() { if (this.fields.inline === false) { return 'id_' + this.fields.ct_field; @@ -91,24 +94,24 @@ return ['id_', this.fields.prefix, '-', this.fields.number, '-', this.fields.ct_field].join(''); } }, - + capFirst: function(string) { return string.charAt(0).toUpperCase() + string.slice(1); }, - + hideLookupLink: function() { var this_id = this.getFkId(); $('#lookup_' + this_id).unbind().remove(); $('#lookup_text_' + this_id + ' a').remove(); $('#lookup_text_' + this_id + ' span').remove(); }, - + showLookupLink: function() { var that = this, - url = this.getLookupUrl(this.cID), + url = this.getLookupUrl(this.cID, true), id = 'lookup_' + this.getFkId(), link = ' '; - + link = link + ''; // insert link html after input element @@ -116,7 +119,7 @@ return id; }, - + pollInputChange: function(window) { var that = this, interval_id = setInterval(function() { @@ -128,11 +131,11 @@ }, 150); }, - + popRelatedObjectLookup: function(link) { var name = id_to_windowname(this.getFkId()), url_parts = [], - href, + href, win; if (link.href.search(/\?/) >= 0) { @@ -156,14 +159,14 @@ win.focus(); return false; }, - + updateObjectData: function() { var that = this; return function() { var value = that.object_input.val(); - - if (!value) { - return + + if (!value) { + return } //var this_id = that.getFkId(); $('#lookup_text_' + that.getFkId() + ' span').text('loading...'); @@ -176,7 +179,7 @@ }, success: function(item) { if (item && item.content_type_text && item.object_text) { - var url = that.getLookupUrl(that.cID); + var url = that.getLookupUrl(that.cID, false); $('#lookup_text_' + that.getFkId() + ' a') .text(item.content_type_text + ': ' + item.object_text) .attr('href', url + item.object_id); @@ -208,10 +211,10 @@ this.url_array = url_array; this.fields = fields; this.popup = popup_var || this.popup; - + // store the base element this.object_input = $("#" + this.getFkId()); - + // find the select we need to change this.object_select = this.prepareSelect($("#" + this.getCtId())); @@ -245,22 +248,22 @@ this.updateObjectData()(); } }; - + var InlineAdmin = { sub_admins: null, url_array: null, fields: null, popup: '_popup', - + install: function(fields, url_array, popup_var) { var inline_count = $('#id_' + fields.prefix + '-TOTAL_FORMS').val(), admin; - + this.url_array = url_array; this.fields = fields; this.sub_admins = []; this.popup = popup_var || this.popup; - + for (var j = 0; j < inline_count; j++) { f = $.extend({}, this.fields); f.number = j; @@ -279,7 +282,7 @@ added_fields.number = ($('#id_' + that.fields.prefix + '-TOTAL_FORMS').val() - 1); admin.install(added_fields, that.url_array, that.popup); that.sub_admins.push(admin); - + $('#' + that.fields.prefix + '-' + added_fields.number + ' .inline-deletelink').click( that.removeHandler(that) ); @@ -290,7 +293,7 @@ var parent_id, deleted_num, sub_admin; - + e.preventDefault(); parent_id = $(e.currentTarget).parents('.dynamic-' + that.fields.prefix).first().attr('id'); deleted_num = parseInt(parent_id.charAt(parent_id.length - 1), 10); @@ -315,7 +318,7 @@ ct_fields = data.fields, popup_var = data.popup_var, fields; - + for (var i = 0; i < ct_fields.length; i++) { fields = ct_fields[i]; if (fields.inline === false) { From 91d250c93ba860c82b8f703f5f4d4e9ba0ac73f4 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Tue, 24 Apr 2018 18:29:16 -0700 Subject: [PATCH 2/3] fixes for django 1.11 --- genericadmin/__init__.py | 0 genericadmin/admin.py | 32 ++------- .../static/genericadmin/js/genericadmin.js | 67 +++++++++---------- 3 files changed, 39 insertions(+), 60 deletions(-) mode change 100644 => 100755 genericadmin/__init__.py mode change 100644 => 100755 genericadmin/admin.py diff --git a/genericadmin/__init__.py b/genericadmin/__init__.py old mode 100644 new mode 100755 diff --git a/genericadmin/admin.py b/genericadmin/admin.py old mode 100644 new mode 100755 index 3daf035..40aff4c --- a/genericadmin/admin.py +++ b/genericadmin/admin.py @@ -1,7 +1,6 @@ import json from functools import update_wrapper -from django.core.urlresolvers import reverse, NoReverseMatch from django.contrib import admin from django.conf.urls import url from django.conf import settings @@ -75,14 +74,7 @@ def get_generic_field_list(self, request, prefix=''): }) if hasattr(self, 'inlines') and len(self.inlines) > 0: - try: - # Django < 1.9 - formsets = zip(self.get_formsets(request), self.get_inline_instances(request)) - except (AttributeError, ): - # Django >= 1.9 - formsets = self.get_formsets_with_inlines(request) - - for FormSet, inline in formsets: + for FormSet, inline in zip(self.get_formsets_with_inlines(request), self.get_inline_instances(request)): if hasattr(inline, 'get_generic_field_list'): prefix = FormSet.get_default_prefix() field_list = field_list + inline.get_generic_field_list(request, prefix) @@ -96,33 +88,23 @@ def wrapper(*args, **kwargs): return update_wrapper(wrapper, view) custom_urls = [ - url(r'^(.*)genericadmin-obj-data/', wrap( - self.generic_lookup), name='admin_genericadmin_obj_lookup'), - url(r'^(.*)genericadmin-init/', wrap( - self.genericadmin_js_init), name='admin_genericadmin_init'), + url(r'^obj-data/$', wrap(self.generic_lookup), name='admin_genericadmin_obj_lookup'), + url(r'^genericadmin-init/$', wrap(self.genericadmin_js_init), name='admin_genericadmin_init'), ] return custom_urls + super(BaseGenericModelAdmin, self).get_urls() - def genericadmin_js_init(self, request, *args, **kwargs): + def genericadmin_js_init(self, request): if request.method == 'GET': obj_dict = {} for c in ContentType.objects.all(): val = force_text('%s/%s' % (c.app_label, c.model)) params = self.content_type_lookups.get('%s.%s' % (c.app_label, c.model), {}) params = url_params_from_lookup_dict(params) - - try: - # Reverse the admin changelist url - url = reverse('admin:%s_%s_changelist' % ( - c.app_label, c.model)) - except (NoReverseMatch, ): - continue - if self.content_type_whitelist: if val in self.content_type_whitelist: - obj_dict[c.id] = (val, url, params) + obj_dict[c.id] = (val, params) elif val not in self.content_type_blacklist: - obj_dict[c.id] = (val, url, params) + obj_dict[c.id] = (val, params) data = { 'url_array': obj_dict, @@ -133,7 +115,7 @@ def genericadmin_js_init(self, request, *args, **kwargs): return HttpResponse(resp, content_type='application/json') return HttpResponseNotAllowed(['GET']) - def generic_lookup(self, request, *args, **kwargs): + def generic_lookup(self, request): if request.method != 'GET': return HttpResponseNotAllowed(['GET']) diff --git a/genericadmin/static/genericadmin/js/genericadmin.js b/genericadmin/static/genericadmin/js/genericadmin.js index c26744e..529dbb9 100755 --- a/genericadmin/static/genericadmin/js/genericadmin.js +++ b/genericadmin/static/genericadmin/js/genericadmin.js @@ -12,10 +12,10 @@ var GenericAdmin = { url_array: null, fields: null, - obj_url: "../genericadmin-obj-data/", + obj_url: "../obj-data/", admin_media_url: window.__admin_media_prefix__, popup: '_popup', - + prepareSelect: function(select) { var that = this, opt_keys = [], @@ -30,7 +30,7 @@ if (this.value) { if (that.url_array[this.value]) { key = that.url_array[this.value][0].split('/')[0]; - + opt = $(this).clone(); opt.text(that.capFirst(opt.text())); if ($.inArray(key, opt_keys) < 0) { @@ -47,7 +47,7 @@ } }); select.empty().append(no_value); - + opt_keys = opt_keys.sort(); $.each(opt_keys, function(index, key) { @@ -61,9 +61,8 @@ return select; }, - getLookupUrlParams: function(cID) { - var q = this.url_array[cID][2] || {}, + var q = this.url_array[cID][1] || {}, str = []; for(var p in q) { str.push(encodeURIComponent(p) + "=" + encodeURIComponent(q[p])); @@ -72,13 +71,11 @@ url = x ? ("?" + x) : ""; return url; }, - - getLookupUrl: function(cID, with_params) { - var url = this.url_array[cID][1]; - if(with_params){url = url + this.getLookupUrlParams(cID);} - return url; + + getLookupUrl: function(cID) { + return '../../../' + this.url_array[cID][0] + '/' + this.getLookupUrlParams(cID); }, - + getFkId: function() { if (this.fields.inline === false) { return 'id_' + this.fields.fk_field; @@ -86,7 +83,7 @@ return ['id_', this.fields.prefix, '-', this.fields.number, '-', this.fields.fk_field].join(''); } }, - + getCtId: function() { if (this.fields.inline === false) { return 'id_' + this.fields.ct_field; @@ -94,24 +91,24 @@ return ['id_', this.fields.prefix, '-', this.fields.number, '-', this.fields.ct_field].join(''); } }, - + capFirst: function(string) { return string.charAt(0).toUpperCase() + string.slice(1); }, - + hideLookupLink: function() { var this_id = this.getFkId(); $('#lookup_' + this_id).unbind().remove(); $('#lookup_text_' + this_id + ' a').remove(); $('#lookup_text_' + this_id + ' span').remove(); }, - + showLookupLink: function() { var that = this, - url = this.getLookupUrl(this.cID, true), + url = this.getLookupUrl(this.cID), id = 'lookup_' + this.getFkId(), link = ' '; - + link = link + ''; // insert link html after input element @@ -119,7 +116,7 @@ return id; }, - + pollInputChange: function(window) { var that = this, interval_id = setInterval(function() { @@ -131,11 +128,11 @@ }, 150); }, - + popRelatedObjectLookup: function(link) { var name = id_to_windowname(this.getFkId()), url_parts = [], - href, + href, win; if (link.href.search(/\?/) >= 0) { @@ -159,14 +156,14 @@ win.focus(); return false; }, - + updateObjectData: function() { var that = this; return function() { var value = that.object_input.val(); - - if (!value) { - return + + if (!value) { + return } //var this_id = that.getFkId(); $('#lookup_text_' + that.getFkId() + ' span').text('loading...'); @@ -179,7 +176,7 @@ }, success: function(item) { if (item && item.content_type_text && item.object_text) { - var url = that.getLookupUrl(that.cID, false); + var url = that.getLookupUrl(that.cID); $('#lookup_text_' + that.getFkId() + ' a') .text(item.content_type_text + ': ' + item.object_text) .attr('href', url + item.object_id); @@ -211,10 +208,10 @@ this.url_array = url_array; this.fields = fields; this.popup = popup_var || this.popup; - + // store the base element this.object_input = $("#" + this.getFkId()); - + // find the select we need to change this.object_select = this.prepareSelect($("#" + this.getCtId())); @@ -248,22 +245,22 @@ this.updateObjectData()(); } }; - + var InlineAdmin = { sub_admins: null, url_array: null, fields: null, popup: '_popup', - + install: function(fields, url_array, popup_var) { var inline_count = $('#id_' + fields.prefix + '-TOTAL_FORMS').val(), admin; - + this.url_array = url_array; this.fields = fields; this.sub_admins = []; this.popup = popup_var || this.popup; - + for (var j = 0; j < inline_count; j++) { f = $.extend({}, this.fields); f.number = j; @@ -282,7 +279,7 @@ added_fields.number = ($('#id_' + that.fields.prefix + '-TOTAL_FORMS').val() - 1); admin.install(added_fields, that.url_array, that.popup); that.sub_admins.push(admin); - + $('#' + that.fields.prefix + '-' + added_fields.number + ' .inline-deletelink').click( that.removeHandler(that) ); @@ -293,7 +290,7 @@ var parent_id, deleted_num, sub_admin; - + e.preventDefault(); parent_id = $(e.currentTarget).parents('.dynamic-' + that.fields.prefix).first().attr('id'); deleted_num = parseInt(parent_id.charAt(parent_id.length - 1), 10); @@ -318,7 +315,7 @@ ct_fields = data.fields, popup_var = data.popup_var, fields; - + for (var i = 0; i < ct_fields.length; i++) { fields = ct_fields[i]; if (fields.inline === false) { From b4916ea9421a484e893543bd1cbc6edc1c27fa66 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Tue, 24 Apr 2018 18:42:46 -0700 Subject: [PATCH 3/3] update version string --- LICENCE | 3 ++- setup.py | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/LICENCE b/LICENCE index de1f972..432ee23 100644 --- a/LICENCE +++ b/LICENCE @@ -1,5 +1,6 @@ Copyright (c) 2009, Weston Nielson (wnielson@gmail.com) 2010, Jan Schrewe (jschrewe@googlemail.com) + 2017, Arthur Hanson (worldnomad@gmail.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -17,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file +THE SOFTWARE. diff --git a/setup.py b/setup.py index b2c2c70..faf0c0f 100644 --- a/setup.py +++ b/setup.py @@ -12,11 +12,11 @@ def convert_readme(): setup( name='django-genericadmin', - version='0.6.1', + version='0.7.0', description="Adds support for generic relations within Django's admin interface.", - author='Weston Nielson, Jan Schrewe', - author_email='wnielson@gmail.com, jschrewe@googlemail.com', - url='https://github.com/jschrewe/django-genericadmin', + author='Weston Nielson, Jan Schrewe, Arthur Hanson', + author_email='wnielson@gmail.com, jschrewe@googlemail.com, worldnomad@gmail.com', + url='https://github.com/arthanson/django-genericadmin', packages = ['genericadmin'], # package_data={'genericadmin': ['static/genericadmin/js/genericadmin.js']}, classifiers=[