I\'m having problems achieving a (probably) rather simple task. I have fully modifiable models (Prodotto, Comune) which are shown as \"addable\" fields, as shown in picture
After a couple of intense days, I finally managed to find a way to achieve that.
A simple trick such as this is more than enough when dealing with this problem within ModelAdmin subclasses (see ClienteAdmin in my code above), so here's the class version without adding capabilities for "Prodotto" field:
@admin.register(Cliente)
class ClienteAdmin(admin.ModelAdmin):
list_display = [
'ragione_sociale', 'forma_societaria', 'titolare', 'partita_iva', ]
list_filter = ['forma_societaria', ]
search_fields = ['ragione_sociale', ]
inlines = [RecapitoInline, SedeInline]
def get_form(self, request, obj=None, **kwargs): # Just added this override
form = super(ClienteAdmin, self).get_form(request, obj, **kwargs)
form.base_fields['prodotto'].widget.can_add_related = False
return form
The real pain comes when dealing with inline classes (TabularInline, StackedInline), as the get_form() function seems not to be called at all, so the previous way won't work.
Explaining all my previous attempts would take too long, and I'm probably not even good enough with Django yet to tell why they didn't work. So let's get straight to the solution, which in fact is not even that complicated.
I subclassed django.contrib.admin.widgets.RelatedFieldWidgetWrapper widget and overridden its render method, so that it doesn't append the "add-another" anchor to the output. Easily done by commenting out a few lines. After doing so, monkeypatching the original RelatedFieldWidgetWrapper with my own version (django.contrib.admin.widgets.RelatedFieldWidgetWrapper = NoAddingRelatedFieldWidgetWrapper) made the trick.
Clearly, for it to work I had to add the import line in the admin.py:
from .widgets import NoAddingRelatedFieldWidgetWrapper
widgets.py
import django.contrib.admin.widgets
from django.utils.safestring import mark_safe
class NoAddingRelatedFieldWidgetWrapper(django.contrib.admin.widgets.RelatedFieldWidgetWrapper):
def render(self, name, value, *args, **kwargs):
from django.contrib.admin.views.main import TO_FIELD_VAR
rel_to = self.rel.to
info = (rel_to._meta.app_label, rel_to._meta.model_name)
self.widget.choices = self.choices
output = [self.widget.render(name, value, *args, **kwargs)]
'''
if self.can_add_related:
related_url = reverse('admin:%s_%s_add' % info, current_app=self.admin_site.name)
url_params = '?%s=%s' % (TO_FIELD_VAR, self.rel.get_related_field().name)
# TODO: "add_id_" is hard-coded here. This should instead use the
# correct API to determine the ID dynamically.
output.append(' '
% (related_url, url_params, name))
output.append('
'
% (static('admin/img/icon_addlink.gif'), _('Add Another')))
'''
return mark_safe(''.join(output))
# Monkeypatch
django.contrib.admin.widgets.RelatedFieldWidgetWrapper = NoAddingRelatedFieldWidgetWrapper
For the sake of completion, here's the final version of the related admin.py:
admin.py
from django.contrib import admin
import django.contrib.admin.widgets
from django.db import models
from .models import Cliente, Prodotto, Sede
from apps.recapito.models import RecapitoCliente
from .widgets import NoAddingRelatedFieldWidgetWrapper
class SedeInline(admin.TabularInline):
model = Sede
extra = 1
def provincia(self, obj):
return obj.comune.provincia
readonly_fields = ['provincia', ]
class RecapitoInline(admin.TabularInline):
model = RecapitoCliente
extra = 1
readonly_fields = ['cliente', 'tipo', 'recapito', ]
@admin.register(Cliente)
class ClienteAdmin(admin.ModelAdmin):
list_display = [
'ragione_sociale', 'forma_societaria', 'titolare', 'partita_iva', ]
list_filter = ['forma_societaria', ]
search_fields = ['ragione_sociale', ]
inlines = [RecapitoInline, SedeInline]
def get_form(self, request, obj=None, **kwargs):
form = super(ClienteAdmin, self).get_form(request, obj, **kwargs)
form.base_fields['prodotto'].widget.can_add_related = False
return form
Shall anyone come out with any better solution, I'll gladly accept it in place of mine.