I have code within a context processor that displays the date in a navbar via the {{date}} tag using the function I created:
import datetime
def date(request):
nav_date = datetime.date.today()
return {
"date" : nav_date
}
However, I would like to create a function to change the date so that whenever a button is clicked in the html code, the function would push the date from the nav_date variable 1 day forward.
def next_day(request):
# add 1 to nav_date and return value
You need to have some kind of common storage for sync button click with context processor's date. I am using session for this example. So you can try like this:
# context processor
def date(request):
nav_date = request.session.get('nav_date')
if nav_time:
nav_time=datetime.datetime.strptime(nav_time, '%d-%m-%Y')
else:
nav_time = datetime.date.today()
return {
"date" : nav_date
}
now need to write a view for updating the date and storing it in session:
def update_date(request):
nav_date = request.session.get('nav_date')
if nav_time:
nav_time=datetime.datetime.strptime(nav_time, '%d-%m-%Y')
else:
nav_time = datetime.date.today()
updated_nav_date = nav_date + datetime.timedelta(days=1)
request.session['nav_date'] = updated_nav_date.strftime('%d-%m-%Y')
return HttpResponse("Date Updated")
define urls:
path('upate-date/' update_date, name="update_date")
Use it in the form:
<form action='{% url "update_date" %}' method='GET'>
<button type='submit'> Update Date</button>
</form>
Use the timedelta function to add/subtract days.
nav_date = nav_date + datetime.timedelta(days=1)
What you'll need is a counter on the frontend or have a url with the number of days to increment.
i.e GET /?number_of_days=1
from datetime import timedelta
from django.utils import timezone
def date(request):
nav_date = timezone.now() + timedelta(days=request.GET.get('number_of_days', 0))
return {
"current_date" : nav_date.date()
}
There's also a templatetag which can be used to format dates.
https://docs.djangoproject.com/en/2.1/ref/templates/builtins/#now
It is {% now "jS F Y H:i" %}
Which should be It is 2nd December 2018 18:11
Related
Timezones in Django...
I am not sure why this is so difficult, but I am stumped.
I have a form that is overwriting the UTC dateTime in the database with the localtime of the user. I can't seem to figure out what is causing this.
my settings.py timezone settings look like:
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'America/Toronto'
USE_I18N = True
USE_L10N = False
USE_TZ = True
I am in Winnipeg, my server is hosted in Toronto. My users can be anywhere.
I have a modelfield for each user that is t_zone = models.CharField(max_length=50, default = "America/Winnipeg",) which users can change themselves.
with respect to this model:
class Build(models.Model):
PSScustomer = models.ForeignKey(Customer, on_delete=models.CASCADE)
buildStart = models.DateTimeField(null=True, blank=True)
...
I create a new entry in the DB using view logic like:
...
now = timezone.now()
newBuild = Build(author=machine,
PSScustomer = userCustomer,
buildStart = now,
status = "building",
addedBy = (request.user.first_name + ' ' +request.user.last_name),
...
)
newBuild.save()
buildStart is saved to the database in UTC, and everything is working as expected. When I change a user's timezone in a view with timezone.activate(pytz.timezone(self.request.user.t_zone)) it will display the UTC time in their respective timezone.
All is good (I think) so far.
Here is where things go sideways:
When I want a user to change buildStart in a form, I can't seem to get the form to save the date to the DB in UTC. It will save to the DB in whatever timezone the user has selected as their own.
Using this form:
class EditBuild_building(forms.ModelForm):
buildStart = forms.DateTimeField(input_formats = ['%Y-%m-%dT%H:%M'],widget = forms.DateTimeInput(attrs={'type': 'datetime-local','class': 'form-control'},format='%Y-%m-%dT%H:%M'), label = "Build Start Time")
def __init__(self, *args, **kwargs):# for ensuring fields are not left empty
super(EditBuild_building, self).__init__(*args, **kwargs)
self.fields['buildDescrip'].required = True
class Meta:
model = Build
fields = ['buildDescrip', 'buildStart','buildLength'...]
labels = {
'buildDescrip': ('Build Description'),
'buildStart': ('Build Start Time'),
...
}
widgets = {'buildDescrip': forms.TextInput(attrs={'class': 'required'}),
and this view:
class BuildUpdateView_Building(LoginRequiredMixin,UpdateView):
model = Build
form_class = EditBuild_building
template_name = 'build_edit_building.html'
login_url = 'login'
def get(self, request, *args, **kwargs):
proceed = True
try:
instance = Build.objects.get(id = (self.kwargs['pk']))
except:
return HttpResponse("<h2 style = 'margin:2em;'>This build is no longer available it has been deleted, please please return to dashboard</h2>")
if instance.buildActive == False:
proceed = False
if instance.deleted == True:
proceed = False
#all appears to be well, process request
if proceed == True:
form = self.form_class(instance=instance)
timezone.activate(pytz.timezone(self.request.user.t_zone))
customer = self.request.user.PSScustomer
choices = [(item.id, (str(item.first_name) + ' ' + str(item.last_name))) for item in CustomUser.objects.filter(isDevice=False, PSScustomer = customer)]
choices.insert(0, ('', 'Unconfirmed'))
form.fields['buildStrategyBy'].choices = choices
form.fields['buildProgrammedBy'].choices = choices
form.fields['operator'].choices = choices
form.fields['powder'].queryset = Powder.objects.filter(PSScustomer = customer)
context = {}
context['buildID'] = self.kwargs['pk']
context['build'] = Build.objects.get(id = (self.kwargs['pk']))
return render(request, self.template_name, {'form': form, 'context': context})
else:
return HttpResponse("<h2 style = 'margin:2em;'>This build is no longer editable here, or has been deleted, please return to dashboard</h2>")
def form_valid(self, form):
timezone.activate(pytz.timezone(self.request.user.t_zone))
proceed = True
try:
instance = Build.objects.get(id = (self.kwargs['pk']))
except:
return HttpResponse("<h2 style = 'margin:2em;'>This build is no longer available it has been deleted, please please return to dashboard</h2>")
if instance.buildActive == False:
proceed = False
if instance.deleted == True:
proceed = False
#all appears to be well, process request
if proceed == True:
form.instance.editedBy = (self.request.user.first_name)+ " " +(self.request.user.last_name)
form.instance.editedDate = timezone.now()
print('edited date ' + str(form.instance.editedDate))
form.instance.reviewed = True
next = self.request.POST['next'] #grabs prev url from form template
form.save()
build = Build.objects.get(id = self.kwargs['pk'])
if build.buildLength >0:
anticipated_end = build.buildStart + (timedelta(hours = float(build.buildLength)))
print(anticipated_end)
else:
anticipated_end = None
build.anticipatedEnd = anticipated_end
build.save()
build_thres_updater(self.kwargs['pk'])#this is function above, it updates threshold alarm counts on the build
return HttpResponseRedirect(next) #returns to this page after valid form submission
else:
return HttpResponse("<h2 style = 'margin:2em;'>This build is no longer available it has been deleted, please please return to dashboard</h2>")
When I open this form, the date and time of buildStart are displayed in my Winnipeg timezone, so Django converted from UTC to my timezone, perfect, but when I submit this form, the date in the DB has been altered from UTC to Winnipeg Time. Why is this?
I have tried to convert the submitted time to UTC in the form_valid function, but this does not seem like the right approach. What am I missing here?
I simply want to store all times as UTC, but display them in the user's timezone in forms/pages.
EDIT
When I remove timezone.activate(pytz.timezone(self.request.user.t_zone)) from both get and form_valid, UTC is preserved in the DB which is great. But the time displayed on the form is now in the default TIME_ZONE in settings.py. I just need this to be in the user's timezone....
EDIT 2
I also tried to add:
{% load tz %}
{% timezone "America/Winnipeg" %}
{{form}}
{% endtimezone %}
Which displayed the time on the form correctly, but then when the form submits, it will again remove 1 hour from the UTC time in the DB.
If I change template to:
{% load tz %}
{% timezone "Europe/Paris" %}
{{form}}
{% endtimezone %}
The time will be displayed in local Paris time. When I submit the form, it will write this Paris time to the DB in UTC+2. So, in summary:
Time record was created was 11:40 Winnipeg time, which writes
16:40 UTC to database, perfect
I access the form template, and time is displayed as local Paris time, 6:40pm, which is also what I would expect.
I submit form without changing any fields.
Record has been updated with the time as 22:40, which is UTC + 6 hours.
What is happening here!?
Put simply: your activate() call in form_valid() comes too late to affect the form field, so the incoming datetime gets interpreted in the default timezone—which in your case is America/Toronto—before being converted to UTC and saved to the database. Hence the apparent time shift.
The documentation doesn't really specify when you need to call activate(). Presumably, though, it has to come before Django converts the string value in the request to the aware Python datetime in the form dictionary (or vice versa when sending a datetime). By the time form_valid() is called, the dictionary of field values is already populated with the Python datetime object.
The most common place to put activate() is in middleware (as in this example from the documentation), since that ensures that it comes before any view processing. Alternatively, if using generic class-based views like you are, you could put it in dispatch().
Good morning. What I would like to achive is that when user posted sooner than 24h from now, I would like to have for example:
Posted: 4h ago, but when it's more than 24h, it would be nice to have: Posted: November 10.
First approach is doable by using: {{ post.date_posted|date:"F d, Y" }}, second one: {{ post.date_posted|timesince }}, but is there a way to "mix" them? Is is possible in Django?
I wrote my own time_utils.py in an app called custom_utils like below based on stack overflow posts. You can modify it accordingly.
import datetime
from django.utils import timezone
def prettydate(d):
if d is not None:
diff = timezone.now() - d
s = diff.seconds
if diff.days > 30 or diff.days < 0:
return d.strftime('Y-m-d H:i')
elif diff.days == 1:
return 'One day ago'
elif diff.days > 1:
return '{} days ago'.format(diff.days)
elif s <= 1:
return 'just now'
elif s < 60:
return '{} seconds ago'.format(s)
elif s < 120:
return 'one minute ago'
elif s < 3600:
return '{} minutes ago'.format(round(s/60))
elif s < 7200:
return 'one hour ago'
else:
return '{} hours ago'.format(round(s/3600))
else:
return None
then in the app models, I do:
from custom_utils.time_utils import prettydate
class ForumPost(models.Model):
# your other fields
published_date = models.DateTimeField(blank=True, null=True)
# your other fields
def pretty_published_date(self):
return prettydate(self.published_date)
reference: #Natural/Relative days in Python
Jose Segall's answer
EDIT: I faced a problem when querying a dataset using annotate, I was not able to use attributes such as the pretty_published_date. I had to use custom template tags. The following is how to implement prettydate function to custom template tags.
Create templatetags folder in one of the app which is in INSTALLED_APPS in settings file of the project. I put mine under forum folder.
Create a blank __init__.py file inside templatetags folder
Create custom_tags.py
Inside custom_tags.py, add the prettydate function
from django import template
import datetime
from django.utils import timezone
register = template.Library()
#register.filter
def prettydate(d):
if d is not None:
# the rest of the function
in template file,
{% load custom_tags %}
{{forumpost.max_activity|prettydate}}
Also make sure that you restart the Django development server. If the server does not restart, Django won't register the tags.
You can use template tags, by defining a new function formatted_date_text
{{ post.date_posted|date:"F d, Y"|formatted_date_text }}
def formatted_date_text(value):
# define your funtion
I'm trying to access a property/computed field that contains a dictionary within my template I'm not quite sure on how I should be looping over the values, and from a code standpoint is this the correct way to go... As you can see below, my view creates a filter queryset and my hope is that each row within that queryset will contain the dict from the computed field/property from the model. Any advice would be greatly appreciated. I'm having a hard time determining what would be the correct way to loop over the data in the template itself. The final product would look something like this...
<div id={{provider.id}}>{{provider.name}}</div>
<div class="dayofweek">Monday</div>
<div class="timesavailable">
<ul>
<li>8:00</li>
<li>8:30></li>
</div>
Model:
from project.lib.file import GetSchedule
class Provider(models.Model):
....
#property
def current_schedule(self):
sched = GetSchedule(self.id)
return sched.returnSchedule()
Helper Function:
class GetSchedule(object):
schedule = {}
def __init__(self, provider_id):
self.provider = provider_id
try:
self.schedule = GetSchedule.__buildSchedule(self,self.provider)
except ObjectDoesNotExist:
print "Init failed - Cannot find provider in db with id: %s" % self.provider
raise ValueError
def __buildSchedule(self, provider):
#default values for now - dict will contain data soon
pid = self.provider
sched = {'Monday':{'8:00','8:30','10:00'}, 'Tuesday':{'10:00','10:30','11:00'}, 'Wednesday':{'9:30','10:00','10:30'}}
val = {
'Schedule' : sched
}
return val
def returnSchedule(self):
return self.schedule
View:
prov_list = Provider.objects.filter(docspeciality__name=dft_spec)
context_dict = {
'DftSpec': dft_spec,
'DftZip': dft_zip,
'DftIns': dft_ins,
'DocList': prov_list}
return render_to_response('search/provider.html', context_dict, context_instance=RequestContext(request))
Every time I use Admin to list the entries of a model, the Admin count the rows in the table. Worse yet, it seems to be doing so even when you are filtering your query.
For instance if I want to show only the models whose id is 123, 456, 789 I can do:
/admin/myapp/mymodel/?id__in=123,456,789
But the queries ran (among others) are:
SELECT COUNT(*) FROM `myapp_mymodel` WHERE `myapp_mymodel`.`id` IN (123, 456, 789) # okay
SELECT COUNT(*) FROM `myapp_mymodel` # why???
Which is killing mysql+innodb. It seems that the problem is partially acknowledged in this ticket, but my issue seems more specific since it counts all the rows even if it is not supposed to.
Is there a way to disable that global rows count?
Note: I am using django 1.2.7.
Django 1.8 lets you disable this by setting show_full_result_count = False.
https://docs.djangoproject.com/en/stable/ref/contrib/admin/#django.contrib.admin.ModelAdmin.show_full_result_count
Okay, I think I found a solution. As Peter suggested, the best approach is to work on the count property and it can be done by overriding it with custom query set (as seen in this post) that specialises the count with an approximate equivalent:
from django.db import connections, models
from django.db.models.query import QuerySet
class ApproxCountQuerySet(QuerySet):
"""Counting all rows is very expensive on large Innodb tables. This
is a replacement for QuerySet that returns an approximation if count()
is called with no additional constraints. In all other cases it should
behave exactly as QuerySet.
Only works with MySQL. Behaves normally for all other engines.
"""
def count(self):
# Code from django/db/models/query.py
if self._result_cache is not None and not self._iter:
return len(self._result_cache)
is_mysql = 'mysql' in connections[self.db].client.executable_name.lower()
query = self.query
if (is_mysql and not query.where and
query.high_mark is None and
query.low_mark == 0 and
not query.select and
not query.group_by and
not query.having and
not query.distinct):
# If query has no constraints, we would be simply doing
# "SELECT COUNT(*) FROM foo". Monkey patch so the we
# get an approximation instead.
cursor = connections[self.db].cursor()
cursor.execute("SHOW TABLE STATUS LIKE %s",
(self.model._meta.db_table,))
return cursor.fetchall()[0][4]
else:
return self.query.get_count(using=self.db)
Then in the admin:
class MyAdmin(admin.ModelAdmin):
def queryset(self, request):
qs = super(MyAdmin, self).queryset(request)
return qs._clone(klass=ApproxCountQuerySet)
The approximate function could mess things up on page number 100000, but it is good enough for my case.
I found Nova's answer very helpful, but i use postgres. I modified it slightly to work for postgres with some slight alterations to handle table namespaces, and slightly different "detect postgres" logic.
Here's the pg version.
class ApproxCountPgQuerySet(models.query.QuerySet):
"""approximate unconstrained count(*) with reltuples from pg_class"""
def count(self):
if self._result_cache is not None and not self._iter:
return len(self._result_cache)
if hasattr(connections[self.db].client.connection, 'pg_version'):
query = self.query
if (not query.where and query.high_mark is None and query.low_mark == 0 and
not query.select and not query.group_by and not query.having and not query.distinct):
# If query has no constraints, we would be simply doing
# "SELECT COUNT(*) FROM foo". Monkey patch so the we get an approximation instead.
parts = [p.strip('"') for p in self.model._meta.db_table.split('.')]
cursor = connections[self.db].cursor()
if len(parts) == 1:
cursor.execute("select reltuples::bigint FROM pg_class WHERE relname = %s", parts)
else:
cursor.execute("select reltuples::bigint FROM pg_class c JOIN pg_namespace n on (c.relnamespace = n.oid) WHERE n.nspname = %s AND c.relname = %s", parts)
return cursor.fetchall()[0][0]
return self.query.get_count(using=self.db)
The Nova's solution (ApproxCountQuerySet) works great, however in newer versions of Django queryset method got replaced with get_queryset, so it now should be:
class MyAdmin(admin.ModelAdmin):
def get_queryset(self, request):
qs = super(MyAdmin, self).get_queryset(request)
return qs._clone(klass=ApproxCountQuerySet)
If this is a serious problem you may have to take Drastic Actions™.
Looking at the code for a 1.3.1 install, I see that the admin code is using the paginator returned by get_paginator(). The default paginator class appears to be in django/core/paginator.py. That class has a private value called _count which is set in Paginator._get_count() (line 120 in my copy). This in turn is used to set a property of the Paginator class called count. I think that _get_count() is your target. Now the stage is set.
You have a couple of options:
Directly modify the source. I do not recommend this, but since you seem to be stuck at 1.2.7 you may find that it is the most expedient. Remember to document this change! Future maintainers (including possibly yourself) will thank you for the heads up.
Monkeypatch the class. This is better than direct modification because a) if you don't like the change you just comment out the monkeypatch, and b) it is more likely to work with future versions of Django. I have a monkeypatch going back over 4 years because they still have not fixed a bug in the template variable _resolve_lookup() code that doesn't recognize callables at the top level of evaluation, only at lower levels. Although the patch (which wraps the method of a class) was written against 0.97-pre, it still works at 1.3.1.
I did not spend the time to figure out exactly what changes you would have to make for your problem, but it might be along the lines of adding a _approx_count member to appropriate classes class META and then testing to see if that attr exists. If it does and is None then you do the sql.count() and set it. You might also need to reset it if you are on (or near) the last page of the list. Contact me if you need a little more help on this; my email is in my profile.
It is possible to change the default paginator used by the admin class. Here's one that caches the result for a short period of time: https://gist.github.com/e4c5/6852723
I managed to create a custom paginator that shows the current page numbe, a next button and a show full count link. It allows for the use of the original paginator if needed.
The trick used is to take per_page + 1 elements from db in order to see if we have more elements and then provide a fake count.
Let's say that we want the the third page and the page has 25 elements => We want object_list[50:75]. When calling Paginator.count the queryset will be evaluated for object_list[50:76](note that we take 75+1 elements) and then return either the count as 76 if we got 25+1 elements from db or 50 + the number of elements received if we didn't received 26 elements.
TL;DR:
I've created a mixin for the ModelAdmin:
from django.core.paginator import Paginator
from django.utils.functional import cached_property
class FastCountPaginator(Paginator):
"""A faster paginator implementation than the Paginator. Paginator is slow
mainly because QuerySet.count() is expensive on large queries.
The idea is to use the requested page to generate a 'fake' count. In
order to see if the page is the final one it queries n+1 elements
from db then reports the count as page_number * per_page + received_elements.
"""
use_fast_pagination = True
def __init__(self, page_number, *args, **kwargs):
self.page_number = page_number
super(FastCountPaginator, self).__init__(*args, **kwargs)
#cached_property
def count(self):
# Populate the object list when count is called. As this is a cached property,
# it will be called only once per instance
return self.populate_object_list()
def page(self, page_number):
"""Return a Page object for the given 1-based page number."""
page_number = self.validate_number(page_number)
return self._get_page(self.object_list, page_number, self)
def populate_object_list(self):
# converts queryset object_list to a list and return the number of elements until there
# the trick is to get per_page elements + 1 in order to see if the next page exists.
bottom = self.page_number * self.per_page
# get one more object than needed to see if we should show next page
top = bottom + self.per_page + 1
object_list = list(self.object_list[bottom:top])
# not the last page
if len(object_list) == self.per_page + 1:
object_list = object_list[:-1]
else:
top = bottom + len(object_list)
self.object_list = object_list
return top
class ModelAdminFastPaginationMixin:
show_full_result_count = False # prevents root_queryset.count() call
def changelist_view(self, request, extra_context=None):
# strip count_all query parameter from the request before it is processed
# this allows all links to be generated like this parameter was not present and without raising errors
request.GET = request.GET.copy()
request.GET.paginator_count_all = request.GET.pop('count_all', False)
return super().changelist_view(request, extra_context)
def get_paginator(self, request, queryset, per_page, orphans=0, allow_empty_first_page=True):
# use the normal paginator if we want to count all the ads
if hasattr(request.GET, 'paginator_count_all') and request.GET.paginator_count_all:
return Paginator(queryset, per_page, orphans, allow_empty_first_page)
page = self._validate_page_number(request.GET.get('p', '0'))
return FastCountPaginator(page, queryset, per_page, orphans, allow_empty_first_page)
def _validate_page_number(self, number):
# taken from Paginator.validate_number and adjusted
try:
if isinstance(number, float) and not number.is_integer():
raise ValueError
number = int(number)
except (TypeError, ValueError):
return 0
if number < 1:
number = 0
return number
The pagination.html template:
{% if cl and cl.paginator and cl.paginator.use_fast_pagination %}
{# Fast paginator with only next button and show the total number of results#}
{% load admin_list %}
{% load i18n %}
{% load admin_templatetags %}
<p class="paginator">
{% if pagination_required %}
{% for i in page_range %}
{% if forloop.last %}
{% fast_paginator_number cl i 'Next' %}
{% else %}
{% fast_paginator_number cl i %}
{% endif %}
{% endfor %}
{% endif %}
{% show_count_all_link cl "showall" %}
</p>
{% else %}
{# use the default pagination template if we are not using the FastPaginator #}
{% include "admin/pagination.html" %}
{% endif %}
and templatetags used:
from django import template
from django.contrib.admin.views.main import PAGE_VAR
from django.utils.html import format_html
from django.utils.safestring import mark_safe
register = template.Library()
DOT = '.'
#register.simple_tag
def fast_paginator_number(cl, i, text_display=None):
"""Generate an individual page index link in a paginated list.
Allows to change the link text by setting text_display
"""
if i == DOT:
return '… '
elif i == cl.page_num:
return format_html('<span class="this-page">{}</span> ', i + 1)
else:
return format_html(
'<a href="{}"{}>{}</a> ',
cl.get_query_string({PAGE_VAR: i}),
mark_safe(' class="end"' if i == cl.paginator.num_pages - 1 else ''),
text_display if text_display else i + 1,
)
#register.simple_tag
def show_count_all_link(cl, css_class='', text_display='Show the total number of results'):
"""Generate a button that toggles between FastPaginator and the normal
Paginator."""
return format_html(
'<a href="{}"{}>{}</a> ',
cl.get_query_string({PAGE_VAR: cl.page_num, 'count_all': True}),
mark_safe(f' class="{css_class}"' if css_class else ''),
text_display,
)
You can use it this way:
class MyVeryLargeModelAdmin(ModelAdminFastPaginationMixin, admin.ModelAdmin):
# ...
Or an even simpler version that does not show the Next button and Show the total number of results :
from django.core.paginator import Paginator
from django.utils.functional import cached_property
class FastCountPaginator(Paginator):
"""A faster paginator implementation than the Paginator. Paginator is slow
mainly because QuerySet.count() is expensive on large queries.
The idea is to use the requested page to generate a 'fake' count. In
order to see if the page is the final one it queries n+1 elements
from db then reports the count as page_number * per_page + received_elements.
"""
use_fast_pagination = True
def __init__(self, page_number, *args, **kwargs):
self.page_number = page_number
super(FastCountPaginator, self).__init__(*args, **kwargs)
#cached_property
def count(self):
# Populate the object list when count is called. As this is a cached property,
# it will be called only once per instance
return self.populate_object_list()
def page(self, page_number):
"""Return a Page object for the given 1-based page number."""
page_number = self.validate_number(page_number)
return self._get_page(self.object_list, page_number, self)
def populate_object_list(self):
# converts queryset object_list to a list and return the number of elements until there
# the trick is to get per_page elements + 1 in order to see if the next page exists.
bottom = self.page_number * self.per_page
# get one more object than needed to see if we should show next page
top = bottom + self.per_page + 1
object_list = list(self.object_list[bottom:top])
# not the last page
if len(object_list) == self.per_page + 1:
object_list = object_list[:-1]
else:
top = bottom + len(object_list)
self.object_list = object_list
return top
class ModelAdminFastPaginationMixin:
show_full_result_count = False # prevents root_queryset.count() call
def get_paginator(self, request, queryset, per_page, orphans=0, allow_empty_first_page=True):
page = self._validate_page_number(request.GET.get('p', '0'))
return FastCountPaginator(page, queryset, per_page, orphans, allow_empty_first_page)
def _validate_page_number(self, number):
# taken from Paginator.validate_number and adjusted
try:
if isinstance(number, float) and not number.is_integer():
raise ValueError
number = int(number)
except (TypeError, ValueError):
return 0
if number < 1:
number = 0
return number
Good afternoon..i have a model with a class like this:
class Reportage:
def get_num(self):
end_date = self.date.end_date
start_date = self.date.start_date
duration = time.mktime(end_date.timetuple()) - time.mktime(start_date.timetuple())
delta_t = duration / 60
num = []
for t in range(0,duration,delta_t):
start = t + start_date
end = datetime.timedelta(0,t+delta_t) + start_date
n_num = self.get_num_in_interval(start,end)
num.append([t, n_num])
return num
I want to serialize with simplejson the array num [] in the views.py for passing in a second moment this array to a jquery script to plot it in a graph..
what's the code to serialize that array..?
I hope I was clear .. thanks in advance to all those who respond ..
Following #ninefingers' response. I think that your question is aimed on how to make that dumped json string available to a jQuery plugin.
# views.py
def my_view(request):
# do stuff
num = reportage_instance.get_num()
num_json = simplejson.dumps(num)
return render(request, 'template.html', {
'num_json': num_json,
})
In your template, you make available that json obj as a Javascript variable
# template.html
<html>
<body>
<script>
var NUM_JSON = {{num_json|safe}};
myScript.doSomething(NUM_JSON);
</script>
</body>
</html>
Now you can call regular JS with the NUM_JSON variable.
If you're looking to do this in a model, something like this would work:
# if this were a models.py file:
import simplejson
# other django imports:
class SomeModel(models.Model):
property = models.SomeField()...
def some_function(self):
num = []
# full num
simplejson.dumps(num)
That'll dump num to a string-representation of the json, which you can then return, or write to a file, and so on.
From a view you have a choice - but if your view is an ajax view returning some json for processing you might do this:
# views.py
# ...
def my_ajax_call(request, model_pk):
try:
mymodel = SomeModel.get(pk=model_pk)
except SomeModel.NotFoundException as e:
return HttpResonse("Unknown model", status=404)
else:
return HttpResponse(mymodel.some_function()) # returns simplejson.dumps(num)
This can then be used from a template in Javascript - the other answer shows you how you might approach that :)