2012-07-25 32 views
18

Tengo un campo de modelo de Django que me gustaría alinear. El campo es una relación de muchos a muchos. Entonces hay "Proyectos" y "Perfiles de usuario". Cada perfil de usuario puede seleccionar cualquier cantidad de proyectos.Interfaz de administración de Django: utilizando horizontal_filter con el campo en línea ManyToMany

Actualmente, tengo la vista en línea "tabular" funcionando. ¿Hay alguna forma de tener un "filtro horizontal" para que pueda agregar y eliminar proyectos fácilmente desde un perfil de usuario?

Consulte la imagen adjunta para ver un ejemplo. enter image description here

Aquí está el código de modelo para el perfil de usuario:

class UserProfile(models.Model): 
    user = models.OneToOneField(User, unique=True) 
    projects = models.ManyToManyField(Project, blank=True, help_text="Select the projects that this user is currently working on.") 

Y el código modelo para un proyecto:

class Project(models.Model): 
    name = models.CharField(max_length=100, unique=True) 
    application_identifier = models.CharField(max_length=100) 
    type = models.IntegerField(choices=ProjectType) 
    account = models.ForeignKey(Account) 
    principle_investigator = models.ForeignKey(User) 
    active = models.BooleanField() 

Y la clave de administrador para la vista:

class UserProfileInline(admin.TabularInline): 
    model = UserProfile.projects.through 
    extra = 0 
    verbose_name = 'user' 
    verbose_name_plural = 'users' 

class ProjectAdmin(admin.ModelAdmin): 
    list_display = ('name', 'application_identifier', 'type', 'account', 'active') 
    search_fields = ('name', 'application_identifier', 'account__name') 
    list_filter = ('type', 'active') 
    inlines = [UserProfileInline,] 
admin.site.register(Project, ProjectAdmin) 

Respuesta

35

El problema no es tener en línea; es por el modo en que funciona el ModelForm, en general. Solo crean campos de formulario para campos reales en el modelo, no atributos de administrador relacionados. Sin embargo, puede agregar esta funcionalidad al formulario:

from django.contrib.admin.widgets import FilteredSelectMultiple 

class ProjectAdminForm(forms.ModelForm): 
    class Meta: 
     model = Project 

    userprofiles = forms.ModelMultipleChoiceField(
     queryset=UserProfile.objects.all(), 
     required=False, 
     widget=FilteredSelectMultiple(
      verbose_name='User Profiles', 
      is_stacked=False 
     ) 
    ) 

    def __init__(self, *args, **kwargs): 
     super(ProjectAdminForm, self).__init__(*args, **kwargs) 
      if self.instance.pk: 
       self.fields['userprofiles'].initial = self.instance.userprofile_set.all() 

    def save(self, commit=True): 
     project = super(ProjectAdminForm, self).save(commit=False) 
     if commit: 
      project.save() 

     if project.pk: 
      project.userprofile_set = self.cleaned_data['userprofiles'] 
      self.save_m2m() 

     return project 

class ProjectAdmin(admin.ModelAdmin): 
    form = ProjectAdminForm 
    ... 

Es posible que necesite un pequeño tutorial. Primero, definimos un campo de formulario userprofiles. Utilizará un ModelMultipleChoiceField, que de forma predeterminada dará como resultado un cuadro de selección múltiple. Como este no es un campo real en el modelo, no podemos simplemente agregarlo a filter_horizontal, por lo que le pedimos que simplemente use el mismo widget, FilteredSelectMultiple, que usaría si estuviera en la lista en filter_horizontal.

Inicialmente establecer el conjunto de consultas como todo el conjunto UserProfile, no se puede filtrar aquí, sin embargo, porque en esta etapa de la definición de la clase, la forma no ha creado una instancia y por lo tanto no tiene que es instance conjunto todavía. Como resultado, anulamos __init__ para que podamos establecer el queryset filtrado como el valor inicial del campo.

Finalmente, reemplazamos el método save, de modo que podamos establecer el contenido del administrador relacionado al mismo que el que estaba en los datos POST del formulario, y listo.

+0

Muchas gracias Chris! ¡Esto funcionó como un encanto la primera vez que lo intenté! –

+0

Gracias, eres el hombre. – whooot

+0

Una gran solución, funcionó muy bien para mí. – Blackeagle52

1

Una adición menor cuando se trata de una relación de muchos a muchos consigo misma. Uno podría querer excluirse de las opciones:

if self.instance.pk: 
     self.fields['field_being_added'].queryset = self.fields['field_being_added'].queryset.exclude(pk=self.instance.pk) 
     self.fields['field_being_added'].initial = """Corresponding result queryset""" 
Cuestiones relacionadas