Philippe Fisher
Philippe Fisher

Reputation: 596

Django Admin foreign key, more detailed

Im trying to customise my admin. I have a model with a foreign key to an another model. I currently have a dropdown that display a list of possible keys to chose from, but the list only shows the title.

model.py

class Equipment(models.Model):
    noequipment = models.IntegerField('Equipment #' ,db_column='Noequipment', primary_key=True)  # Field name made lowercase.
    nom = models.CharField('Name',db_column='Nom', max_length=50, blank=True)  # Field name made lowercase.
    ...
    nooffice = models.ForeignKey('Office', db_column='NoOffice', blank=True, null=True, verbose_name='Office')  # Field name made lowercase.
    ...

    class Meta:
        db_table = 'equipment'
        ordering = ('nom',)

    def __str__(self):
        return self.nom


class Office(models.Model):
    nooffice = models.IntegerField(db_column='NoOffice', primary_key=True)  # Field name made lowercase.
    officename = models.CharField(db_column='OfficeName', max_length=50, blank=True)  # Field name made lowercase.
    adresse = models.CharField(db_column='Adresse', max_length=50, blank=True)  # Field name made lowercase.
    ville = models.CharField(db_column='Ville', max_length=50, blank=True)  # Field name made lowercase.
    codepostal = models.CharField(db_column='CodePostal', max_length=50, blank=True)  # Field name made lowercase.

    class Meta:
        db_table = 'office'

    def __str__(self):
        return self.officename

All I need is that the admin would show a table with the attribute values on top of the drop down.

enter image description here

also here admin.py (Need special ModelAdmin because im using multiple DB as per https://docs.djangoproject.com/en/1.8/topics/db/multi-db/ )

from django.contrib import admin
from .models import Equipment, Manufacturier, Office, Devicetype, Backdoor, Passage, Systadmin, Applicationadmin, Application, Os
from django import forms
from forms import EquipmentAdminForm, ManufacturierAdminForm, OfficeAdminForm, DevicetypeAdminForm, BackdoorAdminForm, PassageAdminForm, SystadminAdminForm, ApplicationadminAdminForm, ApplicationAdminForm, OsAdminForm

class VCOEModelAdmin(admin.ModelAdmin):
# A handy constant for the name of the alternate database.
    using = 'vcoe'

    def save_model(self, request, obj, form, change):
        # Tell Django to save objects to the 'other' database.
        obj.save(using=self.using)

    def delete_model(self, request, obj):
        # Tell Django to delete objects from the 'other' database
        obj.delete(using=self.using)

    def get_queryset(self, request):
        # Tell Django to look for objects on the 'other' database.
        return super(VCOEModelAdmin, self).get_queryset(request).using(self.using)

    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
        # Tell Django to populate ForeignKey widgets using a query
        # on the 'other' database.
        return super(VCOEModelAdmin, self).formfield_for_foreignkey(db_field, request=request, using=self.using, **kwargs)

    def formfield_for_manytomany(self, db_field, request=None, **kwargs):
        # Tell Django to populate ManyToMany widgets using a query
        # on the 'other' database.
        return super(VCOEModelAdmin, self).formfield_for_manytomany(db_field, request=request, using=self.using, **kwargs)

class VCOETabularInline(admin.TabularInline):
    using = 'vcoe'

    def get_queryset(self, request):
        # Tell Django to look for inline objects on the 'other' database.
        return super(VCOEMTabularInline, self).get_queryset(request).using(self.using)

    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
        # Tell Django to populate ForeignKey widgets using a query
        # on the 'other' database.
        return super(VCOEMTabularInline, self).formfield_for_foreignkey(db_field, request=request, using=self.using, **kwargs)

    def formfield_for_manytomany(self, db_field, request=None, **kwargs):
        # Tell Django to populate ManyToMany widgets using a query
        # on the 'other' database.
        return super(VCOEMTabularInline, self).formfield_for_manytomany(db_field, request=request, using=self.using, **kwargs)

class OfficeAdmin(VCOEModelAdmin):
    form = OfficeAdminForm

class EquipmentAdmin(VCOEModelAdmin):
    form = EquipmentAdminForm

...

admin.site.register(Equipment, EquipmentAdmin)
...
admin.site.register(Office, OfficeAdmin)
...

and forms.py

from django import forms
from .models import Equipment, Manufacturier, Office, Devicetype, Backdoor, Passage, Systadmin, Applicationadmin, Application, Os

class EquipmentAdminForm(forms.ModelForm):
    class Meta:
        model = Equipment
        exclude = ['noequipment']

...

class OfficeAdminForm(forms.ModelForm):
    class Meta:
        model = Office
        exclude = ['nooffice']

Upvotes: 2

Views: 803

Answers (1)

drdaeman
drdaeman

Reputation: 11471

I see two possible approaches:

  1. The simplest approach is to make __str__ (or __unicode__, depending on Python version) on your model to include extra information you want to see. E.g.:

    class Office(models.Model):
        ...
        def __unicode__(self):
            return u"{0.officename} ({0.ville}, {0.addresse})".format(self)
    
  2. The most flexible but most complicated is to create a custom widget. There are some readily available libraries that may or may not suit your needs and tastes. For example, you may consider taking a look at django-autocomplete-light's ModelChoiceField and it`s autocomplete styling options.

    The process isn't trivial, but the overall outline is:

    1. Create your own widget class or take something that's readily available.
    2. Create a ModelForm that uses this widget. Some libraries provide a custom fields that are pre-configured to use the widget, if you have a self-implemented one, you can consider writing a field or using Django's built-in ModelChoiceField with widget argument. Some libraries have their own conventions, if you have own implementation, then you can go with something like:

      class EquipmentForm(forms.ModelForm):
          class Meta:
              model = Equipment
              widgets = {
                  "office": OfficeSelectWidget(...)
              }
      
    3. In your ModelAdmin class refer to your form, so Django would use it instead of automatically-generated one.

If you're just experimenting or learning - and have enough time to spare, I suggest go the complicated route and write everything on your own, so you'd know how things work under the hood. Then never do this again, but use batteries from PyPI. If you just want things quick, take a look at abovementioned django-autocomplete-light library, which should be quite simple to get started. Just follow their tutorials and examples, starting from the basic autocomplete field then extending it with custom styling.

Upvotes: 2

Related Questions