Search code examples
djangodjango-tables2

Custom column on django-tables2 that mix multiple foreignkeys


I am trying to make a custom column on django-tables2 with a function that returns the field that is not empty from 3 possibles foreign keys (just one can be not empty). Here is what I've done:

Models.py

class Sitiocancelado(models.Model):
    x = models.OneToOneField(X, on_delete=models.CASCADE, null=True,blank=True)
    y = models.OneToOneField(Y, on_delete=models.CASCADE, null=True, blank=True)
    z = models.OneToOneField(Z, on_delete=models.CASCADE, null=True, blank=True)
    fecha_de_cancelacion = models.DateField(null=True, blank=True)
    comentarios = models.TextField(max_length=200, null=True, blank=True)
    slug = models.SlugField(max_length=100, editable=False)

    def __str__(self):
        return self.z.y.x.nombre_del_sitio

Tables.py

class SitiocanceladoTable(tables.Table):

    def columna_combinada(table):
        if x == None:
            if y == None:
                return z
            else:
                return y
        else:
            return x

    Sitio = tables.Column(columna_combinada)

    class Meta:
        model = Sitiocancelado
        attrs = {
            'class': 'table table-striped table-bordered', 
            'id': 'example', 
            'width' : '100%', 
        }
        fields = ('Sitio',)
        exclude = ('id', 'slug', 'sitioproyecto', 'sitiocontratado', 'sitiooperando',)

Does it make any sense?


Solution

  • That's not really how custom columns work. You basically have two options:

    render_<columnname>

    This looks a bit like your example. Define a column and a method on the table class with name render_<column name>. No need to supply that name to the column you defined. Your example would look like this:

    class SitiocanceladoTable(tables.Table):
        Sitio = tables.Column(empty_values=())
    
        class Meta:
            model = Sitiocancelado
    
        def render_Sitio(self, record):
            if record.x == None:
                if record.y == None:
                    return record.z
                else:
                    return record.y
            else:
                return record.x
    

    This method is good for one-offs. It is not much extra code, and the code is very close with the rest of the definition of the table.

    Note that I added emtpy_values=(). This will make sure your custom implementation of the render function used, even if django-tables2 thinks your column is empty. It's the responsibility of the custom column to render a proper default.

    Subclassing Column

    If you need the functionality of this column in multiple tables, or multiple times in the same table, it might be cleaner to subclass Column or one of the more specific Column implementations.

    Your example might look like this:

    class CombinedFKColumn(tables.Column):
        def render(self, record):
            return getattr(record, 'x', getattr(record, 'y', getattr(record, 'z', self.default)))
    
    class SitiocanceladoTable(tables.Table):
        Sitio = tables.CombinedFKColumn(empty_values=())
    
        class Meta:
            model = Sitiocancelado