views:

56

answers:

1

Dears

I have a django model as following

class Project(models.Model)
      name=models.CharField(max_length=200)

class Application(models.Model)
      proj=models.ForeignKey(Project, null=True, blank=True)

I need to modify the admin form of the project to be able to assign multiple applications to the project, so in the admin.py I have created a ModelAdmin class for the project as following

class ProjectAdmin(ModelAdmin)
      form=projectForm
      project_apps=[]

and the project form as following

class ProjectForm(forms.ModelForm):
    class Meta:
       model = Project

    project_apps =forms.ModelMultipleChoiceField(queryset=Application.objects.all(),required=False,)
def __init__(self, *args, **kwargs):
    super(ProjectForm, self).__init__(*args, **kwargs)
    if self.instance.id is not None:
        selected_items = [ values[0] for values in Application.objects.filter(project=self.instance) ]
        self.fields['project_apps'].initial = selected_items

def save(self,commit=True):
    super(ProjectForm,self).save(commit)
    return self.instance

by doing this I have a multiple select in the create/edit project form. what I need is to override the save method to save a reference for the project in the selected applications?

how can I get the selected applications ????

A: 

Not entirely sure what you're trying to do, but maybe this?

def save(self,commit=True):
    kwargs.pop('commit') # We're overriding this with commit = False
    super(ProjectForm,self).save(commit)
    if self.instance:
        for a in self.cleaned_data['project_apps']:
            a.proj = self.instance
            a.save()
    return self.instance

Now, I can't remember if in this case, self.cleaned_data['project_apps'] will actually contain a list of Application objects or not. I suspect it will, but if not this function will take care of that:

def clean_project_apps(self):
    app_list = self.cleaned_data['project_apps']
    result = []
    for a in app_list:
        try:
            result.append(Application.objects.get(pk=a)
        except Application.DoesNotExist:
            raise forms.ValidationError("Invalid application record") # to be safe
    return result

All in all I think this form is a bad idea though, because basically what is happening here is you're displaying all of the application records which doesn't make sense, since most of them will be associated with other projects.

Oh oh oh!!! Just noticed you wanted this to show up in a Multiple Select list!

You're (probably) doing it wrong

A multiple select means this isn't a one-to-many relationship. It's a many-to-many relationship.

This is what you want to do, easy peasy, doesn't require any custom forms or anything.

class Project(models.Model)
      name=models.CharField(max_length=200)
      project_apps = models.ManyToMany('Application', null=True, blank=True)

class Application(models.Model)
      # nothing here (NO foreign key, you want more than one App/Proj and vice versa)

Indicating that this is a many-to-many field in Project will automagically create the multiple select box in admin. Ta da!

Jordan Reiter
I'd just like to add that if no, really you want it to be a one-to-many relationship, then a multiple select box is the wrong interface to be using. It would be like making a car where turning the wheel right means accelerate, while turning left means slowing down. A multiple choice field means "Look, for any given record, you can select one or more of these options!" That's a many-to-many relationship. If you really mean "These are potential children of this main project" then that is not the right interface and although you don't personally like it, an inline admin is the correct interface.
Jordan Reiter