2012-03-26 14 views
23

Digamos que tengo una aplicación sencilla de blog en Django 1.4:Adición de un ManyToManyWidget a la inversa de un ManyToManyField en la administración de Django

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

class Tag(models.Model): 
    name = … 

es decir, un post tiene muchas etiquetas. En el administrador de Django, obtengo un pequeño y agradable <select multi> si incluyo tags en el fields para el PostAdmin. ¿Hay una manera fácil de incluir la lista de las publicaciones (como simple <select multi>) en el TagAdmin? Intenté poner fields = ['name', 'posts'] en el TagAdmin y obtuve un error ImproperlyConfigured. (mismo resultado para post_set).

Estoy bien con Django, así que podría armar un objeto AdminForm y Admin correcto, pero espero que haya un Right Way ™ para hacerlo.

+0

está buscando ediciones en línea? https://docs.djangoproject.com/en/dev/ref/contrib/admin/#working-with-many-to-many-models – Jingo

+0

Puede configurar el modelo de intermediario con el atributo 'through' y configurar unas pocas líneas en Administración. Pero eso está lejos de ser una solución hermosa. Echa un vistazo a este ticket: https://code.djangoproject.com/ticket/897 – ilvar

+1

Estoy buscando lo mismo, parece bastante simple. ¿Encontraste alguna solución? –

Respuesta

10

Un poco tarde a la fiesta, pero esta es la solución que funciona para mí (sin 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) 

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

+1

Sí, pero eso realmente no hace lo que la pregunta hace, que es para un widget selecto [múltiple]. –

23

Esto es posible hacerlo con un formulario personalizado.

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) 

Eso False allí puede ser reemplazado con un True si desea apilados verticalmente de widgets.

+1

Um ... ¿Me estoy perdiendo algo, o es esto totalmente perdido el objetivo de la OP? El punto es "lista de las publicaciones en TagAdmin". TagAdmin, no PostAdmin. El enfoque se ve bien sin embargo. – frnhr

+0

Quizás intercambie TagAdmin y PostAdmin. –

+0

Quizás. Además, guardar no funcionará de esta manera cuando se agrega un nuevo objeto, porque el administrador de Django lo llama con 'form.save (commit = False)', por lo tanto no pk. En cambio, mueva ese código a 'TagAdmin.save_model (...)'. – frnhr

4

La solución de Matthew no funcionó para mí (Django 1.7) al crear una nueva entrada, así que tuve que cambiarla un poco. Espero que sea útil para alguien :)

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

Modificar sus modelos para agregar 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') 

Luego, en forma estándar Agregar campo a ModelAdmin:

#admin.py 
from django.contrib import admin 

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

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

Esto rompe las migraciones, syncdb, etc. Hay una solución hacky: https://djangosnippets.org/snippets/1295/ pero no lo he intentado con las nuevas migraciones de Django –

+0

Con Django 1.11, esto realmente funciona bien ahora, ya que Hasta donde yo sé. 'makemigrations' genera una migración para el campo redundante M2M, pero se aplica sin problemas. –

Cuestiones relacionadas