How to order by in Django-tables2 - python

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

Related

Short description isn't working for delete selected (delete_selected.short_description isn't changing the name)

I am currently trying to change the name of the "Delete Selected" admin action. I have already effectively override the default (so I can store some data before completely deleting it), but now I want to change the option from the vague "Deleted selected" to something more specific like "Deleted all selected registrations." Or, at least, for it to say, "Deleted selected registrations" like it did before I overwrote the function.
I have so far tried this:
delete_selected.short_description = 'Delete all selected registrations'
But the option is still "Deleted selected." Is there a way to fix this?
Here's my code:
def delete_selected(modeladmin, request, queryset):
"""
This overrides the defult deleted_selected because we want to gather the data from the registration and create a
DeletedRegistration object before we delete it.
"""
for registration in queryset:
reg = registration.get_registrant()
if registration.payment_delegation:
delegate_name = registration.payment_delegation.name
delegate_email = registration.payment_delegation.email
else:
delegate_name = None
delegate_email = None
registration_to_delete = DeletedRegistration.objects.create(
registrant_name = reg.full_name(),
registrant_email = reg.email,
registrant_phone_num = reg.phone,
delegate_name = delegate_name,
delegate_email = delegate_email,
# Filtering out people (with True) who couldn't participate in events because we are only interested in the people
# we had to reserve space and prepare materials for.
num_of_participants = registration.get_num_party_members(True),
special_event = registration.sibs_event,
)
registration.delete()
delete_selected.short_description = 'Delete all selected registrations'
edit: just tried delete_selected.list_display that didn't work either
You can't have it in the function, so I just had to tab it back one space and it worked.
example:
def delete_selected(modeladmin, request, queryset)
code
delete_selected.short_description = "preferred name"
thanks.

Flask Admin: Format the way relationships are displayed

I would like to change the way the relationships are displayed in the Flask-Admin Index view of a Model. I do have two models connected through a many-to-many relationship which get displayed in the admin index view as well. Unfortunately, the relationships are just separated using a comma, which thus the user might lose the overview quickly. Ideally, I would like to convert the relationships entries into a simple list (e.g. like with li in HTML).
Is there an easy way of achieving this?
Thanks a lot!
Instead of the comma, you can use a <br> tag as the separator.
see column_type_formatters.
Define default formatter and update a list type.
def lineby_list_formatter(view, values):
html = u'<br/> '.join(str(v) for v in values)
return Markup(html)
MY_DEFAULT_FORMATTERS = dict(typefmt.BASE_FORMATTERS)
MY_DEFAULT_FORMATTERS.update({
list: lineby_list_formatter
})
class EventView(ModelView):
...
column_type_formatters = MY_DEFAULT_FORMATTERS
Ok... I figured it out myself: You can manipulate the way the data is rendered with overwriting the function _get_list_value(). see code below
def _get_list_value(self, context, model, name, column_formatters,
column_type_formatters):
"""
Returns the value to be displayed.
:param context:
:py:class:`jinja2.runtime.Context` if available
:param model:
Model instance
:param name:
Field name
:param column_formatters:
column_formatters to be used.
:param column_type_formatters:
column_type_formatters to be used.
"""
column_fmt = column_formatters.get(name)
if column_fmt is not None:
value = column_fmt(self, context, model, name)
else:
value = self._get_field_value(model, name)
choices_map = self._column_choices_map.get(name, {})
if choices_map:
return choices_map.get(value) or value
type_fmt = None
for typeobj, formatter in column_type_formatters.items():
if isinstance(value, typeobj):
type_fmt = formatter
break
if type_fmt is not None:
value = type_fmt(self, value)
### overwritten here
if name == 'items':
html_string = '<ul>'
for item in value.split(','):
html_string += '<li> {} </li>'.format(item)
html_string += '</ul>'
value = Markup(html_string)
return value

Why is this Django QuerySet returning no results?

I have this Class in a project, and I'm trying to get previous and next elements of current one.
def get_context(self, request, *args, **kwargs):
context = super(GuidePage, self).get_context(request, *args, **kwargs)
context = get_article_context(context)
all_guides = GuidePage.objects.all().order_by("-date")
context['all_guides'] = all_guides
context['next_guide'] = all_guides.filter(date__lt=self.date)
context['prev_guide'] = all_guides.filter(date__gt=self.date)
print context['next_guide']
print context['prev_guide']
return context
These two lines:
context['prev_guide'] = all_guides.filter(date__lt=self.date)
context['next_guide'] = all_guides.filter(date__gt=self.date)
are returning empty results as printed in the console:
(QuerySet[])
(QuerySet[])
What am I missing?
EDIT:
I changed lt and gt to lte and gte. As I understand that will include results that are also equal in date.
In this case I got ALL elements. All elements were created the same day, but, of course, at different times, so they should be different by minutes. Is this difference not taking into account when filtering for greater/lesser ?
If you want to filter not only by date, but time also, you must change the relevant field in your model to be of DateTimeField instead of DateField.
Like this:
from django.db import models
class MyModel(models.Model):
date_time = models.DateTimeField()
Now, you can do stuff like all_guides.filter(date_time__lte=self.date_time) or all_guides.filter(date_time__gte=self.date_time).
Carefull of the two underscores __.

displaying unique objects only in django templates

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()

django internationalizing a calendar and using single character day name

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.

Categories