2010-10-05 17 views
1

Supposons que je les modèles suivants -Django - Qu'est-ce qu'une bonne façon de gérer ManyToMany avec table intermédiaire dans des formes et des vues

class Item(models.Model): 
    name = models.CharField(max_length=150) 
    value = models.DecimalField(max_digits=12,decimal_places=2) 

class Organization(models.Model): 
    name = models.CharField(max_length=150) 
    items = models.ManyToManyField(Item, through='Customizable') 

class Customizable(models.Model): 
    organization = models.ForeignKey(Organization) 
    item = models.ForeignKey (Item) 
    value = models.DecimalField(max_digits=12,decimal_places=2) 

Plus souvent qu'autrement, lorsque items sont « assignés » à un organization, ils seront ont la même valeur que celle initialement enregistrée dans l'objet Item associé. Mais dans certains cas, un élément affecté à une organisation peut avoir un value substitué (d'où le modèle intermédiaire). Étant donné que l'annulation de la valeur d'origine se produit rarement (mais cela arrive), je veux permettre à l'utilisateur de simplement sélectionner items dans une liste de Item instances pour les affecter à une instance d'organisation. L'utilisateur aura alors la possibilité de redéfinir des valeurs individuelles plus tard, une fois l'attribution en bloc terminée.

donc je la ModelForm simple suivante -

class AssignItemsForm(forms.ModelForm): 
    items = forms.ModelMultipleChoiceField(queryset=Item.objects.all(),required=False,widget=forms.CheckboxSelectMultiple) 
    class Meta: 
     model = Organization 
     exclude = ('name',) 

Maintenant, depuis que je suis un modèle through, un simple form.save() ne fonctionnera pas. Je dois (i) enregistrer Customizable instances correspondant aux éléments sélectionnés par l'utilisateur et (ii) assurez-vous que les instances ont subsisté Customizable la value bonne prise du value pris de l'instance correspondante item liés par foreignkey.

Je suis en train de le manipuler dans une vue (mais mon esprit est bloqué) -

def assign_items(request, oid): 
    organization = Organization.objects.get(id=oid) 
    if request.method == 'POST': 
     form = AssignItemsForm(data=request.POST, instance=organization) 
     if form.is_valid(): 
      current_organization = form.save(commit=False) 
      # 
      #placeholder to save Customizable instances here 
      # 
      return HttpResponseRedirect(reverse('redirect-someplace-else')) 
    else: 
     form = AssignItemsForm(instance=organization,) 
    return render_to_response("assign_items.html", {"form": form,}, context_instance=RequestContext(request)) 

Répondre

0

J'aborder cette question d'une manière différente. Vous avez un modèle intermédiaire pour votre m2m. Par conséquent, je dirais que AssignItemsForm devrait être soutenu par ce modèle intermédiaire. Par conséquent je le changerais comme suit:

# forms.py 
class AssignItemsForm(forms.ModelForm): 
    value = forms.DecimalField(max_digits=12, decimal_places=2, required = False) 

    class Meta: 
     model = Customizable 

Ensuite, la question de permettre aux utilisateurs de choisir une valeur différente. Pour ce faire j'ai rendu le champ value du modèle optionnel (required = False). Je vérifie ensuite si l'utilisateur a fourni une valeur explicite. Sinon, je suppose que la valeur par défaut de Item doit être utilisée. Pour cela, je suis redéfinissant la méthode clean de la forme:

def clean(self): 
     super(AssignItemsForm, self).clean() 
     value, item = self.cleaned_data.get('value'), self.cleaned_data.get('item') 
     if not value: 
      value = item.value 
     self.cleaned_data['value'] = value 
     return self.cleaned_data 

Et enfin je l'ai testé ce dans l'admin.

# admin.py 
from app.forms import AssignItemsForm 

class CAdmin(admin.ModelAdmin): 
    form = AssignItemsForm 

admin.site.register(Item) 
admin.site.register(Organization) 
admin.site.register(Customizable, CAdmin) 

De cette façon, vous pouvez continuer à utiliser form.save() évitant ainsi la manipulation personnalisée dans la vue. Vous devrez modifier légèrement votre vue pour vous assurer que l'organisation est sélectionnée automatiquement pour l'attribution des éléments.

# views.py 
def assign_items(request, oid): 
    organization = Organization.objects.get(id=oid) 
    if request.method == 'POST': 
     form = AssignItemsForm(data=request.POST.copy()) 
     form.save() 
    else: 
     form = AssignItemsForm(initial = {'organization': organization}) 
    ... 
+0

Depuis remplaçant des valeurs ne se produit pas souvent que, je veux vraiment l'utilisateur d'être en mesure d'attribuer de nombreux éléments à une organisation d'un seul coup. Je pense que ce serait plus pratique pour l'utilisateur dans ce cas. – chefsmart

1

Vous devez utiliser la méthode save_m2m:

def assign_items(request, oid): 
    organization = Organization.objects.get(id=oid) 
    if request.method == 'POST': 
     form = AssignItemsForm(data=request.POST, instance=organization) 
     if form.is_valid(): 
      current_organization = form.save(commit=False) 

      current_organization.save() 

      form.save_m2m() 

      return HttpResponseRedirect(reverse('redirect-someplace-else')) 
    else: 
     form = AssignItemsForm(instance=organization,) 
    return render_to_response("assign_items.html", {"form": form,}, context_instance=RequestContext(request)) 

Regardez ici pour plus d'informations:

http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#the-save-method

+0

Pas vraiment. C'est ce que j'ai besoin de faire * avant * save_m2m est ce que je me bats la tête contre. – chefsmart