I have two questions, I've recently built a django custom templatetag which displays a calendar when called. Am currently facing two issues which am not sure how to resolve,
How do I display day names as single character (S, M, T,..etc) I found calendar.day_abbr which returns (SAT, MON..etc)
My site is being used on several languages and I was wondering how do I get them to display as per viewing language. I tried using LocaleTextCalendar() but without any luck.
from django import template
import calendar
from django.conf import settings
register = template.Library()
def calendar_parser(parser, token):
"""
calendar parser will handle validating the parameters and passing them on to the context
"""
try:
tag_name, year, month, entries, as_, resolve_tag = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError, "%r tag requires six arguments" % token.contents.split()[0]
return calendar_node(year, month, entries, resolve_tag)
class calendar_node(template.Node):
"""
Process a particular node in the template. Fail silently.
"""
def __init__(self, year, month, entries, resolve_tag):
try:
self.year = template.Variable(year)
self.month = template.Variable(month)
self.entries = template.Variable(entries)
#resolved strings
self.resolve_tag = resolve_tag
except ValueError:
raise template.TemplateSyntaxError
def render(self, context):
try:
# FIRST_DAY_OF_WEEK beginning of the week, django setting
cal = calendar.LocaleTextCalendar(settings.FIRST_DAY_OF_WEEK, 'ar')
# render calendar header
context['week_header'] = [day for day in calendar.day_name]
# Get the variables from the context so the method is thread-safe.
my_entries = self.entries.resolve(context)
my_year = self.year.resolve(context)
my_month = self.month.resolve(context)
month_days = cal.itermonthdays(my_year, my_month)
lst = [[]]
week = 0
# make month lists containing list of days for each week
# each day tuple will contain list of entries and 'current' indicator
for day in month_days:
entries = current = False # are there entries for this day; current day?
lst[week].append((day, my_entries, current))
if len(lst[week]) == 7:
lst.append([])
week += 1
# assign variable to context as resolve_tag
context[self.resolve_tag] = lst
return ''
except ValueError:
return
except template.VariableDoesNotExist:
raise template.TemplateSyntaxError
Register the template tag so it is available to templates
register.tag("calendar_view", calendar_parser)
calendar.weekheader(n)
Return a header containing abbreviated weekday names. n specifies the width in characters for one weekday.
Thus for n=1 will return single character abbreviations.
Related
I have a model in Django that has two DateFields, but sometimes they receive wrong dates from the front-end, like '20196-10-23'. Even when it might actually be a valid date (crazy, but legit), python doesn't allow to compare both dates due to this error: TypeError: '>=' not supported between instances of 'datetime.date' and 'str', so I want to use clean() method to verify that both dates are valid ones, but I always get dates wrong, even when they are correct.
This is the code for clean() method:
def clean(self, *args, **kwargs):
try:
Reservation.objects.get(booking=self.booking)
except:
pass
else:
raise CustomValidation(_('Booking already exists.'), 'booking', status_code=status.HTTP_400_BAD_REQUEST)
print("{}/{}".format(self.arrival_date, self.departure_date))
try:
datetime.strptime(self.arrival_date, "%Y-%m-%d")
except:
raise CustomValidation(_('Arrival date must be a valid date.'), 'arrival_date', status_code=status.HTTP_400_BAD_REQUEST)
if self.arrival_date >= self.departure_date:
raise CustomValidation(_('Departure date must be later than Arrival date.'), 'departure_date', status_code=status.HTTP_400_BAD_REQUEST)
elif self.arrival_date <= timezone.datetime.now().date():
if self.id == None:
raise CustomValidation(_('Arrival date must be later than today.'), 'arrival_date', status_code=status.HTTP_400_BAD_REQUEST)
if self.status == 'CONFIRMED' and self.confirmation is None:
raise CustomValidation(_('Must provide a confirmation number.'), 'confirmation', status_code=status.HTTP_400_BAD_REQUEST)
I always get an exception, even when the date is correct.
While 20196 is conceptually a valid year, Python won't allow it. I tried this...
import datetime
from datetime import date
def clean(arrival_date):
return date(*map(int, arrival_date.split("-")))
print(clean("20196-10-23"))
# ValueError: year is out of range
...and found out there's actually a hard-coded maximum year value of 9999 for Python dates.
So while you could technically validate the format in various ways, you won't be able to handle dates like this with the built-in datetime module.
I usually first set both dates as datetime.strptime():
try:
#get the dates
my_date_time_1 = self.cleaned_data['date_1']
my_date_time_2 = self.cleaned_data['date_2']
#set the datetime
my_date_time_1 = datetime.strptime(my_date_time_1, '%Y-%m-%d %H:%M:%S')
my_date_time_2 = datetime.strptime(my_date_time_2, '%Y-%m-%d %H:%M:%S')
except:
raise forms.ValidationError(u'Wrong Date Format')
#and then compare dates
if my_date_time_1 >= my_date_time_2:
raise forms.ValidationError(u'Please check the dates')
I am creating an app which, on any given day, only one entity can be created per day. Here is the model:
class MyModel(ndb.Model):
created = ndb.DateTimeProperty(auto_now_add=True)
Since only one entity is allowed to be created per day, we will need to compare the MyModel.created property to today's date:
import datetime
class CreateEntity(webapp2.RequestHandler):
def get(self):
today = datetime.datetime.today()
my_model = MyModel.query(MyModel.created == today).get()
if my_model:
# print("Today's entity already exists")
else:
# create today's new entity
The problem is that I cannot compare the two dates like this. How can I check if an entity was already created 'today'?
I ended up changing the property from DateTimeProperty to DateProperty. Now I am able to do this:
today_date = datetime.datetime.today().date()
today_entity = MyModel.query(MyModel.created == today_date).get()
You are comparing a DateTime object with a Date object.
Instead of
my_model = MyModel.query(MyModel.created == today).get()
use
my_model = MyModel.query(MyModel.created.date() == today).get()
Seems like the only one solution is to use a "range" query, here's a relevant answer https://stackoverflow.com/a/14963648/762270
You can't query by created property using == since you don't actually know the exact creation datetime (which is what you'll find in created due to the auto_now_add=True option)
But you could query for the most recently created entity and check if its creation datetime is today. Something along these lines:
class CreateEntity(webapp2.RequestHandler):
def get(self):
now = datetime.datetime.utcnow()
# get most recently created one:
entity_list = MyModel.query().order(-MyModel.created).fetch(limit=1)
entity = entity_list[0] if entity_list else None
if entity and entity.created.year == now.year and \
entity.created.month == now.month and \
entity.created.day == now.day:
# print("Today's entity already exists")
else:
# create today's new entity
Or you could compute a datetime for today's 0:00:00 am and query for created bigger than that.
Or you could drop the auto_now_add=True option and explicitly set created to a specific time of the day (say midnight exactly) and then you can query for the datetime matching that time of day today.
Using a range query for a single specific known value you want to lookup is overkill and expensive, I would use one of these 2 solutions:
1 - Extra Property
Sacrifice a little space with an extra property, though since it's one per day, it shouldn't be a big deal.
from datetime import datetime
class MyModel(ndb.Model):
def _pre_put_hook(self):
self.date = datetime.today().strftime("%Y%m%d")
created = ndb.DateTimeProperty(auto_now_add=True)
date = ndb.StringProperty()
class CreateEntity(webapp2.RequestHandler):
def get(self):
today = datetime.today().strftime("%Y%m%d")
my_model = MyModel.query(MyModel.date == today).get()
if my_model:
logging.info("Today's entity already exists")
else:
# MyModel.date gets set automaticaly by _pre_put_hook
my_model = MyModel()
my_model.put()
logging.info("create today's new entity")
2 - Use [today] as Entity ID (preferred)
I would rather use today as the ID for my Entity, that's the fastest/cheaper/optimal way to retrieve your entity later. It could also be a combination with something else, i.e. ID=<userid+today>, in case that entity is per user, or maybe just add userid as a parent (ancestor). So it would be something like this:
from datetime import datetime
class MyModel(ndb.Model):
created = ndb.DateTimeProperty(auto_now_add=True)
class CreateEntity(webapp2.RequestHandler):
def get(self):
today = datetime.today().strftime("%Y%m%d")
my_model = MyModel.get_by_id(today)
if my_model:
logging.info("Today's entity already exists")
else:
my_model = MyModel(id=today)
my_model.put()
logging.info("create today's new entity")
I have implemented django-tables2 in my project. But I am not able to order by one of the fields (which I called from outside the app(model)). When I click on the ordering button in the GUI it is not working as expected.
The following is the code:
validation_time = tables.Column(accessor='model.history', verbose_name="Validation Time")
Here is the code to call that field:
def render_validation_time(self, value):
try:
x = value.filter(field_name__iexact='status').latest('id')
validated_time = datetime.fromtimestamp(int(x.date_modified)).strftime('%a, %d %b %Y %H:%M:%S')
except ObjectDoesNotExist:
return "--"
else:
return validated_time
To make a column orderable in Django-tables2 you only need to add
orderable=True
to the column and when you render the table in the template that field will be orderable.
Your column should be like this:
validation_time = tables.Column(accessor='model.history', orderable=True, verbose_name="Validation Time")
If you're overwritting the rendering method I don't know if this will work as expected
I have a list of objects. I want to display these objects in such a way that only the first unique date displays if the subsequent objects contain the same date. If the date is different than it should display. Here is an example.
data:
id: 2, date: "01/01/2010"
id: 3, date: "01/01/2010"
id: 4, date: "02/02/2010"
What I want to display:
id - 2, "01/01/2010"
id - 3,
id - 4, "02/02/2010"
See how id 3 shows nothing since the previous date was the same?
How do I do this with django templates? One thing I tried was creating a custom filter. The only problem is that it uses a global variable which is a no-no in my opinion. How can I maintain state in a function filter or in django templating language to be concious of the previous value?
__author__ = 'Dave'
#This works but isn't best practice
from django import template
register = template.Library()
a = ''
#register.filter()
def ensure_unique(value):
global a
if a == value:
return ''
else:
a = value
return value
Using a simple_tag made it much easier for me to save state and accomplish exactly what I needed to.
from django import template
register = template.Library()
#register.simple_tag(takes_context=True)
def stop_repeat(context, event):
"""
Finds various types of links embedded in feed text and creates links out of them.
"""
if event.date:
if (event.get_date_time_location(), event.id) in context:
return ''
else:
context[(event.get_date_time_location(), event.id)] = (event.get_date_time_location(), event.id)
return event.get_date_time_location()
What would be a way to have date range based rss feeds in Django. For instance if I had the following type of django rss feed model.
from django.contrib.syndication.feeds import Feed
from myapp.models import *
class PopularFeed(Feed):
title = '%s : Latest SOLs' % settings.SITE_NAME
link = '/'
description = 'Latest entries to %s' % settings.SITE_NAME
def items(self):
return sol.objects.order_by('-date')
What would be a way to have PopularFeed used for All Time, Last Month, Last Week, Last 24 Hours, and vice-versa if I wanted to have LeastPopularFeed?
You need to define a class for each feed you want. For example for Last Month feed:
class LastMonthFeed(Feed):
def items(self):
ts = datetime.datetime.now() - datetime.timedelta(days=30)
return sol.object.filter(date__gte=ts).order_by('-date')
Then add these feeds to your urls.py as shown in docs: http://docs.djangoproject.com/en/1.2/ref/contrib/syndication/