Saltar al contenido

¿Cómo filtro las opciones de ForeignKey en un ModelForm de Django?

Nuestros investigadores estrellas han agotado sus depósitos de café, en su búsqueda todo el tiempo por la resolución, hasta que Rodrigo encontró el arreglo en Gitea por lo tanto en este momento la comparte con nosotros.

Solución:

ForeignKey está representado por django.forms.ModelChoiceField, que es un ChoiceField cuyas opciones son un modelo QuerySet. Consulte la referencia de ModelChoiceField.

Por lo tanto, proporcione un QuerySet al campo queryset attribute. Depende de cómo esté construido su formulario. Si crea un formulario explícito, tendrá campos nombrados directamente.

form.rate.queryset = Rate.objects.filter(company_id=the_company.id)

Si toma el objeto ModelForm predeterminado, form.fields["rate"].queryset = ...

Esto se hace explícitamente en la vista. No hackear.

Además de la respuesta de S.Lott y como se menciona como convertirse en Guru en los comentarios, es posible agregar los filtros del conjunto de consultas anulando el ModelForm.__init__ función. (Esto podría aplicarse fácilmente a formularios regulares) puede ayudar con la reutilización y mantiene ordenada la función de visualización.

class ClientForm(forms.ModelForm):
    def __init__(self,company,*args,**kwargs):
        super (ClientForm,self ).__init__(*args,**kwargs) # populates the post
        self.fields['rate'].queryset = Rate.objects.filter(company=company)
        self.fields['client'].queryset = Client.objects.filter(company=company)

    class Meta:
        model = Client

def addclient(request, company_id):
        the_company = get_object_or_404(Company, id=company_id)

        if request.POST:
            form = ClientForm(the_company,request.POST)  #<-- Note the extra arg
            if form.is_valid():
                form.save()
                return HttpResponseRedirect(the_company.get_clients_url())
        else:
            form = ClientForm(the_company)

        return render_to_response('addclient.html', 
                                  'form': form, 'the_company':the_company)

Esto puede ser útil para reutilizar, digamos, si tiene filtros comunes necesarios en muchos modelos (normalmente declaro una clase de formulario abstracto). P.ej

class UberClientForm(ClientForm):
    class Meta:
        model = UberClient

def view(request):
    ...
    form = UberClientForm(company)
    ...

#or even extend the existing custom init
class PITAClient(ClientForm):
    def __init__(company, *args, **args):
        super (PITAClient,self ).__init__(company,*args,**kwargs)
        self.fields['support_staff'].queryset = User.objects.exclude(user='michael')

Aparte de eso, solo estoy repitiendo el material del blog de Django, del cual hay muchos buenos por ahí.

Esto es simple y funciona con Django 1.4:

class ClientAdminForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(ClientAdminForm, self).__init__(*args, **kwargs)
        # access object through self.instance...
        self.fields['base_rate'].queryset = Rate.objects.filter(company=self.instance.company)

class ClientAdmin(admin.ModelAdmin):
    form = ClientAdminForm
    ....

No necesita especificar esto en una clase de formulario, pero puede hacerlo directamente en ModelAdmin, ya que Django ya incluye este método integrado en ModelAdmin (de los documentos):

ModelAdmin.formfield_for_foreignkey(self, db_field, request, **kwargs)¶
'''The formfield_for_foreignkey method on a ModelAdmin allows you to 
   override the default formfield for a foreign keys field. For example, 
   to return a subset of objects for this foreign key field based on the
   user:'''

class MyModelAdmin(admin.ModelAdmin):
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "car":
            kwargs["queryset"] = Car.objects.filter(owner=request.user)
        return super(MyModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

Una forma aún más ingeniosa de hacer esto (por ejemplo, al crear una interfaz de administración de front-end a la que los usuarios puedan acceder) es subclasificar el ModelAdmin y luego modificar los métodos a continuación. El resultado neto es una interfaz de usuario que SÓLO les muestra contenido relacionado con ellos, mientras que le permite a usted (un superusuario) ver todo.

He anulado cuatro métodos, los dos primeros hacen imposible que un usuario elimine algo y también elimina los botones de eliminación del sitio de administración.

La tercera anulación filtra cualquier consulta que contenga una referencia a (en el ejemplo, 'usuario' o 'puercoespín' (solo como una ilustración).

La última anulación filtra cualquier campo de clave externa en el modelo para filtrar las opciones disponibles de la misma manera que el conjunto de consultas básico.

De esta manera, puede presentar un sitio de administración frontal fácil de administrar que permite a los usuarios jugar con sus propios objetos, y no tiene que acordarse de escribir los filtros ModelAdmin específicos de los que hablamos anteriormente.

class FrontEndAdmin(models.ModelAdmin):
    def __init__(self, model, admin_site):
        self.model = model
        self.opts = model._meta
        self.admin_site = admin_site
        super(FrontEndAdmin, self).__init__(model, admin_site)

eliminar los botones 'eliminar':

    def get_actions(self, request):
        actions = super(FrontEndAdmin, self).get_actions(request)
        if 'delete_selected' in actions:
            del actions['delete_selected']
        return actions

previene el permiso de borrado

    def has_delete_permission(self, request, obj=None):
        return False

filtra los objetos que se pueden ver en el sitio de administración:

    def get_queryset(self, request):
        if request.user.is_superuser:
            try:
                qs = self.model.objects.all()
            except AttributeError:
                qs = self.model._default_manager.get_queryset()
            return qs

        else:
            try:
                qs = self.model.objects.all()
            except AttributeError:
                qs = self.model._default_manager.get_queryset()

            if hasattr(self.model, ‘user’):
                return qs.filter(user=request.user)
            if hasattr(self.model, ‘porcupine’):
                return qs.filter(porcupine=request.user.porcupine)
            else:
                return qs

filtra las opciones para todos los campos de clave externa en el sitio de administración:

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if request.employee.is_superuser:
            return super(FrontEndAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

        else:
            if hasattr(db_field.rel.to, 'user'):
                kwargs["queryset"] = db_field.rel.to.objects.filter(user=request.user)
            if hasattr(db_field.rel.to, 'porcupine'):
                kwargs["queryset"] = db_field.rel.to.objects.filter(porcupine=request.user.porcupine)
            return super(ModelAdminFront, self).formfield_for_foreignkey(db_field, request, **kwargs)

Sección de Reseñas y Valoraciones

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *