Configuring Many-to-many field in django admin with related_name
Asked Answered
L

2

10

I have the following model and admin defined in djando 1.5. This is a many-to-many relationship between subnet and vlan. I use the related_name option in the ManyToMany field to be able to get the collection of vlan from the related subnet object. Adding subnet to vlans from the admin works well. However when I try to add an horizontal_filer to the subnet admin in order to add van to its vlan set I get an error saying that the vlans attribute doesn't exist. I'm using subnet object in some view and I can access the vlans attribute just right.

What am I doing wrong here ? I've seen similar post but I couldn't adapt any of the proposed solution with success.

Thanks for your help

model.py

from django.db import models

class Subnet(models.Model):
    networkAddress = models.CharField(max_length=15)
    size = models.IntegerField()

    def __unicode__(self):
        return "%s/%s" % (self.networkAddress, self.size)

class IpAddress(models.Model):
    ipAddress = models.CharField(max_length=15)
    subnet = models.ForeignKey(Subnet)

    def __unicode__(self):
        return "%s" % (self.ipAddress)

class Vlan(models.Model):
    number = models.IntegerField()
    description = models.CharField(max_length=150)
    subnets = models.ManyToManyField(Subnet, related_name='vlans', blank=True)

    def __unicode__(self):
        return "VLAN %s (%s)" % (self.number, self.description)

admin.py

from network.models import Subnet, IpAddress, Vlan
from django.contrib import admin

class SubnetAdmin(admin.ModelAdmin):
    filter_horizontal = ('vlans',)

admin.site.register(Subnet, SubnetAdmin)
admin.site.register(IpAddress)
admin.site.register(Vlan)

and the error I get

Request Method:     GET
Request URL:    http://127.0.0.1:8000/admin/
Django Version:     1.5.2
Exception Type:     ImproperlyConfigured
Exception Value:    

'SubnetAdmin.filter_horizontal' refers to field 'vlans' that is missing from model 'network.Subnet'.
Laughton answered 10/9, 2013 at 10:27 Comment(4)
I had the exact same problem yesterday. I just solved it by moving the ManyToMany declaration to the other model. Maybe you can try creating a model method that returns self.vlans.Cadre
Well, If I change de ManyToManyField to the other model, I get the same problem the other way arround. In this case I really want to be able to edit the relation from both endsLaughton
So then i'd try the model method methodCadre
so you mean something like this : def vlans(self): return self.vlans in Subnet model ? If so I tried it and it throws the same error :(Laughton
C
11

Apparently this is an 8 year old feature request. There is django-admin-extend. Or you could just throw something like this in there:

from django.contrib import admin as admin_module

class SiteForm(ModelForm):
    user_profiles = forms.ModelMultipleChoiceField(
        label='Users granted access',
        queryset=UserProfile.objects.all(),
        required=False,
        help_text='Admin users (who can access everything) not listed separately',
        widget=admin_module.widgets.FilteredSelectMultiple('user profiles', False))

class SiteAdmin(admin_module.ModelAdmin):
    fields = ('user_profiles',)

    def save_model(self, request, obj, form, change):
        # save without m2m field (can't save them until obj has id)
        super(SiteAdmin, self).save_model(request, obj, form, change) 
        # if that worked, deal with m2m field
        obj.user_profiles.clear()
        for user_profile in form.cleaned_data['user_profiles']:
             obj.user_profiles.add(user_profile)

    def get_form(self, request, obj=None, **kwargs):
        if obj:
            self.form.base_fields['user_profiles'].initial = [ o.pk for o in obj.userprofile_set.all() ]
        else:
            self.form.base_fields['user_profiles'].initial = []
        return super(SiteAdmin, self).get_form(request, obj, **kwargs)

It should give you a filter_horizontal when you specify it in the fields tuple.

Cadre answered 10/9, 2013 at 15:15 Comment(1)
As of 2024, its a 19 year old(!!!) feature request, and honestly at this point its a 19yo bug request.Precession
P
0

I have created a public gist that covers this specific issue.

https://gist.github.com/Wtower/0b181cc06f816e4feac14e7c0aa2e9d0

The general idea is to use that specific base form class in order to define a 'reverse' m2m field for the form that would otherwise not include it. Then easily override the form in the admin class.

Although the code is not quite complicated, the gist code is somehow long to include within the answer so apologies for that.

Peril answered 4/4, 2016 at 0:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.