Dealing with versioning using URLPathVersioning in Django 2 - python

I've been having some troubles lately trying to set my REST API to use path versioning.
I have the code in my_app/urls.py with:
def ping():
return "pong"
API_PREFIX = r'^(?P<version>(v1))'
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(f'^{API_PREFIX}/ping/$', get_json(ping))) # assume that get_json returns the right thing
]
I added this lines to settings.py in the same directory:
REST_FRAMEWORK = {
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning'
'DEFAULT_VERSION': 'v1',
'ALLOWED_VERSIONS': ('v1',),
}
This doesn't work if I do a GET localhost:8000/v1/ping/. Is a f string able to include regex syntax or is the problem somewhere else? I tried fr'^{(API_PREFIX}}/ping/$' and that didn't work either.
Bonus question: In the ping function, how can I access and check the version number passed in the path (1 in that case, but will change over time)?

I found out the problem. This paragraph from the docs outlines the problem I was facing;
Using unnamed regular expression groups¶ As well as the named group
syntax, e.g. (?P[0-9]{4}), you can also use the shorter unnamed
group, e.g. ([0-9]{4}).
This usage isn’t particularly recommended as it makes it easier to
accidentally introduce errors between the intended meaning of a match
and the arguments of the view.
> In either case, using only one style within a given regex is
recommended. When both styles are mixed, any unnamed groups are
ignored and only named groups are passed to the view function.

Related

Can Python-Markdown support imageboard-style links?

I would like to add an additional syntax to Python-Markdown: if n is a positive integer, >>n should expand into n. (Double angled brackets (>>) is a conventional syntax for creating links in imageboard forums.)
By default, Python-Markdown expands >>n into nested blockquotes: <blockquote><blockquote>n</blockquote></blockquote>. Is there a way create links out of >>n, while preserving the rest of blockquote's default behavior? In other words, if x is a positive integer, >>x should expand into a link, but if x is not a positive integer, >>x should still expand into nested blockquotes.
I have read the relevant wiki article: Tutorial 1 Writing Extensions for Python Markdown. Based on what I learned in the wiki, I wrote a custom extension:
import markdown
import xml.etree.ElementTree as ET
from markdown.extensions import Extension
from markdown.inlinepatterns import Pattern
class ImageboardLinkPattern(Pattern):
def handleMatch(self, match):
number = match.group('number')
# Create link.
element = ET.Element('a', attrib={'href': f'#post-{number}'})
element.text = f'>>{number}'
return element
class ImageboardLinkExtension(Extension):
def extendMarkdown(self, md):
IMAGEBOARD_LINK_RE = '>>(?P<number>[1-9][0-9]*)'
imageboard_link = ImageboardLinkPattern(IMAGEBOARD_LINK_RE)
md.inlinePatterns['imageboard_link'] = imageboard_link
html = markdown.markdown('>>123',
extensions=[ImageboardLinkExtension()])
print(html)
However, >>123 still produces <blockquote><blockquote>123</blockquote></blockquote>. What is wrong with the implementation above?
The problem is that your new syntax conflicts with the preexisting blockquote syntax. Your extension would presumably work if it was ever called. However, due to the conflict, that never happens. Note that their are five types of processors. As documented:
Preprocessors alter the source before it is passed to the parser.
Block Processors work with blocks of text separated by blank lines.
Tree Processors modify the constructed ElementTree
Inline Processors are common tree processors for inline elements, such as *strong*.
Postprocessors munge of the output of the parser just before it is returned.
Of importance here is that the processors are run in that order. In other words, all block processors are run before any inline processors are run. Therefore, the blockquote block processor runs first on your input and removes the double angle bracket, wrapping the rest of the line in double blockquote tags. By the time your inline processor sees the document, your regex will no longer match and will therefore never be called.
That being said, an inline processor is the correct way to implement a link syntax. However, you would need to do one of two things to make it work.
Alter the syntax so that it does not clash with any preexisting syntax; or
Alter the blockquote behavior to avoid the conflict.
Personally, I would recommend option 1, but I understand you are trying to implement a preexisting syntax from another environment. So, if you want to explore option 2, then I would suggest perhaps making the blockquote syntax a little more strict. For example, while it is not required, the recommended syntax is to always insert a space after the angle bracket in a blockquote. It should be relatively simple to alter the BlockquoteProcessor to require the space, which would cause your syntax to no longer clash.
This is actually pretty simple. As you may note, the entire syntax is defined via a rather simple regex:
RE = re.compile(r'(^|\n)[ ]{0,3}>[ ]?(.*)')
You simply need to rewrite that so that 0 whitespace is no longer accepted (> rather than >[ ]?). First import and subclass the existing processor and then override the regex:
from markdown.blockprocessors import BlockquoteProcessor
class CustomBlockquoteProcessor(BlockquoteProcessor):
RE = re.compile(r'(^|\n)[ ]{0,3}> (.*)')
Finally, you just need to tell Markdown to use your custom class rather than the default. Add the following to the extendMarkdown method of your ImageboardLinkExtension class:
md.parser.blockprocessors.register(CustomBlockQuoteProcessor(md.parser), 'quote', 20)
Now the blockquote syntax will no longer clash with your link syntax and you will get an opportunity to have your code run on the text. Just be careful to remember to always include the now required space for any actual blockquotes.

Django domain + regex parameter not working on production machine

I currently have a django view with a fairly simple search function (takes user input, returns a list of objects). For usability, I'd like the option of passing search paramters via url like so:
www.example.com/search/mysearchstring
Where mysearchstring is the input to the search function. I'm using regex to validate any alphanumeric or underscore characters.
The problem I'm having is that while this works perfectly in my development environment, it breaks on the live machine.
Currently, I am using this exact same method (with different regex patterns) in other django views without any issues. This leads me to believe that either.
1) My regex is truly bad (more likely)
2) There is a difference in regex validators between environments (less likely)
The machine running this is using django 1.6 and python 2.7, which are slightly behind my development machine, but not significantly.
urls.py
SEARCH_REGEX = '(?P<pdom>\w*)?'
urlpatterns = patterns('',
....
url(r'^polls/search/' + SEARCH_REGEX, 'polls.views.search'),
...)
Which are passed to the view like this
views. py
def search(request, pdom):
...
When loading up the page, I get the following error:
ImproperlyConfigured: "^polls/search/(?P<pdom>\w*)?" is not a valid regular expression: nothing to repeat
I've been scratching my head over this one for a while. I've attempted to use a few different methods of encapsulation around the expression with no change in results. Would appreciate any insight!
I would change it to this:
SEARCH_REGEX = r'(?P<pdom>.+)$'
It's usually a good idea to use raw strings r'' for regular expressions in python.
The group will match the entire content of the search part of your url. I would handle query string validation in the view, instead of in the url regex. If someone tries to search polls/search/two+words, you should not return a 404, but instead a 400 status and a error message explaining that the search string was malformed.
Finally, you might want to follow the common convention for search urls. Which is to use a query parameter called q. So your url-pattern would be ^polls/search/$, and then you just handle the q in the view using something like this:
def search_page_view(request):
query_string = request.GET.get('q', '')

How to properly set variables in a latex template for Django

I want to create a pdf with latex through a django view. I created the view and set up a latex template and it works fine except for one thing. When I place a variable in the latex template I have to use spaces around the curly brackets like so:
\somevalue{ {{variable}} }
Otherwise django won't check that it is as a variable. The latex syntax checker already tells me "Unintendes whitespace around }?!". I can pass the variable into the template through my view and the pdf get created but then I have whitespaces around the inserted text.
Does anybody has an idea how to solve this?
Based on some google research, I'd recommend switching templating engines to Jinja, which is supported by Django and has configurable syntax.
Be warned, I haven't fully tested this.
Here's how your latex templates would look like:
\somevalue{((variable))}
The most important part is setting the variable_start_string and variable_end_string options:
>>> import jinja2
>>> env = jinja2.Environment(variable_start_string='((', variable_end_string='))')
>>> t = env.from_string("\somevalue{((variable))}")
>>> t.render(name='Attention!')
'\\somevalue{Attention!}'
Jinja's switching documentation outlines the (few) syntax differences. From the FAQ:
The default syntax of Jinja2 matches Django syntax in many ways. However this similarity doesn’t mean that you can use a Django template unmodified in Jinja2. For example filter arguments use a function call syntax rather than a colon to separate filter name and arguments. Additionally the extension interface in Jinja is fundamentally different from the Django one which means that your custom tags won’t work any longer.
Django 1.9 and 1.8 and maybe other versions have built-in support for Jinja.
I haven't found an example of configuring Jinja syntax in Django, and I can't test this at the moment but I believe you need to change the
TEMPLATES['OPTIONS'] dictionary as needed:
block_start_string='(#',
block_end_string='#)',
variable_start_string='((',
variable_end_string='))',
comment_start_string='((#',
comment_end_string='#))',
The solution I found is to remove extra spaces after template rendering:
template = select_template(self.get_template_names())
latex_content = template.render(context)
latex_content = re.sub(r'\{ ', '{', latex_content)
latex_content = re.sub(r' \}', '}', latex_content)
This has the benefit of not requiring extra template tags flooding the template. However this as the drawback to require template writers to be aware of this behavior and take it into account wherever curly braces are used.
In practice, it would probably be better to write a custom Template class that handles this.
Edit
Note that using this method, there is no reason to use a space as separator rather than any other character. In order to make it clearer for template writers/readers, you can use another character that would ring him a bell. For instance:
template.tex
\somevalue{§{{ variable }}§}
views.py
template = select_template(self.get_template_names())
latex_content = template.render(context)
latex_content = re.sub(r'\{§', '{', latex_content)
latex_content = re.sub(r'§\}', '}', latex_content)
Which makes it clear that if there is a space, it is intended. And I think it's already clear that § everywhere is not intended to be displayed.

i18n translation with a different domain in .py files

I'm a little confused on i18n translations in py files. I have a string where what needs to be translated is already in the plone domain in plone.pot so I want to specify the domain to be plone for that translation only. When I do the following, I get an error. And, this does get rendered in a page template so there's no need to call the translate function, is this correct?
raise ValueError(_(u'Some string', domain='plone'))
TypeError: __call__() got an unexpected keyword argument 'domain'
How should this be done? Thanks a lot!
You need to have two different message factories, i.e. where you do
from zope.i18nmessageid import MessageFactory
_ = MessageFactory('my.package')
you should be also instantiating the Plone one.
_p = MessageFactory('plone')
Then _p('Contributors') would give you the translated string.
Have a look here too.

Python: Need to replace a series of different substrings in HTML template with additional HTML or database results

Situation:
I am writing a basic templating system in Python/mod_python that reads in a main HTML template and replaces instances of ":value:" throughout the document with additional HTML or db results and then returns it as a view to the user.
I am not trying to replace all instances of 1 substring. Values can vary. There is a finite list of what's acceptable. It is not unlimited. The syntax for the values is [colon]value[colon]. Examples might be ":gallery: , :related: , :comments:". The replacement may be additional static HTML or a call to a function. The functions may vary as well.
Question:
What's the most efficient way to read in the main HTML file and replace the unknown combination of values with their appropriate replacement?
Thanks in advance for any thoughts/solutions,
c
There are dozens of templating options that already exist. Consider genshi, mako, jinja2, django templates, or more.
You'll find that you're reinventing the wheel with little/no benefit.
If you can't use an existing templating system for whatever reason, your problem seems best tackled with regular expressions:
import re
valre = re.compile(r':\w+:')
def dosub(correspvals, correspfuns, lastditch):
def f(value):
v = value.group()[1:-1]
if v in correspvals:
return correspvals[v]
if v in correspfuns:
return correspfuns[v]() # or whatever args you need
# what if a value has neither a corresponding value to
# substitute, NOR a function to call? Whatever...:
return lastditch(v)
return f
replacer = dosub(adict, another, somefun)
thehtml = valre.sub(replacer, thehtml)
Basically you'll need two dictionaries (one mapping values to corresponding values, another mapping values to corresponding functions to be called) and a function to be called as a last-ditch attempt for values that can't be found in either dictionary; the code above shows you how to put these things together (I'm using a closure, a class would of course do just as well) and how to apply them for the required replacement task.
This is probably a job for a templating engine and for Python there are a number of choices. In this stackoveflow question people have listed their favourites and some helpfully explain why: What is your single favorite Python templating engine?

Categories