2010-05-18 10 views
12

Ho i seguenti modelli:Django Admin: molti-a-molti casella di riepilogo non si presenta con passaggio di parametri

class Message(models.Model): 
    date = models.DateTimeField() 
    user = models.ForeignKey(User)  
    thread = models.ForeignKey('self', blank=True, null=True) 
    ... 

class Forum(models.Model): 
    name = models.CharField(max_length=24) 
    messages = models.ManyToManyField(Message, through="Message_forum", blank=True, null=True) 
    ... 

class Message_forum(models.Model): 
    message = models.ForeignKey(Message) 
    forum = models.ForeignKey(Forum) 
    status = models.IntegerField() 
    position = models.IntegerField(blank=True, null=True) 
    tags = models.ManyToManyField(Tag, blank=True, null=True) 

Nel sito admin, quando vado per aggiungere/modificare un forum, mi non vedi la lista dei messaggi come ti aspetteresti. Tuttavia, viene visualizzato se rimuovo il parametro 'through' nella dichiarazione ManyToManyField. Cosa succede con quello? Ho registrato tutti e tre i modelli (più Tag) nel sito admin in admin.py.

TIA

risposta

18

documentazione dice:

Quando si specifica un modello di intermediario utilizzando l'argomento attraverso un ManyToManyField, l'amministratore non visualizzerà un widget per impostazione predefinita.

Ma è probabilmente possibile visualizzare i campi M2M nella vista di modifica amministratore anche se è stato definito l'attributo through.

class ForumAdminForm(forms.ModelForm): 
    mm = forms.ModelMultipleChoiceField(
     queryset=models.Message.objects.all(), 
     widget=FilteredSelectMultiple(_('ss'), False, attrs={'rows':'10'})) 

    def __init__(self, *args, **kwargs): 
     if 'instance' in kwargs: 
      initial = kwargs.setdefault('initial', {}) 
      initial['mm'] = [t.service.pk for t in kwargs['instance'].message_forum_set.all()] 

     forms.ModelForm.__init__(self, *args, **kwargs) 

    def save(self, commit=True): 
     instance = forms.ModelForm.save(self, commit) 

     old_save_m2m = self.save_m2m 
     def save_m2m(): 
      old_save_m2m() 

      messages = [s for s in self.cleaned_data['ss']] 
      for mf in instance.message_forum_set.all(): 
       if mf.service not in messages: 
        mf.delete() 
       else: 
        messages.remove(mf.service) 

      for message in messages: 
       Message_forum.objects.create(message=message, forum=instance) 

     self.save_m2m = save_m2m 

     return instance 

    class Meta: 
     model = models.Forum 

class ForumAdmin(admin.ModelAdmin): 
    form = ForumAdminForm 
+0

Funziona perfettamente, ma ha riferimenti non validi nel codice 'servizio'. – alex

10

Date un'occhiata a the official documentation:

+0

Questa soluzione è abbastanza semplice e ha funzionato per me! –

+0

Il tuo link è rotto in quanto la documentazione per 1.6 non è più disponibile sul loro sito. –

2

Ho imparato molto dalla risposta di @ Fedor, ma alcuni commenti e pulizia potrebbero essere ancora utili.

class ForumAdminForm(forms.ModelForm): 
    messages = forms.ModelMultipleChoiceField(
        queryset=Message.objects.all(), 
        widget=FilteredSelectMultiple('Message', False)) 


    # Technically, you don't need to manually set initial here for ForumAdminForm 
    # However, you NEED to do the following for MessageAdminForm 
    def __init__(self, *args, **kwargs): 
     if 'instance' in kwargs: 
      # a record is being changed. building initial 
      initial = kwargs.setdefault('initial', {}) 
      initial['messages'] = [t.message.pk for t in kwargs['instance'].message_forum_set.all()] 
     super(ForumAdminForm, self).__init__(*args, **kwargs) 

    def save(self, commit=True): 
     if not self.is_valid(): 
      raise HttpResponseForbidden 
     instance = super(ForumAdminForm, self).save(self, commit) 
     def save_m2m_with_through(): 
      messages = [t for t in self.cleaned_data['messages'] 
      old_memberships = instance.message_forum_set.all() 
      for old in old_memberships: 
       if old.message not in messages: 
        # and old membership is cleaned by the user 
        old.delete() 
      for message in [x for x in messages not in map(lambda x: x.message, old_memberships)]:     
       membership = Member_forum(message=messsage, forum=instance) 
       # You may have to initialize status, position and tag for your need 
       membership.save() 
     if commit: 
      save_m2m_with_through() 
     else: 
      self.save_m2m = save_m2m_with_through 
     return instance 

    class Meta: 
     model = Forum 
     fields = {'name', 'messages') 

C'è un avvertimento: se si dispone di un altro molti-a-molti nei modelli (cioè senza grazie), super(ForumAdminForm, self).save(self, commit) imposterà self.save_m2m in caso commit è False. Tuttavia, chiamare questo causerebbe un errore, perché questa funzione cerca anche di salvare anche i molti a molti con attraverso. Potrebbe essere necessario salvare manualmente tutte le altre relazioni molti-a-molti o rilevare l'eccezione o altro.

Problemi correlati