Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Configuring Many-to-many field in django admin with related_name

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'.
like image 945
Olivier Cant Avatar asked Sep 10 '13 10:09

Olivier Cant


People also ask

How do I use many to many in Django?

ManyToMany. A ManyToMany field is used when a model needs to reference multiple instances of another model. Use cases include: In this case, the user needs to be able to add multiple members to a meal. If the choices were fixed and the same for every user, then we could use django-multiple-select-field.

What is a manytomany field in Django?

A ManyToMany field is used when a model needs to reference multiple instances of another model. Use cases include: In this case, the user needs to be able to add multiple members to a meal. If the choices were fixed and the same for every user, then we could use django-multiple-select-field.

How to query many-to-many relationships in Django?

Many-to-many relationships can be queried using lookups across relationships: Reverse m2m queries are supported : Relation sets can be cleared: Relation sets can be set: Django will automatically generate a table to manage many-to-many relationships. You might need a custom “through” model.

What is related_name in Django built in validation?

related_name – Django Built-in Field Validation. Last Updated : 01 Nov, 2020. The related_name attribute specifies the name of the reverse relation from the User model back to your model. If you don’t specify a related_name, Django automatically creates one using the name of your model with the suffix _set.


1 Answers

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.

like image 198
dan-klasson Avatar answered Nov 15 '22 21:11

dan-klasson