Filtre el cuadro ManyToMany en Django Admin
-
22-07-2019 - |
Pregunta
Tengo un objeto con una relación de muchos a muchos con otro objeto.
En el Administrador de Django, esto da como resultado una lista muy larga en un cuadro de selección múltiple.
Me gustaría filtrar la relación ManyToMany, así que solo busco las Categorías que están disponibles en la Ciudad que el Cliente ha seleccionado.
¿Es esto posible? ¿Tendré que crear un widget para ello? Y si es así, ¿cómo copio el comportamiento del campo estándar ManyToMany en él, ya que también me gustaría la función filter_horizontal?
Estos son mis modelos simplificados:
class City(models.Model):
name = models.CharField(max_length=200)
class Category(models.Model):
name = models.CharField(max_length=200)
available_in = models.ManyToManyField(City)
class Customer(models.Model):
name = models.CharField(max_length=200)
city = models.ForeignKey(City)
categories = models.ManyToManyField(Category)
Solución
Ok, esta es mi solución usando las clases anteriores. Agregué muchos más filtros para filtrarlo correctamente, pero quería que el código fuera legible aquí.
Esto es exactamente lo que estaba buscando, y encontré mi solución aquí: http://www.slideshare.net/lincolnloop/customizing-the-django-admin#stats-bottom (diapositiva 50)
Agregue lo siguiente a mi admin.py:
class CustomerForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(CustomerForm, self).__init__(*args, **kwargs)
wtf = Category.objects.filter(pk=self.instance.cat_id);
w = self.fields['categories'].widget
choices = []
for choice in wtf:
choices.append((choice.id, choice.name))
w.choices = choices
class CustomerAdmin(admin.ModelAdmin):
list_per_page = 100
ordering = ['submit_date',] # didnt have this one in the example, sorry
search_fields = ['name', 'city',]
filter_horizontal = ('categories',)
form = CustomerForm
Esto filtra las " categorías " ¡Lista sin eliminar ninguna funcionalidad! (es decir: todavía puedo tener mi amado filter_horizontal :))
El ModelForms es muy poderoso, estoy un poco sorprendido de que no esté cubierto más en la documentación / libro.
Otros consejos
Por lo que puedo entender, es que básicamente desea filtrar las opciones mostradas de acuerdo con algunos criterios (categoría según la ciudad).
Puede hacer exactamente eso utilizando el atributo limit_choices_to
de models.ManyToManyField
. Entonces, cambiando la definición de su modelo como ...
class Customer(models.Model):
name = models.CharField(max_length=200)
city = models.ForeignKey(City)
categories = models.ManyToManyField(Category, limit_choices_to = {'available_in': cityId})
Esto debería funcionar, ya que limit_choices_to
, está disponible para este mismo propósito.
Pero una cosa a tener en cuenta, limit_choices_to
no tiene ningún efecto cuando se usa en ManyToManyField con una tabla intermedia personalizada. Espero que esto ayude.
Otra forma es con formfield_for_manytomany
en Django Admin.
class MyModelAdmin(admin.ModelAdmin):
def formfield_for_manytomany(self, db_field, request, **kwargs):
if db_field.name == "cars":
kwargs["queryset"] = Car.objects.filter(owner=request.user)
return super(MyModelAdmin, self).formfield_for_manytomany(db_field, request, **kwargs)
Teniendo en cuenta que " automóviles " son el campo ManyToMany.
Marque este enlace para más información.
Creo que esto es lo que estás buscando:
http://blog.philippmetzler.com/?p=52
usamos django-smart-selects:
http://github.com/digi604/django-smart-selects
Philipp
Como está seleccionando la ciudad y las categorías del cliente de la misma forma, necesitará algunos JavaScript para reducir dinámicamente el selector de categorías a las categorías disponibles en la ciudad seleccionada.
Como dice Ryan, debe haber algún javascript para cambiar dinámicamente las opciones en función de lo que el usuario seleccione. La solución publicada funciona si la ciudad se guarda y el formulario de administración se vuelve a cargar, eso es cuando funciona el filtro, pero piense en una situación en la que un usuario quiera editar un objeto y luego cambie el menú desplegable de la ciudad, pero las opciones en la categoría no se actualizarán.
Category.objects.filter(available_in=cityobject)
Eso debería hacerlo. La vista debe tener la ciudad que el usuario seleccionó, ya sea en la solicitud o como un parámetro para esa función de vista.