Last active
December 9, 2015 21:18
-
-
Save kokardy/4329401 to your computer and use it in GitHub Desktop.
I tried to fix #11561.
"raw_id_fields_readonly" is similar to "raw_id_fields", but do not require "change permission".
If a user does not have "change permission", "raw_id_fields" works as "raw_id_fields_readonly".
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ModelAdmin(BaseModelAdmin): | |
raw_id_fields_readonly = () | |
def get_urls(self): | |
from django.conf.urls import patterns, url | |
def wrap(view): | |
def wrapper(*args, **kwargs): | |
return self.admin_site.admin_view(view)(*args, **kwargs) | |
return update_wrapper(wrapper, view) | |
info = self.model._meta.app_label, self.model._meta.module_name | |
urlpatterns = patterns('', | |
url(r'^$', | |
wrap(self.changelist_view), | |
name='%s_%s_changelist' % info), | |
url(r'^readonly/$', | |
wrap(self.readonly_list_view), | |
name='%s_%s_readonlylist' % info), | |
url(r'^add/$', | |
wrap(self.add_view), | |
name='%s_%s_add' % info), | |
url(r'^(.+)/history/$', | |
wrap(self.history_view), | |
name='%s_%s_history' % info), | |
url(r'^(.+)/delete/$', | |
wrap(self.delete_view), | |
name='%s_%s_delete' % info), | |
url(r'^(.+)/$', | |
wrap(self.change_view), | |
name='%s_%s_change' % info), | |
) | |
return urlpatterns | |
def readonly_list_view(self, request, extra_context=None): | |
""" | |
The 'readonly list' admin view for this model. | |
""" | |
from django.contrib.admin.views.main import ERROR_FLAG | |
opts = self.model._meta | |
app_label = opts.app_label | |
#if not self.has_change_permission(request, None): | |
# raise PermissionDenied | |
list_display = self.get_list_display(request) | |
list_display_links = self.get_list_display_links(request, list_display) | |
list_filter = self.get_list_filter(request) | |
# Check actions to see if any are available on this changelist | |
actions = self.get_actions(request) | |
if actions: | |
# Add the action checkboxes if there are any actions available. | |
list_display = ['action_checkbox'] + list(list_display) | |
ChangeList = self.get_changelist(request) | |
try: | |
cl = ChangeList(request, self.model, list_display, | |
list_display_links, list_filter, self.date_hierarchy, | |
self.search_fields, self.list_select_related, | |
self.list_per_page, self.list_max_show_all, self.list_editable, | |
self) | |
except IncorrectLookupParameters: | |
# Wacky lookup parameters were given, so redirect to the main | |
# changelist page, without parameters, and pass an 'invalid=1' | |
# parameter via the query string. If wacky parameters were given | |
# and the 'invalid=1' parameter was already in the query string, | |
# something is screwed up with the database, so display an error | |
# page. | |
if ERROR_FLAG in request.GET.keys(): | |
return SimpleTemplateResponse('admin/invalid_setup.html', { | |
'title': _('Database error'), | |
}) | |
return HttpResponseRedirect(request.path + '?' + ERROR_FLAG + '=1') | |
# If we're allowing changelist editing, we need to construct a formset | |
# for the changelist given all the fields to be edited. Then we'll | |
# use the formset to validate/process POSTed data. | |
formset = cl.formset = None | |
# Handle GET -- construct a formset for display. | |
if cl.list_editable: | |
FormSet = self.get_changelist_formset(request) | |
formset = cl.formset = FormSet(queryset=cl.result_list) | |
# Build the list of media to be used by the formset. | |
if formset: | |
media = self.media + formset.media | |
else: | |
media = self.media | |
# Build the action form and populate it with available actions. | |
if actions: | |
action_form = self.action_form(auto_id=None) | |
action_form.fields['action'].choices = self.get_action_choices(request) | |
else: | |
action_form = None | |
selection_note_all = ungettext('%(total_count)s selected', | |
'All %(total_count)s selected', cl.result_count) | |
context = { | |
'module_name': force_text(opts.verbose_name_plural), | |
'selection_note': _('0 of %(cnt)s selected') % {'cnt': len(cl.result_list)}, | |
'selection_note_all': selection_note_all % {'total_count': cl.result_count}, | |
'title': cl.title, | |
'is_popup': cl.is_popup, | |
'cl': cl, | |
'media': media, | |
'has_add_permission': False,#self.has_add_permission(request), | |
'app_label': app_label, | |
'action_form': action_form, | |
'actions_on_top': self.actions_on_top, | |
'actions_on_bottom': self.actions_on_bottom, | |
'actions_selection_counter': self.actions_selection_counter, | |
} | |
context.update(extra_context or {}) | |
return TemplateResponse(request, self.change_list_template or [ | |
'admin/%s/%s/change_list.html' % (app_label, opts.object_name.lower()), | |
'admin/%s/change_list.html' % app_label, | |
'admin/change_list.html' | |
], context, current_app=self.admin_site.name) | |
def formfield_for_foreignkey(self, db_field, request=None, **kwargs): | |
""" | |
Get a form Field for a ForeignKey. | |
""" | |
user = request.user | |
model = db_field.rel.to | |
permission_ok = user.has_perms(model._meta.app_label + '.' + model.__name__.lower()) | |
db = kwargs.get('using') | |
if db_field.name in self.raw_id_fields and permission_ok: | |
kwargs['widget'] = widgets.ForeignKeyRawIdWidget(db_field.rel, | |
self.admin_site, using=db, readonly=False) | |
elif db_field.name in self.raw_id_fields_readonly or db_field.name in self.raw_id_fields: | |
kwargs['widget'] = widgets.ForeignKeyRawIdWidget(db_field.rel, | |
self.admin_site, using=db, readonly=True) | |
elif db_field.name in self.radio_fields: | |
kwargs['widget'] = widgets.AdminRadioSelect(attrs={ | |
'class': get_ul_class(self.radio_fields[db_field.name]), | |
}) | |
kwargs['empty_label'] = db_field.blank and _('None') or None | |
return db_field.formfield(**kwargs) | |
def formfield_for_manytomany(self, db_field, request=None, **kwargs): | |
""" | |
Get a form Field for a ManyToManyField. | |
""" | |
# If it uses an intermediary model that isn't auto created, don't show | |
# a field in admin. | |
if not db_field.rel.through._meta.auto_created: | |
return None | |
db = kwargs.get('using') | |
user = request.user | |
model = db_field.rel.to | |
permission_ok = user.has_perms(model._meta.app_label + '.' + model.__name__.lower()) | |
if db_field.name in self.raw_id_fields and permission_ok: | |
kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.rel, | |
self.admin_site, using=db, readonly=False) | |
kwargs['help_text'] = '' | |
elif db_field.name in self.raw_id_fields_readonly or db_field.name in self.raw_id_fields: | |
kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.rel, | |
self.admin_site, using=db, readonly=True) | |
kwargs['help_text'] = '' | |
elif db_field.name in (list(self.filter_vertical) + list(self.filter_horizontal)): | |
kwargs['widget'] = widgets.FilteredSelectMultiple(db_field.verbose_name, (db_field.name in self.filter_vertical)) | |
return db_field.formfield(**kwargs) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ForeignKeyRawIdWidget(forms.TextInput): | |
def render(self, name, value, attrs=None): | |
rel_to = self.rel.to | |
if attrs is None: | |
attrs = {} | |
extra = [] | |
readonly = 'true' if self.readonly else 'false' | |
if rel_to in self.admin_site._registry: | |
# The related object is registered with the same AdminSite | |
related_url = reverse('admin:%s_%s_changelist' % | |
(rel_to._meta.app_label, | |
rel_to._meta.module_name), | |
current_app=self.admin_site.name) | |
params = self.url_parameters() | |
if params: | |
url = '?' + '&'.join(['%s=%s' % (k, v) for k, v in params.items()]) | |
else: | |
url = '' | |
if "class" not in attrs: | |
attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript code looks for this hook. | |
# TODO: "lookup_id_" is hard-coded here. This should instead use | |
# the correct API to determine the ID dynamically. | |
extra.append('<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this, %s);"> ' | |
% (related_url, url, name ,readonly)) | |
extra.append('<img src="%s" width="16" height="16" alt="%s" /></a>' | |
% (static('admin/img/selector-search.gif'), _('Lookup'))) | |
output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)] + extra | |
if value: | |
output.append(self.label_for_value(value)) | |
return mark_safe(''.join(output)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment