I came across this tutorial:
http://thomas.broxrost.com/2008/04/08/django-on-google-app-engine/
Fantastic!
Everything worked.
I just did not fully understand the code below because in comparison to Django it seems different:
views.py:
def main(request):
visitor = Visitor()
visitor.ip = request.META["REMOTE_ADDR"]
visitor.put()
result = ""
visitors = Visitor.all()
visitors.order("-added_on")
for visitor in visitors.fetch(limit=40):
result += visitor.ip + u" visited on " + unicode(visitor.added_on) + u""
return HttpResponse(result)
#model.py:
from google.appengine.ext import db
class Visitor(db.Model):
ip = db.StringProperty()
added_on = db.DateTimeProperty(auto_now_add=True)
What exactly is Visitor() ? A tuple a list?
And what does visitor.ip , visitor.put(), visitors.fetch() do exactly?
I believe:
visitor.ip saves the request.META["REMOTE_ADDR"] in the db field.
visitor.put() saves it.
visitors.fetch(limit = 40) extracts it from the db.
What I was trying to do is a tenplate that lists every IP below the next one.
I believed:
<p><ol><Li> {{ result }} </li></ol></p>
Would do the trick.
But it didn't.
Thanks !
Visitor is a class, and each field in it represents a column in your database.
When you do visitor = Visitor() you're essentially creating a new row in your database. Calling visitor.put() is what actually commits it into the database. Visitors.all() returns a all the rows in the db (it's either a list, tuple, or dictionary), so then visitors.fetch() is just an operation on that.
The reason your template isn't working is because your function in views.py isn't specifying any template. This is taken from the Django tutorial: http://docs.djangoproject.com/en/1.0/intro/tutorial03/
from django.template import Context, loader
from mysite.polls.models import Poll
from django.http import HttpResponse
def index(request):
latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
t = loader.get_template('polls/index.html')
c = Context({
'latest_poll_list': latest_poll_list,
})
return HttpResponse(t.render(c))
The parameter for Context() is a dictionary. The string on the left is what the name of the variable will be within the template, and the right side is what actual variable that it corresponds to. In your example you can use {'mylist': result}, and in your template you could use {{ mylist }} instead of {{ result }}
You also need to make sure to set a template directory in settings.py which the template (in the above example) is polls/index.html within that template dir.
Without knowing anything about the app engine, I'd say this: Visitor() returns an instance of the Visitor class. The step that follows (visitor.ip = request.META["REMOTE_ADDR"]) sets an attribute of the instance created in the first line.
Related
i'm trying to make a custom plotly-graphic on a wagtail homepage.
I got this far. I'm overriding the wagtail Page-model by altering the context returned to the template. Am i doing this the right way, is this possible in models.py ?
Thnx in advanced.
from django.db import models
from wagtail.models import Page
from wagtail.fields import RichTextField
from wagtail.admin.panels import FieldPanel
import psycopg2
from psycopg2 import sql
import pandas as pd
import plotly.graph_objs as go
from plotly.offline import plot
class CasPage(Page):
body = RichTextField(blank=True)
content_panels = Page.content_panels + [
FieldPanel('body'),
]
def get_connection(self):
try:
return psycopg2.connect(
database="xxxx",
user="xxxx",
password="xxxx",
host="xxxxxxxxxxxxx",
port=xxxxx,
)
except:
return False
conn = get_connection()
cursor = conn.cursor()
strquery = (f'''SELECT t.datum, t.grwaarde - LAG(t.grwaarde,1) OVER (ORDER BY datum) AS
gebruiktgas
FROM XXX
''')
data = pd.read_sql(strquery, conn)
fig1 = go.Figure(
data = data,
layout=go.Layout(
title="Gas-verbruik",
yaxis_title="aantal M3")
)
output = plotly.plot(fig1, output_type='div', include_plotlyjs=False)
# https://stackoverflow.com/questions/32626815/wagtail-views-extra-context
def get_context(self, request):
context = super(CasPage, self).get_context(request)
context['output'] = output
return context
Kind of the right track. You should move all the plot code into its own method though. At the moment, it runs the plot code when the site initialises then stays stored in memory.
There's three usual ways to get the plot to the rendered page then.
As you've done with context
As a property or method of the page class
As a template tag called from the template
The first two have more or less the same effect, except the 2nd makes the property available anywhere, not just the template. The context method runs before the page starts rendering, the other two happen during that process. I guess the only real difference there is that if you're using template caching, the context will always run each time the page is loaded, the other two only run when the cache is invalid, or if the code is escaped out of the cache (for fragment caching).
To call the plot as a property of your page class, you'd just pull out the code into a def with the #property decorator:
class CasPage(Page):
....
#property
def plot(self):
try:
conn = psycopg2.connect(
database="xxxx",
user="xxxx",
password="xxxx",
host="xxxxxxxxxxxxx",
port=xxxxx,
)
cursor = conn.cursor()
strquery = (f'''SELECT t.datum, t.grwaarde - LAG(t.grwaarde,1) OVER (ORDER BY datum) AS
gebruiktgas FROM XXX''')
data = pd.read_sql(strquery, conn)
fig1 = go.Figure(
data = data,
layout=go.Layout(
title="Gas-verbruik",
yaxis_title="aantal M3")
)
return plotly.plot(fig1, output_type='div', include_plotlyjs=False)
except Exception as e:
print(f"{type(e).__name__} at line {e.__traceback__.tb_lineno} of {__file__}: {e}")
return None
^ I haven't tried this code ... it should work as is, but no guarantees I didn't make a typo ;)
Now you can access your plot with {{ self.plot }} in the template.
If you want to stick with context, then you'd stay with the def above but just amend your output line to
context['output'] = self.plot
Template tags are more useful when they're being used in StructBlocks and not part of a page class like this, or where you have code that you want to re-use in multiple templates.
Then you'd move all that plot code into a template tag file, register it and call it in the template with {% plot %}. Wagtail template tags work the same as Django: https://docs.djangoproject.com/en/4.1/howto/custom-template-tags/
Is the plot data outside of the site database? If not, you could probably get the data via the ORM if it was defined as a model. If so, it's probably worth writing a view (or stored procedure if you want to pass parameters) on the db server and calling that rather than hard coding the SQL into your python.
The other consideration is the page load time - if the dataset is big, this could take a while and prevent the page from loading. You'd probably want a front-end solution in that case.
I try to render a Django CMS Placeholder from a page to a variable, to return the rendered code as JSON.
So what I do is:
from cms.models.placeholdermodel import Placeholder
from cms.models.pagemodel import Page
def render(self, page_id, placeholder_slot, request):
page = Page.objects.get(id=page_id)
placeholder = page.placeholders.get(slot=placeholder_slot)
Now I want to render the placeholder to a variable. Which function do I have to call in which way to get this?
The Placeholder can be called manually and rendered like that, a few examples:
from cms import models
from django import template
def render(request):
placeholder = models.Placeholder.objects.get_or_create(slot='some_slot')
context = template.RequestContext(request)
return placeholder.render(request, width=None)
Or for json/javascript/etc. where you don't want full html but just the value:
def render_basic(request):
placeholder = models.Placeholder.objects.get_or_create(slot='some_slot')
context = template.RequestContext(request)
return placeholder.render(request, width=None, editable=False)
It's also possible (and even easier) to use the StaticPlaceholder:
from cms import models
def render(request):
placeholder = models.StaticPlaceholder.objects.get_or_create(name='name of the placeholder')
return placeholder.code
You can render it using Placeholder.render. Note that the context must contain a valid HttpRequest object under the 'request' key. width may be None. See the {% render_placeholder %} implementation for an example.
I have a search page that takes a variety of parameters. I want to create a new URL by just altering one parameter in the query. Is there an easy way to do this - something like:
# example request url
http://example.com/search?q=foo&option=bar&option2=baz&change=before
# ideal template code
{% url_with change 'after' %}
# resulting url
http://example.com/search?q=foo&option=bar&option2=baz&change=after
So this would take the request url, alter one query parameter and then return the new url. Similar to what can be achieved in Perl's Catalyst using $c->uri_with({change => 'after'}).
Or is there a better way?
[UPDATED: removed references to pagination]
I did this simple tag which doesn't require any extra libraries:
#register.simple_tag
def url_replace(request, field, value):
dict_ = request.GET.copy()
dict_[field] = value
return dict_.urlencode()
Use as:
<a href="?{% url_replace request 'param' value %}">
It wil add 'param' to your url GET string if it's not there, or replace it with the new value if it's already there.
You also need the RequestContext request instance to be provided to your template from your view. More info here:
http://lincolnloop.com/blog/2008/may/10/getting-requestcontext-your-templates/
So, write a template tag around this:
from urlparse import urlparse, urlunparse
from django.http import QueryDict
def replace_query_param(url, attr, val):
(scheme, netloc, path, params, query, fragment) = urlparse(url)
query_dict = QueryDict(query).copy()
query_dict[attr] = val
query = query_dict.urlencode()
return urlunparse((scheme, netloc, path, params, query, fragment))
For a more comprehensive solution, use Zachary Voase's URLObject 2, which is very nicely done.
Note:
The urlparse module is renamed to urllib.parse in Python 3.
I improved mpaf's solution, to get request directly from tag.
#register.simple_tag(takes_context = True)
def url_replace(context, field, value):
dict_ = context['request'].GET.copy()
dict_[field] = value
return dict_.urlencode()
This worked pretty well for me. Allows you to set any number of parameters in the URL. Works nice for a pager, while keeping the rest of the query string.
from django import template
from urlobject import URLObject
register = template.Library()
#register.simple_tag(takes_context=True)
def url_set_param(context, **kwargs):
url = URLObject(context.request.get_full_path())
path = url.path
query = url.query
for k, v in kwargs.items():
query = query.set_param(k, v)
return '{}?{}'.format(path, query)
Then in the template:
<a href="{% url_set_param page=last %}">
There are a number of template tags for modifying the query string djangosnippets.org:
http://djangosnippets.org/snippets/553/
http://djangosnippets.org/snippets/826/
http://djangosnippets.org/snippets/1243/
I would say those are the most promising looking. One point in all of them is that you must be using django.core.context_processors.request in your TEMPLATE_CONTEXT_PROCESSORS.
You can try https://github.com/dcramer/django-paging
In addition to the snippets mentioned by Mark Lavin, Here's a list of other implementations I could find for a Django template tag which modifies the current HTTP GET query string.
On djangosnippets.org:
#2237 Manipulate URL query strings using context variables using a template tag by JHsaunders
#2332 Querystring Builder - create urls with GET params by jibberia
my favorite: #2413 Yet another query string template tag by atms
#2428 Add GET parameters from current request by naktinis
On PyPI:
django-spurl by Jamie Matthews
django-urltags by Calloway Project/Corey Oordt
the add_query_param filter in django-rest-framework by Tom Christie
On GitHub:
update_querystring by David Gouldin
I have two views:
def importContent(request):
d = get_some_data()
t = get_template('import.html')
c = Context({'entries' : d.entries })
return HttpResponse(t.render(c))
def doImport(request):
return HttpResponse("hey")
Here is import.html:
{% for entry in entries %}
{{ entry.title}} <br>
{% endfor %}
soo
User open importContent() view and press the link, which opens th doImport() view. How can I pass d-variable from importContent() view to doImport() view?
I can think of a couple of ways to approach this.
The first requires that you have sessions enabled. In this mechanism the first view will store the variable in the user's session and the second will retrieve it. For e.g.
def importContent(request):
d = get_some_data()
t = get_template('import.html')
c = Context({'entries' : d.entries })
request.session['entries'] = d
return HttpResponse(t.render(c))
def doImport(request):
if 'entries' in request.session:
d = request.session['entries']
else:
d = # Perform a look up or show a message etc.
return HttpResponse("hey")
The session can be substituted with a custom cache too.
The second is to make the second explicitly look up the data. This is easier if the data is limited and doesn't require any extensive computation.
If you want to pass all entries back to doImport, it won't be easy. The way to pass parameter in a request is to put them in the url, use a post request or use session but this requires more work.
Using URL is not really convenient because there will be a lot of parameters on that url.
Using a post is sort of weird and not suited to a html link.
Using a session requires authentication and use of users.
Can't you just call:
d = get_some_data()
in doImport again ?
What is the best way to pass request information to a model class method?
I'm wondering whether I have my logic in the wrong place. Maybe I need to move it out of my model.
I want to be able to pass in POST variables or a form to filter the model by country or institution.
I can't do that from the template, but the question is whether I should do that from within the model or controller somehow.
My Model:
class AccountExtra(User):
def myPendingPaymentSchedules(self, status=1, **args):
if self.is_staff: # status = approved by MFI
schedules = PaymentSchedule.objects.select_related(depth=3).filter(active=True)
if country:
schedules = schedules.filter(country=country)
if institution:
schedules = schedules.filter(institution=institution)
return schedules
My Controller:
myAccount = get_object_or_404(AccountExtra, id=request.user.id)
My Template
{% for sample in myAccount.myPendingPaymentSchedules %} # Can't pass parameters for country, etc
Yes, I'd say your logic is in the wrong place. I don't know where the values are coming from that you're trying to pass into myPendingPaymentSchedules, but it seems like it should be done in the view rather than the template. Then you can pass the resulting schedules directly into the template context.
(By the way, your naming scheme is not very Pythonic: I'd use my_account and my_pending_payment_schedules - see PEP8.
Thanks for the feedback. I've done a little bit of research about how to access business login from within templates and I thought I'd provide an update in case others find this question in the search results:
There are two cases in which the parameters for the method need to be passed:
Case #1) Passing paramaters for a single value
If we only have a single account, we can simply pass them to the model through a single call in the controller, and pass the result to the template as a single context variable.
Model
class AccountExtra(models.Model):
..
def my_pending_payment_schedules(self, status=1, country=None, institution=None)
if self.is_staff:
schedules = payment_schedule.objects.filter(active=True)
if country:
schedules = schedules.filter(product__country=country)
if institution:
schedules = schedules.filter(product__institution=institution)
return schedules
Controller
my_account = get_object_or_404(AccountExtra, id=request.user.id)
form = staff_approval_form(request.POST)
if form.is_valid():
cd = form.cleaned_data
pending_schedules = my_account.my_pending_payment_schedules(
country=cd.get('country', None),
institution=cd.get('institution', None)
)
c = RequestContext( request, {
'form': form,
'pending_schedules': pending_schedules,
})
return render_to_response(
'my/approvals/approvals_index.html',
context_instance=RequestContext(request, c)
)
Template
{% for sample in pending_schedules %}
Case #2: Passing parameters for multiple values
If however, we are trying to iterate through multiple users's pending schedules who each require different parameters, we can't use a simple 'pending_schedules' variable.
We have to either turn that variable into a dictionary to store the results of multiple users.
A collegue of mine developed a template tag that allows you to access the dictionary by key, as you iterate through the loop.
Templatetags
#register.filter
def hash(obj, key):
"""return hash lookup of key in object
If the key can be hard-coded into the template, then the normal dot-notation
is sufficient (obj.key). But if the key is referenced by a name in the
template context then this hash filter becomes useful.
Template usage: obj|hash:key
"""
return obj[key]
Template:
for user in users:
for sample in pending_schedules|hash:user.id
Do something
endfor
endfor