2009-05-15 23 views
10

Dado un modelo con campos ForeignKeyField (FKF) o ManyToManyField (MTMF) con una clave externa a 'self', ¿cómo puedo evitar auto (recursivo) selección dentro del administrador de Django (administrador).Cómo evitar la selección automática (recursiva) para campos FK/MTM en el administrador de Django

En resumen, debería ser posible evitar selección propia (recursiva) de una instancia modelo en el administrador. Esto se aplica al editar instancias existentes de un modelo, no creando nuevas instancias.

Por ejemplo, tome el siguiente modelo para un artículo en una aplicación de noticias;

class Article(models.Model):   
    title = models.CharField(max_length=100) 
    slug = models.SlugField() 
    related_articles = models.ManyToManyField('self') 

Si hay 3 Article casos (título: a1-3), cuando se edita un Article instancia existente a través de la administración del campo related_articles está representado de forma predeterminada por un html (múltiple) cuadro de selección que proporciona una lista de TODOS los artículos (Article.objects.all()). El usuario solo debe ver y poder seleccionar Article instancias distintas a él, p. Ej. Al editar Article a1, related_articles disponible para seleccionar = a2, a3.

Actualmente puedo ver 3 posibles maneras de hacerlo, en orden de preferencia decreciente;

  1. proporcionan una manera de establecer el queryset proporcionar opciones disponibles en el campo de formulario de administración para la related_articles (a través de un filtro de consulta excluir, por ejemplo Article.objects.filter(~Q(id__iexact=self.id)) para excluir la instancia actual que se está editando de la lista de related_articles un usuario puede ver y seleccione desde. La creación/configuración del queryset a usar podría ocurrir dentro del constructor (__init__) de un Article ModelForm personalizado, o mediante algún tipo de opción dinámica limit_choices_to Model. Esto requeriría una forma de capturar la instancia que se está editando para usar para el filtrado.
  2. Anula la función save_model de la clase Article Model o ModelAdmin para comprobar y r quitarse del related_articles antes de guardar la instancia. Esto todavía significa que los usuarios administradores pueden ver y seleccionar todos los artículos, incluida la instancia que se está editando (para artículos existentes).
  3. Filtre las referencias propias cuando sea necesario para su uso fuera del administrador, p. plantillas.

La solución ideal (1) es actualmente posible hacerlo a través de formularios modelo personalizado exterior de la administración, ya que es posible pasar en una variable queryset filtrada para la instancia que está siendo editado a la forma modelo constructor. La pregunta es, ¿puede obtener en la instancia Article, es decir, 'auto' está editando el administrador antes de que se cree el formulario para hacer lo mismo.

Podría ser que voy de este por el camino equivocado, pero si tu les permite definir un FKF/MTMF al mismo modelo entonces debe haber una manera de tener el administrador - hacer lo correcto - y prevenir un usuario de seleccionarse excluyéndolo en la lista de opciones disponibles.

Nota: Solución 2 y 3 son posible hacer ahora y se proporcionan para tratar de evitar que estos como respuestas, lo ideal sería que me gustaría obtener una respuesta a la solución 1.

Respuesta

2

Se puede utilizar una Modelo de formulario personalizado en el administrador (estableciendo the "form" attribute of your ModelAdmin subclass). Entonces lo haces de la misma manera en el administrador como lo harías en cualquier otro lado.

+0

* Técnicamente *, sé por qué [limit_choices_to] (https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey .limit_choices_to) no puede hacer esto. Prácticamente, no entiendo por qué nunca se ha implementado. – kojiro

+2

@kojiro - las cosas simples deberían ser simples, las cosas avanzadas deberían ser posibles. No todas las características posibles deben incluirse en limit_choices_to, precisamente porque siempre puede escribir su propio código ModelForm para hacer lo que desee. Si tiene una sugerencia de sintaxis intuitiva para agregar esto a limit_choices_to sin hacerlo más complejo, puede abrir un ticket con parche y proponerlo. –

9

Carl es correcta, he aquí una muestra de cortar y pegar el código que iría en admin.py

Encuentro Navegación por la relaciones de Django puede ser difícil si usted no tiene un conocimiento sólido, y un ejemplo vivo puede valer 1000 veces más que "ve a leer esto" (no es que no necesites entender lo que está sucediendo).

class MyForm(forms.ModelForm): 
    class Meta: 
     model = MyModel 

    def __init__(self, *args, **kwargs): 
     super(MyForm, self).__init__(*args, **kwargs) 
     self.fields['myManyToManyField'].queryset = MyModel.objects.exclude(
      id__exact=self.instance.id) 
0

Usted puede también anular el método de la ModelAdmin get_form así:

def get_form(self, request, obj=None, **kwargs): 
    """ 
    Modify the fields in the form that are self-referential by 
    removing self instance from queryset 
    """ 
    form = super().get_form(request, obj=None, **kwargs) 
    # obj won't exist yet for create page 
    if obj: 
     # Finds fieldnames of related fields whose model is self 
     rmself_fields = [f.name for f in self.model._meta.get_fields() if (
      f.concrete and f.is_relation and f.related_model is self.model)] 
     for fieldname in rmself_fields: 
      form.base_fields[fieldname]._queryset = 
       form.base_fields[fieldname]._queryset.exclude(id=obj.id) 
    return form 

Tenga en cuenta que este es un todo en talla única solución que encuentra automáticamente los campos y elimina modelo de autorreferencia self de todos ellos :-)

Cuestiones relacionadas