2010-05-18 6 views
12

mam następujące modele:Django Admin: Wiele do wielu listbox nie pokazać się z przelotowym parametru

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) 

w lokacji administratora, kiedy idę, aby dodać/zmienić forum, ja nie widzę pola listy wiadomości, jak można się spodziewać. Jednak pojawia się, jeśli usunięto parametr "through" w deklaracji ManyToManyField. Co z tym? Zarejestrowałem wszystkie trzy modele (plus Tag) na stronie administratora w admin.py.

TIA

Odpowiedz

18

Dokumentacja mówi:

Po określeniu modelu pośrednika używając poprzez argumentu do ManyToManyField, administrator nie będzie wyświetlany widget domyślnie.

Prawdopodobnie możliwe jest wyświetlanie pól M2M w widoku zmiany administratora, nawet jeśli zdefiniowano atrybut 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

Działa doskonale, ale ma niepoprawne odwołania w kodzie 'service'. – alex

10
+0

To rozwiązanie jest dość proste i działa dla mnie! –

+0

Twój link jest uszkodzony, ponieważ dokumentacja dla wersji 1.6 nie jest już dostępna w jego witrynie. –

2

Wiele się nauczyłem z odpowiedzi @ Fedora, ale niektóre komentarze i czyszczenie mogą być nadal korzystne.

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') 

Jest jedno zastrzeżenie: jeśli masz inny wiele do wielu relacji w modelach (czyli bez Through), super(ForumAdminForm, self).save(self, commit) ustawi self.save_m2m w przypadku commit jest fałszywy. Jednak wywołanie tego spowodowałoby błąd, ponieważ ta funkcja próbuje także zapisać wiele-do-wielu z wykorzystaniem również. Może być konieczne ręczne zapisanie wszystkich innych relacji typu wiele-do-wielu lub przechwycenie wyjątku.

Powiązane problemy