mfalcon
mfalcon

Reputation: 880

Add custom html between two model fields in Django admin's change_form

Let's say I've two models:

class Book(models.Model):
    name = models.CharField(max_length=50)
    library = models.ForeignKeyField('Library')

class Library(models.Model):
    name = models.CharField(max_length=50)   
    address = models.CharField(max_length=50)  
    tel = models.CharField(max_length=50)  

Is there a nice way to add some html(a readonly input field) between name and address in the Library change_form template?. I'm doing it overriding admin/includes/fieldset.html but it's getting messy and I can't find a way to display the html exactly where I want to. For example, if I want to add html displaying the amount of books that the library has below the name field I woul do this:

{% for field in line %}
    ...
    {% if field.field.name == 'name' %}
        {{ field.field }}
        <div class="form-row total_books">
            <div>
                <label for="total_books">Total books:</label>
                <input type="text" maxlength="10" name="totbooks" id="totbooks" readonly="readonly">
            </div>
       </div>
    {% else %}
        {{ field.field }}
    {% endif %}
    ...
{% endfor %}

New solution:

I've found a nicer way I think, but I don't know why am I getting this error: "PresupuestoAdmin.readonly_fields1, 'name' is not a callable or an attribute of PresupuestoAdmin' or found in the model 'Presupuesto' ". It seems that the 'name' field is not added to the form used by the admin.

class FoooAdminForm(forms.ModelForm): 
    name = models.CharField(max_length=100) 
    class Meta: 
        model = Foo 

class FooAdmin(admin.ModelAdmin): 
    form = FooAdminForm 
    fieldsets = ( 
        (None, { 'fields': ('id', 'name', 'date')}), 
    ) 
    readonly_fields = ('id', 'name') 

admin.site.register(Foo, FooAdmin) 

Upvotes: 6

Views: 7914

Answers (3)

Udi
Udi

Reputation: 30472

models.py:

class Library(models.Model):
    name = models.CharField(max_length=50)   
    address = models.CharField(max_length=50)  
    tel = models.CharField(max_length=50)

    def book_count(self):
      return self.book_set.count()

admin.py:

class LibraryAdmin(admin.ModelAdmin): 
    fieldsets = ( 
        (None, { 'fields': ('name', 'book_count', 'address', 'tel' )}), 
    ) 
    readonly_fields = ('book_count',) 

admin.site.register(Library, LibraryAdmin)

Upvotes: 12

mfalcon
mfalcon

Reputation: 880

class FooAdminForm(forms.ModelForm):
    name = forms.CharField(max_length=100, 
                widget=forms.TextInput(attrs={'readonly': 'readonly'}))

    def __init__(self, *args, **kwargs): 
        super(FooAdminForm, self).__init__(*args, **kwargs)
        self.fields['name'].required = False
        if kwargs.has_key('instance'): 
            instance = kwargs['instance'] 
            # do something with the instance here if you want 

    class Meta:
        model = Foo

class FooAdmin(admin.ModelAdmin):
    form = FooAdminForm

    fieldsets = (
        (None, {
            'fields': ('id', 'name',),})
    )
    readonly_fields = ('id',) 

Upvotes: 2

Udi
Udi

Reputation: 30472

This might not give you exactly what you need, but you can override the widget for the field bar in model Foo by adding this to your admin.py:

from django import forms
from django.contrib import admin
from django.contrib.admin import site
from django.contrib.admin.widgets import AdminTextInputWidget
from django.utils.safestring import mark_safe
from models import Foo

class AdminFooWidget(AdminTextInputWidget):
    def render(self, name, value, attrs=None):
        s = super(AdminTextInputWidget, self).render(name, value, attrs)
        return mark_safe(s+u'<br/><b>Hello world!</b>')

class FooAdminForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(FooAdminForm, self).__init__(*args, **kwargs)
        self.fields['bar'].widget = AdminFooWidget()

class FooAdmin(admin.ModelAdmin):
    form = FooAdminForm     

site.register(Foo, FooAdmin)

Upvotes: 3

Related Questions