2012-03-26 8 views
23

Diciamo che ho un semplice blog applicazione in Django 1.4:Aggiunta di un ManyToManyWidget sul retro di un ManyToManyField nel Django Admin

class Post(models.Model): 
    title = … 
    published_on = … 
    tags = models.ManyToManyField('Tag') 

class Tag(models.Model): 
    name = … 

vale a dire un post ha molti tag. Sull'amministratore di Django, ottengo un bel po 'di <select multi> se includo lo tags nello fields per lo PostAdmin. C'è un modo semplice per includere l'elenco dei post (come un semplice <select multi>) nel TagAdmin? Ho provato a inserire fields = ['name', 'posts'] nel TagAdmin e ho ottenuto un errore ImproperlyConfigured. (stesso risultato per post_set).

Sono d'accordo con Django, quindi potrei creare un oggetto AdminForm e Admin adatto, ma spero che ci sia un Right Way ™ per farlo.

+0

stai cercando modifiche in linea? https://docs.djangoproject.com/en/dev/ref/contrib/admin/#working-with-many-to-many-models – Jingo

+0

Puoi impostare il modello intermedio usando l'attributo 'through' e impostare poche linee in Admin. Ma questa è tutt'altro che bella soluzione. Date un'occhiata a questo biglietto: https://code.djangoproject.com/ticket/897 – ilvar

+1

Sto cercando la stessa cosa - sembra abbastanza semplice. Hai mai trovato una soluzione? –

risposta

10

Un po 'in ritardo alla festa, ma questa è la soluzione che funziona per me (nessuna magia):

# admin.py 

from django.contrib import admin 
from models import Post 

class TagPostInline(admin.TabularInline): 
    model = Post.tags.through 
    extra = 1 

class PostAdmin(admin.ModelAdmin): 
    inlines = [TagPostInline] 

admin.site.register(Post, PostAdmin) 

Riferimento: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#working-with-many-to-many-models

+1

Sì, ma ciò non fa realmente ciò che la domanda chiede, ovvero per un widget [multiplo] selezionato. –

23

Ciò è possibile a che fare con un modulo personalizzato.

from django.contrib import admin 
from django import forms 

from models import Post, Tag 

class PostAdminForm(forms.ModelForm): 
    tags = forms.ModelMultipleChoiceField(
     Tag.objects.all(), 
     widget=admin.widgets.FilteredSelectMultiple('Tags', False), 
     required=False, 
    ) 

    def __init__(self, *args, **kwargs): 
     super(PostAdminForm, self).__init__(*args, **kwargs) 
     if self.instance.pk: 
      self.initial['tags'] = self.instance.tags.values_list('pk', flat=True) 

    def save(self, *args, **kwargs): 
     instance = super(PostAdminForm, self).save(*args, **kwargs) 
     if instance.pk: 
      instance.tags.clear() 
      instance.tags.add(*self.cleaned_data['tags']) 
     return instance 

class PostAdmin(admin.ModelAdmin): 
    form = PostAdminForm 

admin.site.register(Post, PostAdmin) 

Che False in là può essere sostituito con un True se si desidera impilati verticalmente widget di.

+1

Um ... Mi sto perdendo qualcosa, o questo manca totalmente il punto dell'OP? Il punto è "elenco dei post in TagAdmin". TagAdmin, non PostAdmin. L'approccio sembra buono però. – frnhr

+0

Forse scambia TagAdmin e PostAdmin. –

+0

Forse. Inoltre, save non funzionerà in questo modo quando si aggiunge un nuovo oggetto, perché l'amministratore di Django lo chiama con 'form.save (commit = False)', quindi no pk. Invece, sposta quel codice in "TagAdmin.save_model (...)". – frnhr

4

La soluzione di Matthew non ha funzionato per me (Django 1.7) durante la creazione di una nuova voce, quindi ho dovuto cambiarla un po '. Spero che sia utile per qualcuno :)

class PortfolioCategoriesForm(forms.ModelForm): 
    items = forms.ModelMultipleChoiceField(
     PortfolioItem.objects.all(), 
     widget=admin.widgets.FilteredSelectMultiple('Portfolio items', False), 
     required=False 
    ) 

    def __init__(self, *args, **kwargs): 
     super(PortfolioCategoriesForm, self).__init__(*args, **kwargs) 
     if self.instance.pk: 
      initial_items = self.instance.items.values_list('pk', flat=True) 
      self.initial['items'] = initial_items 

    def save(self, *args, **kwargs): 
     kwargs['commit'] = True 
     return super(PortfolioCategoriesForm, self).save(*args, **kwargs) 

    def save_m2m(self): 
     self.instance.items.clear() 
     self.instance.items.add(*self.cleaned_data['items']) 
0

modificare i modelli di aggiungere campo inverso:

# models.py 
from django.db import models 

class Post(models.Model): 
    title = models.CharField(max_length=100) 
    published_on = models.DateTimeField() 
    tags = models.ManyToManyField('Tag') 

class Tag(models.Model): 
    name = models.CharField(max_length=10) 
    posts = models.ManyToManyField('blog.Post', through='blog.post_tags') 

Poi, nel modo standard aggiungere campo per ModelAdmin:

#admin.py 
from django.contrib import admin 

class TagAdmin(admin.ModelAdmin): 
    list_filter = ('posts',) 

admin.site.register(Tag, TagAdmin) 
+0

Questo rompe migrazioni, syncdb ecc. C'è una soluzione hacky: https://djangosnippets.org/snippets/1295/ ma non l'ho provato con le nuove migrazioni di Django –

+0

Con Django 1.11, questo in realtà funziona bene ora, come per quanto posso dire. 'makemigrations' genera una migrazione per il campo M2M ridondante, ma si applica senza problemi. –

Problemi correlati