views:

98

answers:

3

I've seen all the howtos about how you can set a ModelMultipleChoiceField to use a custom queryset and I've tried them and they work. However, they all use the same paradigm: the queryset is just a filtered list of the same objects.

In my case, I'm trying to get the admin to draw a multiselect form that instead of using usernames as the text portion of the , I'd like to use the name field from my account class.

Here's a breakdown of what I've got:

# models.py
class Account(models.Model):
    name = models.CharField(max_length=128,help_text="A display name that people understand")
    user = models.ForeignKey(User, unique=True) # Tied to the User class in settings.py

class Organisation(models.Model):
    administrators = models.ManyToManyField(User)


# admin.py
from django.forms import ModelMultipleChoiceField
from django.contrib.auth.models import User

class OrganisationAdminForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        from ethico.accounts.models import Account
        self.base_fields["administrators"] = ModelMultipleChoiceField(
            queryset=User.objects.all(),
            required=False
        )
        super(OrganisationAdminForm, self).__init__(*args, **kwargs)

class Meta:
    model = Organisation

This works, however, I want queryset above to draw a selectbox with the Account.name property and the User.id property. This didn't work:

queryset=Account.objects.all().order_by("name").values_list("user","name")

It failed with this error:

'tuple' object has no attribute 'pk'

I figured that this would be easy, but it's turned into hours of dead-ends. Anyone care to shed some light?

A: 

The queryset needs to be a QuerySet, when you do values_list you get a list so that won't work.

If you want to change the default display of models, just override __unicode__. See http://docs.djangoproject.com/en/dev/ref/models/instances/#unicode

For example:

def __unicode__(self):
    return u"%s for %s" % (self.name, self.user)

Django will use __unicode__ whenever you asks it to print a model. For testing you can just load up a model in the shell and do print my_instance.

knutin
I would do exactly that, except that in this case, the model in question is User, which I don't control. Do you know how I would adjust the __unicode__ property of the User class?
Daniel Quinn
+1  A: 

You can use a custom widget, override its render method. Here's what I had done for a text field :

class UserToAccount(forms.widgets.TextInput):
    def render(self, name, value, attrs=None):
        if isinstance(value, User) :
            value = Account.objects.get(user=value).name
        return super (UserToAccount, self).render(name, value, attrs=None)        

Then of course, use the widget parameter of your administrator field, in order to use your custom widget. I don't know if it can be adapted for a select, but you can try out.

sebpiq
Why have I been down-voted ? Can you explain ?
sebpiq
A: 

Taking a queue from sebpiq, I managed to figure it out:

class OrganisationAdminForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):

        from django.forms import MultipleChoiceField
        from ethico.accounts.models import Account

        self.base_fields["administrators"] = MultipleChoiceField(
            choices=tuple([(a.user_id, a.name) for a in Account.objects.all().order_by("name")]),
            widget=forms.widgets.SelectMultiple,
            required=False
        )

        super(OrganisationAdminForm, self).__init__(*args, **kwargs)

    class Meta:
        model = Organisation


class OrganisationAdmin(admin.ModelAdmin):
    form = OrganisationAdminForm


admin.site.register(Organisation, OrganisationAdmin)

The key was abandoning the queryset altogether. Once I went with a fixed choices= parameter, everything just worked. Thanks everyone!

Daniel Quinn