filter list in a Django admin foreign key field

0

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

1 - I have a model with two foreignkey fields pointing to Enterprise model.

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 supplier in the supplier field, only the companies that received the type "supplier" appear? And in the same customer, only for Companies marked with the type "clients"?

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 the company-only contacts of the selected vendor.

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 I did this in a way that did not look pretty, as I edited the default templat of admin and made a bind event on change in the vendor field to a callback that does an ajax and brings the list of contacts just to that provider

$('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);
    });
})

It works perfectly, but I wanted to know if there is a way where I do not need to change the template, solve this in the backend, changing admin.py for example.

    
asked by anonymous 23.01.2018 / 18:01

2 answers

1

I believe the first part you can solve by customizing the formfield_for_foreignkey method of administration classes. 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 it will not have a purely backend output, with some modification in admin.py not ...

    
23.01.2018 / 20:35
0

Ronaldo,

I believe the django-smart-selects library can help you. I've already used this lib in a project and it worked fine. Even the example below (extracted from the README of it), exemplifies well your case, in my point of view. See:

Given the following templates:

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 you select a continent, if you want only countries on this continent to be available for selection, you can use ChainedForeignKey in the 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 to have helped, and any doubts I am at your disposal!

    
24.01.2018 / 18:37