How to iterate over attributes of an object using Jinja - flask

I have a model for coffee beans that I send over to my HTML pages and display the objects' data. Currently, I have a solution that requires me to update my HTML code if I modify my Bean object.
I want to the remove coupling from my program by iterating over the attributes associated with the object as opposed to hard-coding each attribute into the HTML.
My current (outdated) solution:
# Bean model defined using Flask-SQLAlchemy
class Bean(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20), index=True, nullable=False)
...
<!-- some_page.html -->
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
...
</tr>
</thead>
<tbody>
{% for bean in beans %}
{% include '_bean_info.html' %}
{% endfor %}
</tbody>
</table>
<!-- _bean_info.html -->
<tr>
<th scope="col">{{ bean.id }}</th>
<th scope="col">{{ bean.name }}</th>
...
</tr>
What I am trying to do:
<!-- _bean_info.html -->
<tr>
{% for each b in bean %}
<th scope="col">
{{ bean.b }} <!-- where 'b' is an attribute, e.g. 'name' -->
</th>
</tr>

Depending on what your data structure looks like, you may be able to take advantage of the fact that the attributes of an object are available via the __dict__ attribute...in other words, if we have data like this:
from dataclasses import dataclass
#dataclass
class Bean:
id: str
name: str
beans = [
Bean(id=1, name="Guatemalan"),
Bean(id=2, name="Peruvian"),
Bean(id=3, name="Hawaiian"),
]
Then we can use that in a template like this:
import jinja2
t = jinja2.Template('''
<table>
{% for bean in beans %}
<tr>
{% for attr in bean.__dict__.keys() -%}
<td>{{ attr }}</td><td>{{ bean[attr] }}</td>
{% endfor -%}
</tr>
{% endfor %}
</table>
''')
print(t.render(beans=beans))
Put together, the examples above will produce:
<table>
<tr>
<td>id</td><td>1</td>
<td>name</td><td>Guatemalan</td>
</tr>
<tr>
<td>id</td><td>2</td>
<td>name</td><td>Peruvian</td>
</tr>
<tr>
<td>id</td><td>3</td>
<td>name</td><td>Hawaiian</td>
</tr>
</table>
...which is I think what you want.
In this particular situation, I would probably explicitly convert my model into a dict, because this makes the template a little less magical:
from dataclasses import dataclass, asdict
...
t = jinja2.Template('''
<table>
{% for bean in beans %}
<tr>
{% for attr in bean.keys() -%}
<td>{{ attr }}</td><td>{{ bean[attr] }}</td>
{% endfor -%}
</tr>
{% endfor %}
</table>
''')
print(t.render(beans=(asdict(bean) for bean in beans)))

Related

Flask-security delete user return an error

I'm trying to add a delete button on my users admin page in a flask app.
But I already have this error when I click on button :
sqlalchemy.orm.exc.UnmappedInstanceError: Class 'builtins.str' is not mapped
This is my adminusers.html file :
{% extends 'base.html' %}
{% block main %}
<main>
<table class="table table-striped users">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Email</th>
<th scope="col">Roles</th>
<th scope="col" class="text-center">Delete</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<th scope="row">{{ user.id }}</th>
<td>{{ user.name }}</td>
<td>{{ user.email }}</td>
<td>
{% for role in user.roles %}
{{role.name}};
{% endfor %}
</td>
<td style="text-align: center;"></i></td>
</tr>
{% endfor %}
</tbody>
</table>
</main>
{% endblock main %}
And my app.py file :
#app.route('/adminusers')
def list_users():
users= User.query.all()
return render_template('adminusers.html', users=users)
#app.route('/delete_user/<user>')
def delete_user(user):
user_datastore.delete_user(user=user)
db.session.commit()
return redirect(url_for('adminusers'))
I'm trying to use the 'email' or 'name' but it already return an error
Thanks #pjcunningham, like that it works :
#app.route('/delete_user/<id>')
def delete_user(id):
user = user_datastore.get_user(id)
user_datastore.delete_user(user)
db.session.commit()
return redirect(url_for('adminusers'))

django show user information in regroup only to user

Making a web app that lets a user log workouts. I have a page that groups all the logged workouts together by date, using regroup and a for 2 loops. How do I only show the user that is logged in information their own information. When I try to use a 3rd for loop (ex. {% for workout in workouts%}, {% if workout.person == user %})I just get 2 tables with the same information. Heres the code in my template.
{% regroup workouts by date_of as dateof %}
{%for date_of in dateof %}
<table class="table table-striped table-dark mt-3">
<thead>
<th scope="col">{{date_of.grouper}}</th>
</thead>
<thead>
<tr>
<th scope="col">Body Part</th>
<th scope="col">Exercise</th>
<th scope="col">Weight(lbs)</th>
<th scope="col">Sets</th>
<th scope="col">Reps</th>
</tr>
</thead>
<tbody>
{% for workout in date_of.list %}
<tr>
<td>{{workout.body_part}}</td>
<td>{{workout.exercise}}</td>
<td>{{workout.weight}}</td>
<td>{{workout.sets}}</td>
<td>{{workout.reps}}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endfor %}
Thanks #gunsodo, after looking into it filtering the ListView with get_queryset solved the problem. 🙌🏼

How to persist data found in tables within a django template, in postgresql

I am scraping several sites and I show the data obtained in 6 tables within a django template.
My intention is to persist the data of the tables in postgresql, but I can not realize how to perform that task.
In principle I am trying to save the data from the second table.
For this I have created the models that I show below, as well as a view that is called: registroDivisaArgentina ().
The template is called quotes.html and within it, there are 6 tables.
I have tried to work with a class called: RegisterArgentineValues () within a forms.py file
models.py
class DivisasArgentina(models.Model):
class Meta:
ordering = ['CodigoDA']
CodigoDA = models.CharField(max_length=50, primary_key = True)
Texto_para_Reporte = models.CharField(max_length=70)
def __str__(self):
return '{}'.format(self.Texto_para_Reporte)
class ValoresDivisasArgentina(models.Model):
class Meta:
ordering = ['Dia']
DivisasArgentina = models.ForeignKey(DivisasArgentina, on_delete=models.PROTECT)
Dia = models.DateField(default=date.today)
Hora = models.DateTimeField(default=timezone.now)
Compra = models.FloatField()
Venta = models.FloatField()
Variacion_dia_anterior = models.FloatField()
ClaveComparacion = models.CharField(max_length=1)
def __str__(self):
return '{} - {} - {}'.format(self.DivisasArgentina, self.Dia, self.ClaveComparacion)
cotizaciones.html
{% extends 'base.html' %}
{% block contenido %}
<form method="POST" class="post-form">{% csrf_token %}
<div class="container">
<table class="table table-striped table-bordered" id="tab1">
<thead>
<tr>
<th>Divisas en el Mundo</th>
<th>Valor</th>
</tr>
</thead>
<tbody>
<tr>
{%for element in cotiz_mun%}
<tr>
{% for key,value in element.items %}
<td> {{ value }} </td>
{% endfor %}
</tr>
{% endfor %}
</tr>
</tbody>
</table>
</div>
<div class="container">
<table class="table table-striped table-bordered" id="tab2">
<thead>
<tr>
<th>Divisas en Argentina</th>
<th>Compra</th>
<th>Venta</th>
</tr>
</thead>
<tbody>
<tr>
{%for element in cotiz_arg%}
<tr>
{% for key,value in element.items %}
<td>{{ value }} </td>
{% endfor %}
</tr>
{% endfor %}
</tr>
</tbody>
<thead>
{{ form.as_table }}
</table>
</div>
<div class="container">
<table class="table table-striped table-bordered" id="tab3">
<thead>
<tr>
<th>Dolar Banco Nacion Argentina (Divisas)</th>
<th>Compra</th>
<th>Venta</th>
</tr>
</thead>
<tbody>
<tr>
{%for element in cotiz_exp%}
<tr>
{% for key,value in element.items %}
<td>{{ value }} </td>
{% endfor %}
</tr>
{% endfor %}
</tr>
</tbody>
</table>
<table class="table table-striped table-bordered" id="tab4">
<thead>
<tr>
<th colspan="4">Dolar Futuro en Argentina</th>
</tr>
<tr>
<th>Mes 1</th>
<th>Mes 2</th>
<th>Mes 3</th>
<th>Mes 4</th>
</tr>
</thead>
<tbody>
<tr>
{%for element in cotiz_dol%}
<td>
{{ element.Valores}}
</td>
{% endfor %}
</tr>
</tbody>
</table>
<table class="table table-striped table-bordered" id="tab5">
<thead>
<tr>
<th colspan="3">Indicadores Varios - Tasa Libor</th>
</tr>
<tr>
<th>Libor a 1 Mes</th>
<th>Libor a 2 Mes</th>
<th>Libor a 3 Mes</th>
</tr>
</thead>
<tbody>
<tr>
{%for element in cotiz_lib%}
<td>
{{ element.Valores }}
</td>
{% endfor %}
</tr>
</tbody>
</table>
<table class="table table-striped table-bordered" id="tab6">
<thead>
<tr>
<th>Indicadores Varios - Indice Merval y Oro</th>
<th>Valores</th>
</tr>
</thead>
<tbody>
<tr>
{%for element in cotiz_ind%}
<tr>
{% for key,value in element.items %}
<td> {{ value }} </td>
{% endfor %}
</tr>
{% endfor %}
</tr>
</tr>
</tbody>
</table>
</div>
<div class="container" id="saveData">
<br></br>
<button type="submit" class="btn btn-primary pull-right">Guardar Datos</button>
</div>
</form>
{% endblock %}
views.py
def mostrar_cotizaciones(request):
cotiz_arg = json.loads(j_cotizaciones_argentina)
cotiz_mun = json.loads(j_cotizaciones_mundiales)
cotiz_exp = json.loads(j_cotizacion_export)
cotiz_dol = json.loads(j_dolar_futuro)
cotiz_ind = json.loads(j_indicadores)
cotiz_lib = json.loads(j_libor)
context = {'cotiz_mun': cotiz_mun,
'cotiz_arg': cotiz_arg,
'cotiz_exp': cotiz_exp,
'cotiz_dol': cotiz_dol,
'cotiz_ind': cotiz_ind,
'cotiz_lib': cotiz_lib,
}
return render(request, 'cotizaciones.html', context)
def registrarDivisaArgentina(request):
if request.method == 'POST':
formulario = RegistrarValoresDivisasArgentinas(request.POST)
if formulario.is_valid():
formulario.save()
return HttpResponseRedirect('/listadoValores')
else:
formulario = RegistrarValoresDivisasArgentinas()
formulario.setup('Registrar', css_class="btn btn-success")
return render(request, 'cotizaciones.html', {'formulario':formulario})
forms.py
from django import fla
from django.forms import ModelForm
from django import forms
from fla.models import *
class RegistrarValoresDivisasArgentinas(forms.ModelForm):
class Meta:
model = ValoresDivisasArgentina
fields= [Compra, Venta]
I have done some tests, but none has given a favorable result. Someone can tell me how to process the data (in the views and forms) that are in the tables, to be able to store them in my postgres tables ?
I do this kind of task very often, and I've found out that the combination of ScraPy with scrapy-djangoitem is a very good combo to use here.
Good luck!

How to display 2 queryset list Frond end?(Django ListView get_queryset)

I got 2 queryset list expired_item and queryset in Django ListView, but I don't know when item is expired(queryset is empty), how to display another list expired_item on frond end, no matter what I changed in abc.html, expired_item won't dispaly, I pasted my code as below:
class ABCListView(ListView):
model = ABC
ordering = ('name', 'skill_course')
context_object_name = 'abcs'
template_name = ''
def get_queryset(self, **kwargs):
# Omitted
......
......
# Omitted
expired_item = list(ABC.objects.filter(pk__in=aa).exclude(pk__in=z))
queryset = Permit.objects.filter(pk__in=z)
return queryset
And my html file of abc.html as below:
{% extends 'base.html' %}
{% block content %}
<nav aria-label="breadcrumb">
</nav>
<h2 class="mb-3">My Items list</h2>
<div class="card">
<table class="table mb-0">
<thead>
<tr>
<th>Name</th>
<th>Department</th>
<th>Status</th>
<th></th>
</tr>
</thead>
<tbody>
{% for a in abcs %}
<tr>
<td class="align-middle">{{ a.name }}</td>
<td class="align-middle">{{ a.department.get_html_badge }}</td>
<td class="align-middle badge badge-pill badge-danger">{{ a.status }}</td>
</tr>
{% empty %}
{% endfor %}
</tbody>
</table>
</div>
<h2 class="mb-3">My Expired Items list</h2>
<div class="card">
<table class="table mb-0">
<thead>
<tr>
<th>Name</th>
<th>Department</th>
<th>Status</th>
<th></th>
</tr>
</thead>
<tbody>
{% for b in expired_item %}
<tr>
<td class="align-middle">{{ b.name }}</td>
<td class="align-middle">{{ b.department.get_html_badge }}</td>
<td class="align-middle badge badge-pill badge-danger">{{ a.status }}</td>
</tr>
{% empty %}
{% endfor %}
</tbody>
</table>
</div>
<div class="card-footer">
{% endblock %}
Thanks so much for any advice!
I would suggest use a normal django view. This Generic ListView is just created for the use of one list. Just pass both querysets in your context and render your template with that.
You could also use get_context_data() but this would be more or less hacky and not the qay I would recommend.

Counting objects and showing the results in Django

I have a question regarding counting objects, filtering the results and finally putting them into my template.
in Models.py:
class Fms(models.Model):
department = models.CharField(max_length=255, verbose_name='Department')
date = models.DateField()
in Views.py:
def raport(request):
raport = Fms.objects.filter(date__year='2013').extra(select={'month': "EXTRACT(month FROM date)"}).values('month', 'department').annotate(Count('department'))
return render_to_response ('Fms/raport.html',
{'raport': raport},
context_instance=RequestContext(request))
Question is, how to show a result in a table like:
Or at least make it show each month and how many times department was mentioned in that month.
you need something like:
{% for each_model in raport %}
{{each_model.department}}
{{each_model.date}}
{% endfor %}
If you do not pass a keyword to annotate, it uses the name of the field with __count appended to it. https://docs.djangoproject.com/en/dev/topics/db/aggregation/#generating-aggregates-for-each-item-in-a-queryset To access that in your template you would simply do:
<table>
<thead>
<tr>
<th>Month</th>
<th>Count</th>
</tr>
</thead>
<tbody>
{% for item in raport %}
<tr>
<td>{{ item.month }}</td>
<td>{{ item.department__count }}</td>
</tr>
{% endfor %}
</tbody>
</table>
To display it horizontally...
<table>
<thead>
<tr>
{% for item in raport %}
<th>{{ item.month }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
<tr>
{% for item in raport %}
<td>{{ item.department__count }}</td>
{% endfor %}
</tr>
</tbody>
</table>