filter list in Django admin Foreign key field

Asked

Viewed 997 times

0

I have two questions, both related to the form generated automatically by the Django admin. If I create an administrative screen I know how to solve these problems, but working directly with the admin I am suffering a little, even because I believe that the admin should not be edited this way, but unfortunately it will not be possible to migrate the administrative screens in the short term to a custom template and outside the admin. Presented the context, here are my doubts:

1 - I have a model with two fields of the type foreignkey pointing to model Company.

class Norma(models.Model):
    fornecedor = models.ForeignKey(
        Empresa,
        verbose_name="Fornecedor",   
        related_name="fornecedor+"
     )
     contato = models.ForeignKey(Contatos)
     numero = models.CharField(max_length=80, verbose_name="Norma")
     descricao = models.CharField(verbose_name="Descrição")
     cliente = models.ForeignKey(
         Empresa,
         verbose_name="Cliente",
         related_name="cliente+"
     )

Is it possible, in the form created by the admin, that when entering the vendor in the vendor field, only the companies that received the "vendor" type appear? And in the customer the same thing, only for companies marked with the type "customers"?

class Empresa(models.Model):
    razao_social = models.CharField(max_length=50, blank=True, null=True)
    apelido = models.CharField(max_length=50, null=False, unique=True)
    tipo = models.ManyToManyField(Tipo)

class Tipo(models.Model):
    tipo = models.CharField(max_length=50, null=False, unique=True)

2 - bring contacts only from selected supplier company.

class Contato(models.Model):
    empresa = models.ForeignKey(Empresa, verbose_name="Empresa")
    nome_contato = models.CharField(max_length=80, verbose_name="Nome do Contato")

I confess that I did it in a way that wasn’t pretty, because I edited the default templat of admin and made a bind event on change in the vendor field to a callback that makes an ajax and brings the contact list only to that vendor

$('body').on('change', '#id_fornecedor', function(){
    var id = $(this).val();
    $.post('/empresas/contatos_fornecedor/', {id: id})
    .then(function(data){
            var contatos = JSON.parse(data);

            var options =  contatos.reduce(function(html, contato){
                return html+= "<option    value='"+contato.pk+"'>"+contato.fields.nome_contato+"</option>"
            }, "<option value=''>Selecione</option>");

        console.log(options);

        $('#id_contato').html(options);
    });
})

works perfectly, but wanted to know if there is a way where I don’t need to change the template, solve it in the backend, changing admin.py for example.

2 answers

1

I believe that the first part you can solve by customizing the method formfield_for_foreignkey of the classes of administration. Something like:

class NormaAdmin(admin.ModelAdmin):
    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
        if db_field.name == "fornecedor":
            kwargs["queryset"] = Empresa.objects.filter(tipo__in='fornecedor')
        elif db_field.name == "cliente":
            kwargs["queryset"] = Empresa.objects.filter(tipo__in='cliente')
        return super(NormaAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

admin.site.register(CleanerAdmin, Cleaner)

As for the second part I think there will be a purely backend output, with some modification in admin.py not...

  • 1

    Thank you very much! Here I managed to really solve the first part. The only modifications I needed to do was in filter(), the way you posted mine didn’t work, I think because in type, I have another Foreign key that will fetch type too, summarizing it looked like this: Company.objects.filter(type__tipo_contains='Vendor') Company.objects.filter(type__type__contains='Client') And my admin.site.Registering only worked when I reversed the parameters, passed the model first and then the admin model: siteadmin.site.Register(Standards, Normasadmin) Friend, thank you very much, helped me a lot

  • Beauty! It was bad for the missing points of the answer, was without python and Django to test.

0

Ronaldo,

I believe the library Django-smart-selects can help you. I’ve used this lib on a project and it worked well. Including the example below (extracted from the README of the same), exemplifies well your case, in my point of view. See:

Given the following models:

class Continent(models.Model):
    name = models.CharField(max_length=255)

class Country(models.Model):
    continent = models.ForeignKey(Continent)
    name = models.CharField(max_length=255)

class Location(models.Model):
    continent = models.ForeignKey(Continent)
    country = models.ForeignKey(Country)
    area = models.ForeignKey(Area)
    city = models.CharField(max_length=50)
    street = models.CharField(max_length=100)

Once selecting a continent, if you want only the countries of this continent to be available for selection, you can use ChainedForeignKey on the model Location

from smart_selects.db_fields import ChainedForeignKey

    class Location(models.Model)
        continent = models.ForeignKey(Continent)
        country = ChainedForeignKey(
            Country,
            chained_field="continent",
            chained_model_field="continent",
            show_all=False,
            auto_choose=True,
            sort=True)
        area = ForeignKey(Area)
        city = models.CharField(max_length=50)
        street = models.CharField(max_length=100)

I hope I’ve helped, and any doubt I’m at your disposal!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.