Flask jinja display date fields correctly - flask

I've found a shorthand method to fetch the results from MongoDb and to pass to the jinja template.
#app.route('/home')
def home():
table = mongo.db.posts
result = table.find( { } ).sort([("postdate", 1)])
records = json.loads(json_util.dumps(result))
if result.count() > 0:
return render_template('users/index.html', posts=records)
else:
message = 'I couldn't find any post'
return render_template('users/index.html', message=message)
And in users/index.html I would like to display the results like this:
{% for post in posts %}
<tr>
<td>{{post._id}}</td>
<td>{{post.title}}</td>
<td>{{post.author}}</td>
<td class="date">{{post.postdate}}</td>
</tr>
{% endfor %}
Everything works as expected except date fields. Is there a way to display date fields correctly ?
{'$date': 1508227970796}
{'$date': 1508228089163}
{'$date': 1508241780398}

What you are seeing is the timestamp of this date, there are two possibilities to solve this issue:
you ca use this inside you view to convert the date into a readable format before sending the array to the Jinja template
from datetime import datetime
datetime.fromtimestamp(the_date_you_want_to_convert)
or you can create a simple Jinja filter which will convert a given timestamp into your readable date format.

Related

django - template IF tag - how to do IF variable <= current date [duplicate]

I would like to compare a date to the current date in Django, preferably in the template, but it is also possible to do before rendering the template. If the date has already passed, I want to say "In the past" while if it is in the future, I want to give the date.
I was hoping one could do something like this:
{% if listing.date <= now %}
In the past
{% else %}
{{ listing.date|date:"d M Y" }}
{% endif %}
With now being today's date, but this does not work. I couldn't find anything about this in the Django docs. Can anyone give some advice?
Compare date in the view, and pass something like in_the_past (boolean) to the extra_context.
Or better add it to the model as a property.
from datetime import date
#property
def is_past_due(self):
return date.today() > self.date
Then in the template:
{% if listing.is_past_due %}
In the past
{% else %}
{{ listing.date|date:"d M Y" }}
{% endif %}
Basically the template is not the place for date comparison IMO.
As of Django 1.8 the following slightly distasteful construct does the job:
{% now "Y-m-d" as todays_date %}
{% if todays_date < someday|date:"Y-m-d" %}
<h1>It's not too late!</h1>
{% endif %}
Hackish, but it avoids the need for a custom tag or context processor.
I added date_now to my list of context processors.
So in the template there's a variable called "date_now" which is just datetime.datetime.now()
Make a context processor called date_now in the file context_processors.py
import datetime
def date_now(request):
return {'date_now':datetime.datetime.now()}
And in settings.py, modify CONTEXT_PROCESSORS to include it, in my case it's
app_name.context_processors.date_now
addition to #bx2 beneficial answer, if your field is a datetime field just call date() function to models datetimefield:
from datetime import date
#property
def is_past_due(self):
if date.today() > self.date.date():
return True
return False
EDIT: i think this could be shrunken to:
from datetime import date
#property
def is_past_due(self):
return date.today() > self.date.date()
I believe the easiest way to achieve this is by importing datetime in your views.py and passing today's date as context data.
context['today'] = datetime.date.today()
Then in the template tags you could do something like this.
{% if listing.date < today % }
This way if you are passing a list of objects as context, you can apply the filter to each line as you output it in the HTML template. I had a list of items that I had filtered out and was using Bootstrap to stylize as I displayed them. I wanted to make overdue dates stand out and applied the filtering only if one of my dates was less than today's date.
You can always pass datetime.datetime.now (since django models use Python's standard datetime object).
Using render_to_response, you could do something like this (after importing datetime):
return render_to_response('template.html', {'now': datetime.datetime.now()})
Now that you have access to "now" inside of the template, you can compare dates just like you did in your examples.
Furthermore, if you use RequestContext in your views - you will be able to add "now" as a context_processor if you need this in multiple files. This will add "now" to any template rendered with a RequestContext.
However, it is more realistic that you simply just get the list of records that are before now in your original queryset and avoid querying for useless data in the first place:
listing.objects.filter(date__lt=datetime.datetime.now())
You can have 2 tasks.
You must show the object using DetailView. You can solve this problem by using a function that passes a boolean value to the template. However, keep in mind that in django you cannot compare a date from a SQL database with datetime.datetime.now(), since the second option does not contain time zone information.
Use django.utils.timezone.now().
You must style the object from the ListView. Then I advise you to use a custom filter that you will call in the template.
#register.filter
def compare_date(date):
if not date:
return False
return date < timezone.now()
If you use Django, you could use the bellow code(https://stackoverflow.com/a/3798865/17050678).
In your models.py
#property
def is_past_due(self):
today = datetime.datetime.today()
return today > self.start_date
But it will take an error to you. like this:
can't compare offset-naive and offset-aware datetimes
So you should take the timezone type like the bellow.
import pytz
#property
def is_past_due(self):
today = datetime.datetime.today().astimezone(pytz.timezone('Asia/Tokyo'))
return today > self.start_date
Then you could compare it in your template.
{% if schedule.is_past_due %}
/*your code here*/
{% endif %}
I found this question and had a similar problem. I was looking to display information if it had only occurred in the past.
Using Django's "timesince" tag in the template I was able to solve my problem. I'm not saying it's efficient, but it works for me.
In Template:
{% if model_name.date_time|timesince >= "1 min" %}
<p>In the past</p>
{% else %}
<p>{{ model_name.date_time }}</p>
{% endif %}

Unable to iterate a dictionary in django templating language

I have not been able to iterate (key and values) in a nested dictionary generated from views.py where
context = { "orders_current": orders_current }
with
return render(request, "orders/cart.html", context)
orders_current is a result from a query in views.py:
orders_current = Orders.objects.values('def', 'abc', 'toppings')
'toppings' is stored in the database as JSON data but converted (loads) back to a dictionary in the class method:
def __str__(self)
in models.py. I did this since I read somewhere this is a recommend way of storing a dictionary in the postgresql.
Note that orders_current has multiple and nested dictionaries e.g.:
< QuerySet [{'category_name': 'Regular Pizza', 'size': 'small', 'item_name': 'Cheese', 'item_price': Decimal('12.70'), 'toppings_selected': True, 'toppings': '{"Mushrooms": 1.5, "Canadian Bacon": 1.5}'}] >
The dictionary {{ order.toppings }} passed to the html, cart.html is shown to have the value (in verbatim) e.g.:
{"Mushrooms": 1.5, "Canadian Bacon": 1.5}
So my latest attempt to extract the topping name and the corresponding price (key, value) from the dictionary is:
{% for order in orders_current %}
...
<table>
{% for name, price in order.toppings.items %}
<tr>
<td>{{ name }}:</td>
<td>${{ price }}</td>
</tr>
{% endfor %}
</table>
I got no values (name, price)from this code snippet.
Based on my web searches, I've tried almost all variations of the above and with different ways of setting up the context for transfer. Any help will be appreciated.
Based on your queryset:
< QuerySet [{'category_name': 'Regular Pizza', 'size': 'small', 'item_name': 'Cheese', 'item_price': Decimal('12.70'), 'toppings_selected': True, 'toppings': '{"Mushrooms": 1.5, "Canadian Bacon": 1.5}'}] >
order.toppings is a string, try to convert it to dictionary in the views:
import ast
for order in orders_current:
order["toppings"] = ast.literal_eval(order["toppings"])
Here are different ways to convert string dictionary to dictionary.
https://www.geeksforgeeks.org/python-convert-string-dictionary-to-dictionary/
After you convert it, it should work in the template.

Django - MultipleChoiceField is being displayed as a string instead of a list

I have a MultipleChoiceField forms field (associated with a models CharField) which shows up in the database like this. It seems to have been converted to a string in the database, because when I try to display it with a 'for property in property_type' statement in the HTML, it shows up like this. I want it to be displayed like this
So I have to write some code to fix this issue. My pseudocode will look something like:
for record in property_type:
split record at comma
for i in record:
if record[i] is not a letter or number:
remove record[i]
Now my question is, where do I write this code? Do I write it in the views.py or in the HTML file? I tried doing it in views but I don't know how to select a single database record. I tried doing it in the HTML but I was limited by the template tags.
Here is the shortened version of my code:
models.py:
property_type = models.CharField(max_length=50, help_text="You can select more than 1 option")
forms.py:
property_type = forms.MultipleChoiceField(widget=forms.SelectMultiple, choices=BuyerListing.PROPERTY_TYPE)
HTML:
{% for property in listing.property_type %}
<p>Property type: {{ property }}</p>
{% endfor %}
EDIT:
Got it to work thanks to #ytsejam and #Daniel Roseman. With ytsejam's code, the result will show up like this:
['1' '2']
I added a basic regex to ytsejam's code to remove the brackets and quotes:
def split_properties(self):
a = self.property_type
a = re.sub(r'[\]\[\']', '', a)
a = a.split(',')
return a
Now the list will display like this, which is very easy to manipulate in HTML.
1 2
in your models.py
def split_properties(self):
return self.properties.split(',')
and in your template use
{% for property in className.split_properties %} {{ property }} {% endfor %}

Custom template filter syntax in django

So i have created a custom filter:
#register.filter(name='field_values')
def field_values(model, field):
return model.objects.values_list(field, flat=True)
that takes model class value and a field for an argument and returns a list of model objects for that particular field. Here is the template where I try to call it.
<tr>
{% for field in fields %}
<td>{{ model_class|field_values: field }} </td>
{% endfor %}
</tr>
model_class is a dynamically created model in the views as such
views.py
...
if form.is_valid(): # All validation rules pass
model_classes_field = form.cleaned_data['model_classes_field']
model_class = get_model('Directories', model_classes_field)
I was expecting everything to run smoothly but instead I get a "*field_values requires 1 arguments, 0 provided*" error. Why does this happen when i have already inserted an argument?? is it a problem with the filter syntax?
You have a space after the colon which is confusing the parser. Use it like this instead:
{{ model_class|field_values:field }}
(Note that your code is pretty inefficient: it's querying the database over and over again, once for each field. Rather than doing this, you should probably query it once in the view and group the output by field.)

Output Django Model as Table

I have a view definition that (attempts to) outputs a model as a table. This is what I have so far:
def output_table(request):
output = My_Model()
return render_to_response('outputtable.html', {'output': output})
Here's the HTML for outputtable.html:
<html>
<table>
{{ output.as_table }}
</table>
</html>
What am I doing wrong? It doesn't work. Now, it's passing the model correctly, because if I change My_Model() to My_Model.objects.all() and then output it as simply {{ output }} then it shows me what I would see in the Django shell.
So what you need to do is the following:
1) add
from django.forms import ModelForm to your models.py
2) add
class My_Model_Form(ModelForm):
class Meta:
model = My_Model
3) in your views.py, change output = My_Model() to output = My_Model_Form()
Now you are all set. So the trick is to inherit your Form from your original model.
If you're just looking to output one specific model, change your template to something like
<tr>
<td>Name:</td>
<td>{{ output.name}}</td>
</tr>
for all the fields you care about. If this is something you want to be able to do for any arbitrary model in your app, take a look at this snippet. That would provide you with a fields collection to loop over.
The models.Model class doesn't have a method as_table() like the forms.ModelForm class does.
My solution was to also use template tags. I went with an inclusion tag.
myapp/templatetags/model_helpers.py
from django import template
register = template.Library()
#register.inclusion_tag('myapp/model_table.html', takes_context=True)
def model_as_table(context, model_key=None, model_table_attrs_key=None):
if model_key is None:
model_key = 'object'
if model_table_attrs_key is None:
model_table_attrs_key = 'model_table_attrs'
try:
attrs = context[model_table_attrs_key]
except KeyError:
attrs = context[model_key]._meta.get_all_field_names()
table_context = {'rows': []}
for attr in attrs:
try:
value = str(getattr(context[model_key], attr))
if value:
table_context['rows'].append({'attr': attr,
'value': context[model_key][attr]})
except AttributeError:
pass
# Needs a way to display many_to_many fields.
except StopIteration:
pass
return table_context
myapp/templates/myapp/model_table.html
{% for row in rows %}
<tr>
<td class="name">{{ row.attr }}</td>
<td class="field">{{ row.value }}</td>
</tr>
{% endfor %}
myapp/templates/myapp/outputtable.html
{% load model_helpers %}
<table>
{% model_as_table %}
</table>
With this implementation you can pass which model's attributes you want to display and in which order.
myapp/views.py
def output_table(request):
output = My_Model()
return render_to_response('outputtable.html',
{'output': output, 'model_table_attrs': ['attr1', 'attr2']})
I also like this cleanly separate html and python code.
There is no method as_table on a model instance (MyModel())or on Querysets (MyModel.objects.all()). You may have seen as_table as the forms method as_table. You don't have a form there.
If you want to print a model instance/ a queryset as a table, you will have to design it yourself.
OMG this is old but Generic Views seem to be a good fit for this problem, for me at least. A List detail view should help me get running faster. :)
I came up with a solution that worked for my specific need which renders any simple model's data into table rows. Add the following filter into templatetags/your_tags_file.py (don't forget the __init__.py file in the templatetags folder):
from django import template
register = template.Library()
#register.filter()
def as_table(model):
ret = ""
for name in model._meta.get_all_field_names():
try:
field = str(getattr(model, name))
if field:
ret += '<tr><td class="name">'+name+'</td><td class="field">'+field+'</td></td>'
except AttributeError:
pass
return ret
In the template now you can just do:
{% load your_tags_file %}
<table>
{{output|as_table|safe}}
</table>
This will render the model in a simple table for you. You can easily add any desired thead and tbody logic as you see fit by modifying the logic of how ret is generated. Hope this helps someone.