I have data in the following (simplified) format:
MetricData(models.Model) with following fields: id, metric, date, facility, value
Now I want to create a table with the following format (execute the script to get the indented output table):
<table style="width:100%">
<tr>
<th>Date</th>
<th>Facility 1</th>
<th>Facility 2</th>
<th>Facility 3</th>
</tr>
<tr>
<td>03/2019</td>
<td>1.0</td>
<td>1.5</td>
<td>2.5</td>
</tr>
<tr>
<td>04/2019</td>
<td>1.5</td>
<td>1.5</td>
<td>2.0</td>
</tr>
</table>
As you can see, the number of facilities which is dynamic (new ones can be added to the database), are the column headers. For each facility there will be metric data in the database.
All examples from django-datatables-view I find are basically using models directly and one model entry is converted to one table row.
You can override the QuerySet for your model to get a list of headers:
class MetricDataQuerySet(models.QuerySet):
#property
def headers(self):
return [getattr(instance, self.model.header_column) for instance in self]
class MetricData(models.Model):
header_column = 'facility'
...
objects = MetricDataQuerySet.as_manager()
Notice I added the header_column, instead of hard coding facility in the QuerySet. This allows you to reuse the QuerySet for different models if you end up needing to.
Now, in your view:
def some_view(request):
...
context = {
'objects': MetricData.objects.all()
}
return render(request, 'some_template.html', context)
Finally, on some_template.html, you can do this:
<table>
<tr>
{% for header in objects.headers %}
<th>{{ header }}</th>
{% endfor %}
</tr>
{% for object in objects %}
<tr>
<td>row.date</td>
<td>row.metric</td>
<td>row.value</td>
</tr>
{% endfor %}
</table>
Related
I have modeled my database using models.py within my django project, one of the fields is a JSONField and I can save json data into that field without any problem. My doubt comes in how I can show that information as an html table. At the moment I have been using ListView to show that information in a template but I don't know how to transform it into a table.
If you use object.json_field.items you can loop through them just like a normal dictonary / can also use .keys and .values
Example use in a table
<table>
<thead>
<tr>
<th>Key</th>
<th>Value</th>
</tr>
</thead>
<tbody>
{% for k, v in object.json_field.items %}
<tr>
<th>{{k}}</th>
<th>{{v}}</th>
</tr>
{% endfor %}
</tbody>
</table>
I made only one table in my application. I want to display some information on the page admin.html and
specific user information on info.html page. How should I give the query to show the particular user information on which client has clicked on the admin page. I want to display other attributes of my database like email, phone number, college name only on the info.html page. Here are my files:
database
class database(db.Model):
id=db.Column('user_id',db.Integer, primary_key=True)
name = db.Column(db.String(20))
text=db.Column(db.String(1000))
personality_score=db.Column(db.Integer)
skills_score=db.Column(db.Integer)
experience_score=db.Column(db.Integer)
total_score=db.Column(db.Integer)
college=db.Column(db.String(100))
email=db.Column(db.String(100))
phone_number=db.Column(db.String(100))
app.py
#app.route('/admin')
def admin():
return render_template('admin.html',database=database.query.order_by(database.total_score.desc()).all())
#app.route('/info')
def info():
return render_template('info.html')
admin.html
<table class="css-serial">
<tbody>
<tr>
<th>Rank</th>
<th>ID</th>
<th>Name</th>
<th>Profile Summary</th>
<th>Personality Score</th>
<th>Skills Score</th>
<th>Experience Score</th>
<th>Total Score</th>
</tr><tr>
{% for data in database %}
<td></td>
<td>{{data.id}}</td>
<td><a href=info>{{ data.name }}</a></td>
<td>{{ data.text }}</td>
<td>{{ data.personality_score }}</td>
<td>{{data.skills_score}}</td>
<td>{{data.experience_score}}</td>
<td>{{data.total_score}}</td>
</tr></tbody>
{% endfor %}
</table>
I have not written anything in the info.html file because I was not able to figure out how to fetch 'id' or data to display on that page.
Assuming that I understood your question correctly, here's what you wish to do:
You want to click on the anchor tag on the {{data.name}} and the person should first be redirected to the '/info' route.
On that route, the information associated with the person whose name was clicked is to be showed.
(1) is re-routing concept in flask. For which we use url_for method.
(2) is a basic WHERE query in sqlAlchemy ORM for which we can use the filter_by filter and first() collector.
Your route '/info' should take a parameter, user id, which we will use to filter the user information. Preferably, the info route can be changed as follows:
#app.route('/info/<id>')
def info(id):
user_info = database.query.filter_by(id=id).first()
return render_template('info.html', information=user_info)
Change your admin.html as follows:
<table class="css-serial">
<tbody>
<tr>
<th>Rank</th>
<th>ID</th>
<th>Name</th>
<th>Profile Summary</th>
<th>Personality Score</th>
<th>Skills Score</th>
<th>Experience Score</th>
<th>Total Score</th>
</tr>
<tr>
{% for data in database %}
<td></td>
<td>{{data.id}}</td>
<td>
{{ data.name }}
</td>
<td>{{ data.text }}</td>
<td>{{ data.personality_score }}</td>
<td>{{data.skills_score}}</td>
<td>{{data.experience_score}}</td>
<td>{{data.total_score}}</td>
</tr>
</tbody>
{% endfor %}
</table>
If you pay attention, in the method that's executed in the './info' route, we have added a parameter which will be used to filter information from the database.
filter_by basically creates the WHERE clause in any SQL based database and the first() is a collector of the information, which will basically give the information corresponding to the first row, that is satisfying the filter.
Since two people can have the same name, we pass the {{data.id}} in the filter.
Using the information parameter passed in the render_template() of '/info' route you can make the info.html page.
So this should work. I have answered the question based on my understanding of the question.
Your question was unclear.
Let me know if you don't understand something or if I have misunderstood something.
First, retrieve all the data in the database table in, say, the info() view function:
#app.route('/info')
def info():
page = request.args.get('page', 1, type=int)
user_info = database.query.order_by(
database.timestamp.desc()).paginate(
page, app.config['POSTS_PER_PAGE'], False
)
next_url = url_for('info',
page=user_info.next_num) \
if user_info.has_next else None
prev_url = url_for('info',
page=user_info.prev_num) \
if user_info.has_prev else None
return render_template('info.html',
title='User Information',
next_url=next_url,
prev_url=prev_url,
user_info=user_info.items
)
In my example, I am querying the database in the order of the time a user was recorded in the database. For now you do not have that field, but I just wanted to point that out in case you want to use it. That would mean you add a new column timestamp = db.Column(db.DateTime, default=datetime.utcnow) to database.
Additionally, I have added pagination such that in the event your database has so many entries, you can choose to show a certain amount of data in per page, with the option to move to page 2, 3 ... Read more from the documentation.
With data now in user_info, you can use a for loop to display all user information.
<table class="css-serial">
<tbody>
<tr>
<th>Rank</th>
<th>ID</th>
<th>Name</th>
<th>Profile Summary</th>
<th>Personality Score</th>
<th>Skills Score</th>
<th>Experience Score</th>
<th>Total Score</th>
</tr>
{% for data in user_info %}
<tr>
<td></td>
<td>{{data.id}}</td>
<td><a href=info>{{ data.name }}</a></td>
<td>{{ data.text }}</td>
<td>{{ data.personality_score }}</td>
<td>{{data.skills_score}}</td>
<td>{{data.experience_score}}</td>
<td>{{data.total_score}}</td>
</tr>
{% endfor %}
</tbody>
</table>
I got a view that receives a model and field the user passes via post (existing model and field inside the app of course) and makes a queryset filter on it, then i need to show in my template that result in a table (name fields must be the column headers) and their respective values.
This what i got so far trying to serialize the queryset result in order to make it easier to show in template:
Views.py:
from django.contrib.contenttypes.models import ContentType
class CommitteeReport(BaseView):
template_name = 'committee/committee_report.html'
def post(self, request, **kwargs):
myfield = request.POST['field'].lower()
my_model = request.POST['model'].lower()
queryset_obj = ContentType.objects.get(model = my_model).model_class().objects.filter(**{myfield:True})
return render(request, self.template_name,{
'requirements': queryset_obj,
})
And my template:
<div class="tab-pane active" id="tab_1">
<table class="datatable table table-striped table-hover" cellspacing="0">
<thead>
<tr>
{% for key in requirements %}
<th>{{ key.fields.name }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for item in requirements %}
<tr>{{ item.fields.value }}</tr>
{% endfor %}
</tbody>
</table>
</div>
Thing is, i don't get result or if i change the tag inside the template, i get the objects dictionary for every row.
Any idea of how to achieve what i need ?, thanks in advance.
You can get the list of fields of model using my_instance._meta.local_fields. So you could try this in your view before return:
if queryset_obj.exists():
fields = queryset_obj.first()._meta.local_fields
else:
fields = []
return render(request, self.template_name,{
'requirements': queryset_obj,
'fields': fields,
})
And then using the context variable fields in your template:
<table class="datatable table table-striped table-hover" cellspacing="0">
<thead>
<tr>
{% for field in fields %}
<th>{{ field.name }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for req in requirements %}
<tr>
{% for field in fields %}
<td>{{ req|lookup:field.name }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
where you define a custom lookup template filter to fetch the field value:
def lookup(model, attr):
if hasattr(model, attr):
return getattr(model, attr)
else:
return None
Note: I'm assuming that all requirements are the same class and have the same structure, so that requirements.0, the first object in the QuerySet, is used for making the headers of the table.
Note 2: I haven't tried this, you might move some of the work to the view and pass easier variables as context variables (e.g. the list of field names).
Note 3: I haven't added error handling, you should check if model is actually a Model instance for example.
There is a char field named json_field in Django Model. I am trying to iterate it from the view but it returns only one result as the return statement does. I am trying to figure it out how I can iterate json_field using yield.
the result that Model Object returns like:
id : 1
title : "Some Title"
json_field : [{"key":"value","key2":"value2"},{"key":"value","key2":"value2"}]
created : "Sat Oct 21 2017 14:00:53 GMT+0300 (+03)"
view.py
import json
def MyView(request):
model_query = MyModel.objects.all() or MyModel.objects.filter or exclude...
for item in model_query:
data_item = json.loads(item.json_field)
template = "template.html"
context = {"title":title, "data_item":data_item}
return render(request, template, context)
in template.html
{% for query_item in model_query %}
<table>
<thead>
<tr>
<th>{{ query_item.title }} - {{ query_item.created }}</th>
</tr>
</thead>
<tbody>
<tr>
<th>Some Heading </th>
<th>Some Heading </th>
</tr>
<!-- json data -->
{% for item in data_item %}
<tr>
<th>{{ item.key }}</th>
<td>{{ item.key2|floatformat:2 }}</td>
</tr>
{% endfor %}
<!-- json data -->
</thead>
</table><
{% endfor %}
Any help will be appreciated.
You can prepare dataset for you template.
# Fetch data from db as queryset of dicts
items = list(MyModel.objects.filter().values('title', 'created', 'json_field'))
# Decode json in-place
for item in items:
item['json_field'] = json.loads(item['json_field'])
context = {"title":title, "items": items}
Then interate through items inside your template:
{% for item in items %}
<table>
<thead>
<tr>
<th>{{ item.title }} - {{ item.created }}</th>
</tr>
</thead>
<tbody>
<tr>
<th>Some Heading </th>
<th>Some Heading </th>
</tr>
<!-- json data -->
{% for entry in item.json_field %}
<tr>
<th>{{ entry.key }}</th>
<td>{{ entry.key2|floatformat:2 }}</td>
</tr>
{% endfor %}
<!-- json data -->
</thead>
</table><
{% endfor %}
If you're using PostgreSQL, you can using JSONField. It uses the postgres's jsonb type, which is optimized for keeping a json serializable text.
If not, you still can use django-jsonfield. It almost gives the same functionality, even though some of the cool features of django's JSONField are not available (like this kind of lookups).
If none of these work for you, you can also implement your own JSONField by inheriting from CharField or TextField, and overriding some of the functions. This way, you won't need any of the logics of your field in your views.
Edit:
If you find changing your field hard or don't wanna do it for whatever reason, you can do this in your view:
for item in model_query:
item.loaded_json = json.loads(item.json_field)
then you can use it like a normal field in your template:
{% for query_item in model_query %}
{% for item in query_item.loaded_json %}
<span>{{ item.key }}</spam>
{% endfor %}
{% endfor %}
Hello!
The solution depends on your purposes.
Use comprehensions if you want to construct a list of json arrays:
data_items = [json.loads(item.json_field) for item in model_query]
... or generator of json array:
data_items = (json.loads(item.json_field) for item in model_query)
If you want to have a single array of json objects try this:
data_items = []
for item in model_query:
data_items.extend(json.loads(item.json_field))
Then you can use data_items as a template context.
A little tip: You can utilize JSONField at ORM level if you use PostgreSQL or MySQL. Consider this approach if you plan to make any filter queries on this field. As additional benefit JSON encoding/decoding will be out of box.
Thanks for updating your code!
Now I would restructure the json.load() list of dicts so you can use it. That is better style than mangling in the template.
concatenation is done by:
my_dict = dict()
for d in data_item
my_dict.update( d )
if you want to merge, check this thread:
How to merge two dictionaries in a single expression?
Goal: {% for loop %} over a list (using Jinja2) and then print out results {{print}} in a HTML table using Bootstrap.
Problem: List is not printing in the template.
In the view_config, I used query .all() to return a list of all the assessment_results objects. They are returning... I confirmed this via terminal/print debugging. However, the for loop is not returning the values needed to populate a table; as read in Jinja2 tutorial. I don't think I need to use a for loop in the view_config as I have seen others do (see here), but I am new to this and am trying to figure out how these two programs (SQLALCHEMY and Jinja2) interact.
An example from the printout after using .all() mentioned above:
[<Assessment_Result(owner='<User(username ='baseball', firstname ='Jen', lastname ='See', email='girl#aol.com')>', assessment='<Assessment(name='Becoming a Leader', text='better decisions')>')>]
view_config code:
views.py
#view_config(route_name='assessment_results', request_method='GET', renderer='templates/assessment_results.jinja2')
def all_assessment_results(request):
with transaction.manager: # < --- THIS WAS THE ISSUE !
assessment_results = api.retrieve_assessment_results()
if not assessment_results:
raise HTTPNotFound()
return {'assessment_results': assessment_results}
Corresponding Jinja2 template using Bootstrap:
assessment_results.jinja2
<div class="container">
<table class="table table-hover">
<thead>
<tr>
<td> Assessment ID </td>
<td> Assessment </td>
<td> Owner </td>
</tr>
</thead>
<tbody>
<tr>
{% for x in assessment_results %}
<td>{{ x.assessments|e }}</td>
<td>{{ x.owners|e}}</td>
{% else %}
<td><em>no users found</em></td>
{% endfor %}
</tr>
</tbody>
</table>
</div>
You should look at the documentation
http://jinja.pocoo.org/docs/dev/templates/#for
You want to iterate over a dict, so consider using iteritems, itervalues or what ever you want.
Also note that your query will not return a dict, it will return a list or rows that matched.
I am also not sure if the for-else works in jinja. But you should avoid using that anyways.