I have the following template in django, i want to get the totals of the last 2 columns for each of my document objects
{% for documento in documentos %}
{% for cuenta in documento.cuentasxdocumento_set.all %}
<tr {% cycle 'class="gray"' '' %} >
{% if forloop.first %}
<td>{{ documento.fecha_creacion.date }}</td>
<td>{{ cuenta.cuenta.nombre }}</td>
<td>
{% if cuenta.monto >= 0 %}
{{ cuenta.monto}}
{% endif %}
</td>
<td>
{% if cuenta.monto <= 0 %}
{{ cuenta.monto }}
{% endif %}
</td>
{% else %}
<td colspan="4"></td>
<td>{{ cuenta.cuenta.codigo }}</td>
<td>{{ cuenta.cuenta.nombre }}</td>
<td>
{% if cuenta.monto <= 0 %}
{{ cuenta.monto }}
{% endif %}
</td>
<td>
{% if cuenta.monto >= 0 %}
{{ cuenta.monto }}
{% endif %}
</td>
{% endif %}
</tr>
{% endfor %}
<tr>
<td colspan="1"></td>
<td>Document Total</td>
<td></td>
<td></td>
</tr>
{% endfor %}
This is all done using the following models, which are simplified for the purpose of this question
class Documento(models.Model):
numero_impreso = models.CharField(max_length=50)
fecha_creacion = models.DateTimeField(auto_now_add = True)
cuentas = models.ManyToManyField('CuentaContable', through = 'CuentasXDocumento', null = True)
def __unicode__(self):
return self.tipo.nombre + ": " + self.numero_impreso
class CuentasXDocumento(models.Model):
cuenta = models.ForeignKey('CuentaContable')
documento = models.ForeignKey('Documento')
monto = models.DecimalField(max_digits= 14, decimal_places = 6)
linea = models.IntegerField()
class CuentaContable(models.Model):
codigo = models.CharField(max_length=50)
nombre = models.CharField(max_length=100)
def __unicode__(self):
return self.nombre
Finally I'm sorry for the bad english :)
From my experience with Django, I would say that these things aren't easily done in the template. I try to do my calculations in the view instead of the template.
My recommendation would be to calculate the two sums you need in the view instead of the template.
That beings said, it is possible to do some work in the template using custom filters and tags. Using filters it might look like this:
<td>{% documento.cuentasxdocumento_set.all | sum_monto:"pos" %}</td>
<td>{% documento.cuentasxdocumento_set.all | sum_monto:"neg" %}</td>
Filters take two arguments, the value that you pass to the filter and an argument that you can use to control its behavior. You could use the last argument to tell sum_monto to sum the positive values or the negative values.
This is a quick untested filter implementation off the top of my head:
from django import template
register = template.Library()
#register.filter
def sum_monto(cuentas, op):
if op == "pos":
return sum(c.monto for c in cuentas if c.monto > 0)
else
return sum(c.monto for c in cuentas if c.monto < 0)
Related
I have a long process that I managed to stream into a Jinja template, but now I would like to show not only results but also that could be viewed by the user as a meaning of progress.
This is my current code: it iterates over a huge collection of items, some of which produce results and others do not. I only want to show the items that match the search.
The rendering part:
lista_pantallas = buscar_componente_stream_generate(componente, atributo, valor)
return Response(stream_template('consultas/busqueda_comp.html',
lista_pantallas=lista_pantallas,
componente=componente, atributo=atributo, valor=valor,
error_msg=err_msg))
This is the way I generate the iterator:
def buscar_componente_stream_generate(componente, atributo, valor):
with uopy.connect(...) as session:
with uopy.File(...) as fapant:
pantallas_fmpant = uopy.List()
pantallas_fmpant.select(fapant)
functor = BuscadorObjetoAtributo(componente, atributo, valor)
for idx, pantalla in enumerate(pantallas_fmpant):
try:
if print_pant:
print(f'{idx} - Pantalla: {pantalla}')
procesar_pantalla(pantalla, functor)
for item in functor.lista_objetos():
yield item
functor.borrar_objetos()
except Exception as ex:
print('{0} - {1} - {2}'.format(idx, pantalla, str(ex)))
And the Jinja2 template
{% if lista_pantallas %}
<h1>Lista de pantallas</h1>
<h2>CondiciĆ³n: {{ componente }}.{{ atributo }} = {{ valor }}</h2>
<h2>Ultima pantalla procesada: {{idx}} - {{pantalla}}</h2>
<table>
<thead>
<th>Pantalla</th>
<th>Atributo</th>
</thead>
<tbody>
{% for item in lista_pantallas %}
{% if loop.index0 is even() %}
{% set par_css = 'par' %}
{% else %}
{% set par_css = 'impar' %}
{% endif %}
<tr class={{ par_css }}>
<td>{{ item['fichero'] }}</td>
<td>{{ item['prop'] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
How can I refresh the template with the values of the variables idx and pantalla?
i have 2 tables in DB wallet(id,name) and balance(id,wallet_id)
i need table consisting of 2 cells (post,date)
where will all wallets in first cell and them balance in second
{% for wallets1 in wallets %}
<tr>
{% for balance1 in balance %}
{% if balance1.wallet_id == wallets1.id %}
<td> {{ balance1.balance }}</td>
{% endif %} {% endfor %}
{% endfor %}
if we have balance of coin we print balance
if balance1.wallet_id need print "0"
The difficulty next. If i do that
{% for wallets1 in wallets %}
<tr>
{% for balance1 in balance %}
{% if balance1.wallet_id == wallets1.id %}
<td> {{ balance1.balance }}</td>
{% else %}
<td> 0</td>
{% endif %} {% endfor %}
{% endfor %}
zero will be printed many times
views
wallets = Wallet.objects.all()
balance = User_balance.objects.filter(user_id= user.id)
args['wallets'] = wallets
args['balance'] = balance
return render_to_response("coins.html", args, user.id)
model
class Wallet(models.Model):
name = models.CharField(max_length=100)
class User_balance(models.Model):
user_id = models.IntegerField()
wallet_id = models.IntegerField()
balance = models.CharField(max_length=100)
You didn't post your view nor models so we have to assume a couple things but basically you're doing it wrong. Since balances have a foreign key on wallets, you don't have to loop over all balances for each wallets, you can just use the reverse relationship:
{% for wallet in wallets %}
<tr>
{% for balance in wallet.balance_set.all %}
<td> {{ balance.balance }}</td>
{% else %}
<td> 0</td>
{% endfor %}
</tr>
{% endfor %}
you must annotate balance in your view like this
wallers = Wallet.objects.all().annotate(total_balance=models.Count('balance'))
...
return render(request, 'template.html', {"wallets": wallets})
then in your html print wallets like this
<table>
<tr><th>wallet</th><th>balance</th></tr>
{% for wallet in wallets %}
<tr>
<td>{{ wallet.name }}</td>
<td>{{ wallet.total_balance|default:0 }}</td>
</tr>
{% endfor %}
</table>
I'm pretty new to django so I apologize if this has an obvious answer.
Say you have the following three models:
models.py
class Customer(models.Model):
name = models.CharField()
slug = models.SlugField()
class Product(models.Model):
plu = models.Charfield()
description = models.Charfield()
class Template(models.Model):
customer = models.ForeignKey(Customer)
product = models.ForeignKey(Product)
price = models.DecimalField()
The inline formset would look something like:
TemplateFormSet = inlineformset_factory(Customer, Template, extra=0,
fk_name='customer', fields=('price'))
Is it possible to follow the Template formset's Product foreign key backwards so you could display the plu and description fields within the same table?
For example something like this:
<table>
<tbody>
{% for obj in customer.template_set.all %}
<tr>
<td>{{ obj.product.plu }}</td>
<td>{{ obj.product.description }}</td>
<td>{% render_field formset.form.price class="form-control form-control-sm" %}</td>
</tr>
{% endfor %}
</tbody>
</table>
The formset's fields appear with the html above but the data from the bound form instance doesn't appear and I can't save by editing the empty fields.
I've also tried below but each formset is repeated for each object (for x formsets there are x*x rows):
<tbody>
{% for obj in customer.template_set.all %}
{% for form in formset %}
<tr>
<td>{{ obj.product.plu }}</td>
<td>{{ obj.product.description }}</td>
<td>{% render_field form.price class="form-control" %}</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
Basically I'm trying to go from the top portion of the image to the bottom
The formset functionality is only to show forms but something that you can do is create a custom form that display the 2 fields with the function of readonly like:
class your_form(models.ModelForm):
class Meta()
model = Template
fields = ['price', 'product']
def __init__(self, *args, **kwargs):
super(ItemForm, self).__init__(*args, **kwargs)
self.fields['product'].widget.attrs['readonly'] = True
TemplateFormSet = inlineformset_factory(Customer, Template, extra=0,
fk_name='customer', form=your_form)
That's my best tried, if you wanna display both try returning in your models something like:
class Product(models.Model):
plu = models.Charfield()
description = models.Charfield()
def __str__(self, *args, **kwargs):
a = '<td>' + self.plu + '</td><td>' + self.plu '</td>'
return self.plu + self.description # Or return 'a'
I managed to get it working although I'm not sure its the most efficient way. If anyone has a better way please let me know.
{{ formset.management_form }}
<table>
<thead>
...
</thead>
<body>
{% for obj in customer.template_set.all %}
{% with forloop.counter as outer_counter %}
{% for form in formset %}
{% if forloop.revcounter == outer_counter %}
{% for hidden in form.hidden_fields %} {{ hidden }} {% endfor %}
<tr>
<td>{{ obj.product.plu }}</td>
<td>{{ obj.product.description }}</td>
<td>{% render_field form.price class="form-control" %}</td>
</tr>
{% endif %}
{% endfor %}
{% endwith %}
{% endfor %}
</tbody>
</table>
Should also mention I'm using django-widget-tweaks which is where {% render_field %} comes from.
Update (the proper way):
{% for template in customer.template_set.all %}
{% for form in formset %}
{% if form.instance.id == template.pk %}
I have a ModelFormSet:
TransactionFormSet = modelformset_factory(Transaction, exclude=("",))
With this model:
class Transaction(models.Model):
account = models.ForeignKey(Account)
date = models.DateField()
payee = models.CharField(max_length = 100)
categories = models.ManyToManyField(Category)
comment = models.CharField(max_length = 1000)
outflow = models.DecimalField(max_digits=10, decimal_places=3)
inflow = models.DecimalField(max_digits=10, decimal_places=3)
cleared = models.BooleanField()
And this is the template:
{% for transaction in transactions %}
<ul>
{% for field in transaction %}
{% ifnotequal field.label 'Id' %}
{% ifnotequal field.value None %}
{% ifequal field.label 'Categories' %}
// what do i do here?
{% endifequal %}
<li>{{ field.label}}: {{ field.value }}</li>
{% endifnotequal %}
{% endifnotequal %}
{% endfor %}
</ul>
{% endfor %}
The view:
def transactions_on_account_view(request, account_id):
if request.method == "GET":
transactions = TransactionFormSet(queryset=Transaction.objects.for_account(account_id))
context = {"transactions":transactions}
return render(request, "transactions/transactions_for_account.html", context)
I want to list all the Transaction information on a page.
How can I list the "account" property of Transaction and the "categories"?
Currently the template shows only their id, I want to get a nice representation for the user (preferrably from their str() method).
The only way I can see that would work is to iterate over the FormSet, get the Ids of the Account and Category objects, get the objects by their Id and store the information I want in a list and then pull it from there in the template, but that seems rather horrible to me.
Is there a nicer way to do this?
Thanks to the comments, I figured it out that what I was doing was pretty dumb and pointless.
This works:
1) Get all Transaction objects
transactions = Transaction.objects.for_account(account_id)
2) Pass to template
context = {"transactions":transactions,}
return render(request, "transactions/transactions_for_account.html", context)
3) Access attributes, done
{% for transaction in transactions %}
<tr>
<td class="tg-6k2t">{{ transaction.account }}</td>
<td class="tg-6k2t">{{ transaction.categories }}</td>
<td class="tg-6k2t">{{ transaction.date }}</td>
<td class="tg-6k2t">{{ transaction.payee }}</td>
<td class="tg-6k2t">{{ transaction.comment }}</td>
<td class="tg-6k2t">{{ transaction.outflow }}</td>
<td class="tg-6k2t">{{ transaction.inflow }}</td>
<td class="tg-6k2t">{{ transaction.cleared }}</td>
</tr>
{% endfor %}
python 2.6, with Django 1.3.1 on Redhat 6.3
In Django how would I go about changing the background colour of a table cell depending on it's value, as in if it is over 10 it's red, between 7 and 9 it's orange, below 7 is green etc..
The data is coming from a non django database/model.
I am using a standard template to iterate over the table, but would have no problem using a custom template for this.
I see the following
Link
that deals with changing cell colour but it seems to be based on a concrete value in the cell as opposed to being within a range.
using the following test code for a view
def dashboard(request):
if request.user.is_authenticated():
user = request.user.first_name
else:
return redirect('/bcpm/login')
table_headers = ['Colmun1','Column2','Column3']
table_data = [['test1',2,3],['test2',2,4],['test3',5,5]]
page_title = 'Dashboard'
template_dict = {'header_list':table_headers, 'page_title':page_title,
'results':table_data,'username':user}
return render_to_response('dashboard.html',template_dict)enter code here
and the following generic table template:
<table border=1 width=98% style="margin-left:12px;">
<tr>
{% for item in header_list %}
<th>{{ item }}</th>
{% endfor %}
</tr>
{% for row in results %}
<tr>
{% for line in row %}
<td>{{line}}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
Thanks.
Almost solved;
With the help of brianbuck below i came up with the following,
in the view:
def dashboard(request):
if request.user.is_authenticated():
user = request.user.first_name
else:
return redirect('/login'
table_headers = ['Column1','Column2','Column3']
table_data = [['name','thing',8],['name','thing',5]]
page_title = 'Dashboard'
template_dict = {'header_list':table_headers, 'page_title':page_title,
'results':table_data,'username':user}
return render_to_response('dashboard.html',template_dict)
in the template;
<table border=1 width=68% style="margin-left:12px;">
<tr>
{% for item in header_list %}
<th>{{ item }}</th>
{% endfor %}
</tr>
{% for element in results %}
<tr>
<td> {{ element.0 }} </td>
<td> {{ element.1 }} </td>
{% if element.3 > 7 %} <td class="red"> {{ element.3 }} </td>
{% else %} <td class="green"> {{ element.3 }} </td> {% endif %}
</tr>
{% endfor %}
</table>
{% endif %}
I really could not get it to do an {% if or %}
When I tried to set it up to do a
"greater than or equal to 7 or less than or equal to 8"
it would always evaluate to this expression for a number higher than 7, even though the first if statement should be true for anything higher than 9.
I am using Django 1.3 and I think there may be some limitations of the if/else and the multiple evaluations, either way I have it 80% working with two values red/green and that is good enough for the moment.
Thank you all.
Got it to work like this;
{% for element in results %}
<tr>
<td> {{ element.0 }} </td>
<td> {{ element.1 }} </td>
<td> {{ element.2 }} </td>
<td> {{ element.3 }} </td>
{% if element.4 > 8 %} <td class="red"> {{ element.4 }} </td>
{% else %}{% if element.4 > 8 or element.4 >= 5 %} <td class="orange"> {{ element.4 }} </td>
{%else %}{% if element.4 < 5 %}<td class="green"> {{ element.4 }} </td>
{% endif %}{% endif %}{% endif %}
<td> {{ element.5 }} </td>
This would not be required if you have a version of Django that supports elif or if you add some of the django snippets that are available to extend your django installation.
Hurrah.
This assumes you have three classes named:
td.red {
backgroundColor: red;
}
td.orange {
backgroundColor: orange;
}
td.green {
backgroundColor: green;
}
...
Django 1.3 doesn't have elif so you will probably have to do it a bit more clunky.
<td class="
{% if val >= 10 %}red{% endif %}
{% if val >= 7 or val <= 9 %}orange{% endif %}
{% if val < 7 %}green{% endif %}">
{{ val }}
</td>
I wanted to do this only in admin.py.
Let's say your column is called col :
You want to set the column to green if its value is bigger than 0 and to red if the opposite is true.
def TableAdmin(admin.ModelAdmin)
def col_(self, obj):
green_style = "<script>document.querySelectorAll('.green_table_elem').forEach(elem => { elem.parentElement.style.background = 'green'; })</script>"
red_style = "<script>document.querySelectorAll('.red_table_elem').forEach(elem => { elem.parentElement.style.background = 'red'; })</script>"
if obj.col > 0:
return mark_safe(f'<div class="green_table_elem">{obj.col}</div> {green_style}')
else:
return mark_safe(f'<div class="red_table_elem">{obj.col}</div> {red_style}')
list_display = ('col_',)
This colors the td (column) itself and not the added div like some answers do.