Why doesn't Django template language allow something like a[ 0 ][ 1 ]? - python

When I was trying to access a tuple inside a list in the Django template format, I found out I couldn't access it with a[ 0 ][ 1 ], instead I had to use a.0.1.
Suppose that a is something like
a = [
( 'a', 'apple' ),
( 'b', 'bee' ),
]
Why doesn't Django template language support a[ 0 ][ 1 ]? In normal Python programming, a.0.1 would give you a syntax error.

The Django docs on the template API explain this nicely:
Dots have a special meaning in template rendering. A dot in a variable name signifies a lookup. Specifically, when the template system encounters a dot in a variable name, it tries the following lookups, in this order:
Dictionary lookup. Example: foo["bar"]
Attribute lookup. Example: foo.bar
List-index lookup. Example: foo[bar]
The template system uses the first lookup type that works. It's short-circuit logic. Here are a few examples:
>>> from django.template import Context, Template
>>> t = Template("My name is {{ person.first_name }}.")
>>> d = {"person": {"first_name": "Joe", "last_name": "Johnson"}}
>>> t.render(Context(d))
"My name is Joe."
>>> class PersonClass: pass
>>> p = PersonClass()
>>> p.first_name = "Ron"
>>> p.last_name = "Nasty"
>>> t.render(Context({"person": p}))
"My name is Ron."
>>> t = Template("The first stooge in the list is {{ stooges.0 }}.")
>>> c = Context({"stooges": ["Larry", "Curly", "Moe"]})
>>> t.render(c)
"The first stooge in the list is Larry."
Variable._resolve_lookup in django.templates.base appears to be the function responsible for this, and hasn't changed much since the oldest revision I can find

You can find some information about this in the django book:
The beginning of the chapter should explain why it works this way:
In the previous chapter, you may have noticed something peculiar in how we returned the text in our example views. Namely, the HTML was hard-coded directly in our Python code, like this:
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
Although this technique was convenient for the purpose of explaining how views work, it’s not a good idea to hard-code HTML directly in your views. Here’s why:
Any change to the design of the page requires a change to the Python code. The design of a site tends to change far more frequently than the underlying Python code, so it would be convenient if the design could change without needing to modify the Python code.
Writing Python code and designing HTML are two different disciplines, and most professional Web development environments split these responsibilities between separate people (or even separate departments). Designers and HTML/CSS coders shouldn’t be required to edit Python code to get their job done.
It’s most efficient if programmers can work on Python code and designers can work on templates at the same time, rather than one person waiting for the other to finish editing a single file that contains both Python and HTML.
For these reasons, it’s much cleaner and more maintainable to separate the design of the page from the Python code itself. We can do this with Django’s template system, which we discuss in this chapter.
...
Dot lookups can be summarized like this: when the template system encounters a dot in a variable name, it tries the following lookups, in this order:
Dictionary lookup (e.g., foo["bar"])
Attribute lookup (e.g., foo.bar) 1
Method call (e.g., foo.bar())
List-index lookup (e.g., foo[2])
The system uses the first lookup type that works. It’s short-circuit logic.

The reason I would say that the django template language doesnt do XYZ way of accessing context data is because generally at that point you are doing too much in the template side vs your view that renders it.
The design decision of their template engine seems lighter than maybe some others which give you more pythonic direct access to data. But ideally you would be formatting th context before passing it in.
You also have the ability to create your own template filters for doing more custom processing of data.
Specific to your question, accesing child members using dot notation is the django template way to try multiple approaches to resolving the member. It tries dictionary keys, attributes, etc. in a certain order. You just use dot notation for everything.

Related

Listing the title of created items in a choice field of another item in plone

I have a problem with my Plone item I cannot solve. 'Car' is supposed to create a list of all instances of 'Colour'.
All 'Colour' instances are in a given container. I cannot make it static because I want to add more 'Colour' instances in the future.
I tried selecting each item in my container and add it to my vocabularylist. I only need the id/title of my object, but I always end up with a giant stacktrace of failures.
In the end I want to choose a colour out of the given instances on creating a new 'Car' instance similar to a dropdown.
I have read the docs but cannot find a solution and this is my best idea.
I am also not a python programmer and this is my first plone project. I can add the complete failure list later if you need it.
I appreciate every bit of help. Thank you.
```colour= schema.Choice(
title=u"Colour",
description=u"Paintjob",
vocabulary=givecolour(),
required=False
)
#provider(IContextSourceBinder)
def givecolour():
colourlist = self.context.portal_catalog(path={"query" : "/madb-entw/it/colourcontainer", "depth" : 1})
list = []
i = 0
for colour in colourlist:
list.append(
SimpleVocabulary.createTerm(
colourlist.[i].getObject().id
)
)
i += 1
return SimpleVocabulary(list)```
Please always add your traces, so that we can help you better.
There is also the official community.plone.org forum, where are more people can help you.
I recommend you to use the plone.api to find your objects, this is a bit easier and well doumented.
something like this:
from plone import api
color_brains = api.content.find(context=api.content.get(path='/madb-entw/it/colourcontainer'), depth=1, portal_type='Color')
# no need to do getOject() here, get the id/title directly from the catalog brain
colors = [(color.id, color.Title) for color in color_brains]
One note to your query:
colourlist = self.context.portal_catalog(path={"query" :
"/madb-entw/it/colourcontainer", "depth" : 1})
Path has to be absolute, which means it includes the Plone site id and this can be different in another Plone site.
So an absolute path is not a good idea here, better get the portal object and traverse your path relative from there.
If madb-entw is your Plone site id:
portal.restrictedTraverse('it/colourcontainer')
or better as above, use plone.api.content.get(path='/it/colourcontainer')
Which is cleaner and easier.

How can I safely override the django.contrib.admin.utils quote() method?

It appears that the quote() and unquote() methods inside django.contrib.admin.utils do not effectively handle underscores in primary keys. Specifically, I have some string-type primary keys that look like cus_C2xVQnht and when I use the django admin interface to edit them via the small pencil icon, the popup window will display an error like Customer with ID "cusÂxVQnht" doesn't exist. Perhaps it was deleted? (it is converting the C2 to the codepoint 00C2, aka Â. This is true for other valid codepoints as well (00C7, 00C6, 001B, etc)
If I manually go to the customers model and find the ID, I can pull it up and edit it just fine, but it seems the URL encoding doesn't work right when the primary key has an underscore in it.
After quite a lot of digging I managed to find these two functions buried deep inside django.contrib.admin.utils:
def quote(s):
"""
Ensure that primary key values do not confuse the admin URLs by escaping
any '/', '_' and ':' and similarly problematic characters.
Similar to urllib.parse.quote(), except that the quoting is slightly
different so that it doesn't get automatically unquoted by the Web browser.
"""
if not isinstance(s, str):
return s
res = list(s)
for i in range(len(res)):
c = res[i]
if c in """:/_#?;#&=+$,"[]<>%\n\\""":
res[i] = '_%02X' % ord(c)
return ''.join(res)
def unquote(s):
"""Undo the effects of quote(). Based heavily on urllib.parse.unquote()."""
mychr = chr
myatoi = int
list = s.split('_')
res = [list[0]]
myappend = res.append
del list[0]
for item in list:
if item[1:2]:
try:
myappend(mychr(myatoi(item[:2], 16)) + item[2:])
except ValueError:
myappend('_' + item)
else:
myappend('_' + item)
return "".join(res)
They appear to be called somewhere in the admin template rendering process, but I couldn't figure out where/how often/all the locations, so I decided to do a quick monkey patch to decide if it was worth pursuing as a solution: I changed all the underscores in quote() and unquote() except for the one in the list of problem characters in quote to dots...for example:
'_%02X' in quote()becomes '.%02X'
split('_') in unquote() becomes split('.')
myappend('_' + item) in unquote() becomes myappend('.' + item)
Upon doing this, the admin works correctly and it appears that the links attached to the edit icons on related fields are to the correct model instances, so I can edit them by clicking the pencil icons and don't get the error message noted above.
All that said, I can't seem to find a way to safely override these two methods. I really would rather not change the primary keys to eliminate the underscores because there are a lot of linked models in my database and it just seems like it will become a huge pain. This fix seems much easier and more reliable, and given that it worked properly on previous versions of Django I don't see how it's a bad idea to implement.
So, how can I override those methods? Or, as a related question, is there something I can do in the __str__ methods of my models to alleviate this problem? I'd do that much sooner than start writing custom classes that override Django admin internals. If there is no other solution, I would need some help in properly restructuring my database to adjust the primary keys, but I can say that these keys work perfectly on the "old" site I'm working on, which runs Django 1.11.6 and Python 2.7.9 (vs the current Django 2.1.1 and Python 3.6.5)
Please let me know if I can provide any more info. Thank you!!
This is fixed in django 2.2. See https://github.com/django/django/commit/e4df8e6dc021fa472fa77f9b835db74810184748

How does the get_list tastypie function work?

I'm trying to use the get_list tastypie function but I can't make it work. I've looked for documentation about that but I can't find it.
Whatever, I've a list of item ids and an ItemResource. I'm trying to return a list of serialized objects.
So I just want to do something like that :
item_resource = ItemResource()
item_ids = my_item_id_list
return item_resource.get_list(request, id=item_ids)
But of course it's not working.
What would be the correct syntax to do that ?
Thx !
Unless your ItemResource accepts filters (more here), you have to copy-paste all the stuff from here, lines #1306 - #1313.
The point is that get_list results get filtered only by obj_get_list (initial filters), and apply_filters (request-specific filters) so you have to skip directly to the serialization part (you can include the pagination part, if needed).
This is one of the cases where django-restframework appears to be better than django-tastypie - it refactores serialization out into a separate class, avoiding the code duplication.

webapp2: regex in routing

I have such a problem: I want one method of the class to handle different URIs (for URI "/solution/add" and "solution/edit"). So I wrote such routing:
app = webapp2.WSGIApplication([webapp2.Route(r'/solutions/(add|edit)', handler='solution.SolutionPage:add_edit_solution'), ], debug=True)
And webapp2 gives 404 error. Could you please suggest the solution of this problem?
Ofcourse I can write different routes for every URI, but it's not so interesting.)
As the webapp2 docs indicate, you have to put the regex in angle brackets, with a colon separating name and expression. The name is optional, but
everything outside of <> is not interpreted as a regular expression to be matched
So something like this: '/blog/<:\d{4}>/<:\d{2}>' Or in your case, this:
webapp2.Route(r'/solutions/<:(add|edit)>',
handler='solution.SolutionPage:add_edit_solution')
If I can add something.
For my own purpose, I've try to create a handler which perform a little bit similar operation, but the point is that I've used self.request.host or self.request.route instead of arguments.
Doing this, and parsing the result with a switch case or if/elif/else loop, allow me to create a class named URIHandler which is use to route any kind of request to the correct ressources (even 404/500/yyy error pages) dynamicly whithout having to rewrite or add route for each new ressources.
So I'll be interested in comparing the two method to bench them a little bit.

How to prevent auto escape in Django templates?

In the docs it says:
The only exceptions are variables that are already marked as “safe” from escaping, either by the code that populated the variable, or because it has had the safe or escape filters applied."
How does the "populated the variable" part work ? I'm actually looking for a way to declare a template tag as safe in the view. I somehow think it's not a good idea to let a designer decide. My co-worker will just add it whenever she 'thinks' it's a good idea.
https://docs.djangoproject.com/en/dev/ref/templates/builtins/?from=olddocs
Django has a subclass of strings called safe strings (specifically SafeUnicode or SafeString), which can be created using django.utils.safestring.mark_safe. When the template engine comes across a safe string it doesn't perform HTML escaping on it:
>>> from django.utils.safestring import mark_safe
>>> from django.template import Template, Context
>>> Template("{{ name }}").render(Context({'name': mark_safe('<b>Brad</b>')}))
u"<b>Brad</b>"
If you're writing your own template tag, you need to implement render() which will return a string that will be treated as safe, meaning you have to handle any escaping necessary yourself. However if you're writing a template filter, you can set the attribute is_safe = True on the filter to avoid auto escaping of the returned value, e.g.
#register.filter
def myfilter(value):
return value
myfilter.is_safe = True
See https://docs.djangoproject.com/en/4.0/howto/custom-template-tags/#filters-and-auto-escaping for more details.
You could call django.utils.safestring.mark_safe and pass you variable
...
return direct_to_template('my-template.html', {'safe_var': mark_safe('<script>alert("");</script>')})
In template it will be printed without escaping (alert will popup). Though auto-escape is really a great feature that will save you from some bad things.

Categories

Resources