In Django, How do you write the url pattern for a json file? - django

My website generates json files in saves them with the user specified name: test.json
I couldn't immediately access it, so I assumed that I would have to write a URL pattern and view to see this file.
I want something akin to this :
url(r'^(?P<Controller>).json$', views.loadjson, name='loadjson')

If you need to serve static JSON files then doing so through Django is not the best way - if that's the case use something like Nginx.
If you wan't Django to generate those files and/or for e.g. an authentication mechanism in front of it then ok for Django.
To your question:
# urls.py
url(r'^(?P<json_file>[\w]+).json$', views.loadjson, name='loadjson')
It seems like you forgotten about [\w]+ which I guess is the right pattern for your needs.
the view function will be called with a WSGIRequest instance and the json_file argument (from your url pattern), from there you should be able to do whatever you like with the file. It's a good idea to return in as an application/json content type as those are supposed to be JSON.
# views.py
from django.http import HttpResponse
def loadjson(request, json_file):
# open, generate, fetch the json file
# for e.g.:
json_content = read_file(json_file)
return HttpResponse(
json_content,
content_type='application/json',
status=200
)

Related

Persian text in url Django

I have some links that include Persian texts, such as:
http://sample.com/fields/طب%20نظامی
And in the view function I want to access to Persian part, so:
url = request.path_info
key = re.findall('/fields/(.+)', url)[0]
But I get the following error:
IndexError at /fields/
list index out of range
Actually, the problem is with the index zero because it can not see anything there! It should be noted that it is a Django project on IIS Server and I have successfully tested it with other servers and the local server. I think it has some thing related to IIS. Moreover I have tried to slugify the url without success. I can encode urls successfully, but I think it is not the actual answer to this question.
Based on the comments:
I checked the request.path too and the same problem. It contains:
/fields/
I implemented a sample django project in local server and here is my views:
def test(request):
t = request.path
return HttpResponse(t)
The results:
http://127.0.0.1:8000/تست/
/تست/
Without any problem.
Based on the #sytech comment, I have created a middlware.py in my app directory:
from django.core.handlers.wsgi import WSGIHandler
class SimpleMiddleware(WSGIHandler):
def __call__(self, environ, start_response):
print(environ['UNENCODED_URL'])
return super().__call__(environ, start_response)
and in settings.py:
MIDDLEWARE = [
...
'apps.middleware.SimpleMiddleware',
]
But I am getting the following error:
__call__() missing 1 required positional argument: 'start_response'
Assuming you don't have another problem in your rewrite configuration, on IIS, depending on your rewrite configuration, you may need to access this through the UNENCODED_URL variable which will contain the unencoded value.
This can be demonstrated in a simple WSGI middleware:
from django.core.handlers.wsgi import WSGIHandler
class MyHandler(WSGIHandler):
def __call__(self, environ, start_response):
print(environ['UNENCODED_URL'])
return super().__call__(environ, start_response)
You would see the unencoded URL and the path part that's in Persian would be passed %D8%B7%D8%A8%2520%D9%86%D8%B8%D8%A7%D9%85%DB%8C. Which you can then decode with urllib.parse.unquote
urllib.parse.unquote('%D8%B7%D8%A8%2520%D9%86%D8%B8%D8%A7%D9%85%DB%8C')
# طب%20نظامی
If you wanted, you could use a middleware to set this as an attribute on the request object or even override the request.path_info.
You must be using URL rewrite v7.1.1980 or higher for this to work.
You could also use the UNENCODED_URL directly in the rewrite rule, but that may result in headaches with routing.
I can encode urls successfully, but I think it is not the actual answer to this question.
Yeah, that is another option, but may result in other issues like this: IIS10 URL Rewrite 2.1 double encoding issue
You can do this by using python split() method
url = "http://sample.com/fields/طب%20نظامی"
url_key = url.split(sep="/", maxsplit=4)
url_key[-1]
output : 'طب%20نظامی'
in this url is splited by / which occurs 4 time in string so it will return a list like this
['http:', '', 'sample.com', 'fields', 'طب%20نظامی']
then extract result like this url_key[-1] from url_key
you can Split the URL by :
string = http://sample.com/fields/طب%20نظامی
last_part = string. Split("/")[-1]
print(last_part)
output :< طب%20نظامی >
slugify(last_part)
or
slugify(last_part, allow_unicode=True)
I guess This Will Help You :)

301 redirect router for Django project

I have a website building tool created in Django and I'd like to add easy user defined 301 redirects to it.
Webflow has a very easy to understand tool for 301 redirects. You add a path (not just a slug) and then define where that path should lead the user.
I'd like to do the same for the Django project I'm working on. I currently allow users to set a slug that redirects /<slug:redirect_slug>/ and they can set to go to any URL. But I'd like them to be able to add, for example, the path for an old blog post '/2018/04/12/my-favorite-thing/'
What's the best URL conf to use in Django to safely accept any path the user wants?
You can use the Path Converters that convert the path parameters into appropriate types, which also includes a converter for urls.
An example would be like the following:
path('api/<path:encoded_url>/', YourView.as_view()),
As per the docs:
Matches any non-empty string, including the path separator, '/'. This allows you to match against a complete URL path rather than just a segment of a URL path as with str.
In your view, you can get your URL like this:
encoded_url = self.kwargs.get('encoded_url')
Add a RerouteMiddleware which first checks if the request can be served by the existing URLs from the urls.py. If it cannot be served, check if the requested path is from the old -> new URLs mapping, if a match found redirect it to the new URL.
Sample piece of code to try it out.
try:
resolve(request.path_info)
except Resolver404:
# Check if the URL exists in your database/constants
# where you might have stored the old -> new URL mapping.
if request.path is valid:
new_url = # Retrieve the new URL
return redirect(new_url)
response = self.get_response(request)
return response

Django-Weasyprint image issue

As it says in the docs page, I defined a img tag in my html file as follows:
<img src='{% static 'image.png' %}'/>
This url exists in the server and I even made a different view with a http response and the image is displayed just fine. Here is the code for both views:
The pdf-weasyprint view:
def card_view(request):
template = loader.get_template('card.html')
context = {'sample': None
}
html = template.render(RequestContext(request, context))
response = HttpResponse(mimetype='application/pdf')
HTML(string=html).write_pdf(response)
return response
The html view:
def card_view2(request):
context = {'sample': None,
}
return render_to_response('card.html', context,
context_instance=RequestContext(request))
I thought the default url fetcher was supposed to find and render the image (it's a png - so no format issue should be involved)
Any ideas? Any help would be appreciated!!
What exactly is the issue? Do you get anything in the logs? (You may need to configure logging if your server does not log stderr.) What does the generated HTML look like?
I’d really need answers to the above to confirm, but my guess is that the image’s URL is relative, but with HTML(string=...) WeasyPrint has no idea of what is the base URL. Try something like this. (I’m not sure of the Django details.)
HTML(string=html, base_url=request.build_absolute_uri()).write_pdf(response)
This will make a real HTTP request on your app, which may deadlock on a single-threaded server. (I think the development server defaults to a single thread.)
To avoid that and the cost of going through the network, you may want to look into writing a custom "URL fetcher". It could be anywhere from specialized to just this one image, to a full Django equivalent of Flask-WeasyPrint.
Here it is a URL fetcher which reads (image) files locally, without performing an HTTP request:
from weasyprint import HTML, CSS, default_url_fetcher
import mimetypes
def weasyprint_local_fetcher(url):
if url.startswith('local://'):
filepath = url[8:]
with open(filepath, 'rb') as f:
file_data = f.read()
return {
'string': file_data,
'mime_type': mimetypes.guess_type(filepath)[0],
}
return default_url_fetcher(url)
In order to use it, use the local:// scheme in your URLs, for example:
<img src="local://myapp/static/images/image.svg" />
Then, pass the fetcher to the HTML __init__ method:
html = HTML(
string=html_string,
url_fetcher=weasyprint_local_fetcher,
)

Handling multiple sub-urls in Django's url.py file

In my djang urls pattern file, I'd like to hold a bunch of sub-url's but I don't want to make it ugly.
I have a file which handles all of my Ajax requests (it outputs different JSON files depending on the request it gets.
example (in my url.py):
in the form: (url, maps to)
(ajax/do_a, ajax.do_a)
ajax/do_b, ajax.do_b)
ajax/do_c, ajax.do_c)
ajax/do_d, ajax.do_d)
these are all sub-urls, eg.
mywebsite.com/ajax/do_a
mywebsite.com/ajax/do_b
etc.
Basically do_a,do_b,do_c,and do_d are all different request handlers sitting in the same same in the "ajax.py" file. I really don't want to be filling up my urls.py file with all of these urls for ajax requests. I was thinking of move this so that I only have
ajax/
in my url.py file and then somehow parse the ajax/ request url in my request handler (in the ajax.py file) so I can see what string came after the "ajax/". I'm not sure how to do this or if this would be a good idea to do this....Could anyone offer some advice? thanks :)
You could set up a dispatcher view for handling these. For example, in your urls.py:
(r'^ajax/do_(?P<do_token>(\d+))/$', 'do_dispatcher', {}, "di_dispatcher"),
Then, give yourself a view to handle it:
def do_a(request):
pass
def do_b(request):
pass
def do_c(request):
pass
DO_LOOKUP = {
'a' = do_a,
'b' = do_b,
'c' = do_c,
}
def do_dispatch(request, do_token):
do_func = DO_LOOKUP.get(do_token, None)
if do_func is None:
return HttpResponseNotFound("No do could be found for token '%s'!" % do_token)
else:
return do_func(request)

How to generate temporary URLs in Django

Wondering if there is a good way to generate temporary URLs that expire in X days. Would like to email out a URL that the recipient can click to access a part of the site that then is inaccessible via that URL after some time period. No idea how to do this, with Django, or Python, or otherwise.
If you don't expect to get a large response rate, then you should try to store all of the data in the URL itself. This way, you don't need to store anything in the database, and will have data storage proportional to the responses rather than the emails sent.
Updated: Let's say you had two strings that were unique for each user. You can pack them and unpack them with a protecting hash like this:
import hashlib, zlib
import cPickle as pickle
import urllib
my_secret = "michnorts"
def encode_data(data):
"""Turn `data` into a hash and an encoded string, suitable for use with `decode_data`."""
text = zlib.compress(pickle.dumps(data, 0)).encode('base64').replace('\n', '')
m = hashlib.md5(my_secret + text).hexdigest()[:12]
return m, text
def decode_data(hash, enc):
"""The inverse of `encode_data`."""
text = urllib.unquote(enc)
m = hashlib.md5(my_secret + text).hexdigest()[:12]
if m != hash:
raise Exception("Bad hash!")
data = pickle.loads(zlib.decompress(text.decode('base64')))
return data
hash, enc = encode_data(['Hello', 'Goodbye'])
print hash, enc
print decode_data(hash, enc)
This produces:
849e77ae1b3c eJzTyCkw5ApW90jNyclX5yow4koMVnfPz09JqkwFco25EvUAqXwJnA==
['Hello', 'Goodbye']
In your email, include a URL that has both the hash and enc values (properly url-quoted). In your view function, use those two values with decode_data to retrieve the original data.
The zlib.compress may not be that helpful, depending on your data, you can experiment to see what works best for you.
You could set this up with URLs like:
http://yoursite.com/temp/1a5h21j32
Your URLconf would look something like this:
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^temp/(?P<hash>\w+)/$', 'yoursite.views.tempurl'),
)
...where tempurl is a view handler that fetches the appropriate page based on the hash. Or, sends a 404 if the page is expired.
models
class TempUrl(models.Model):
url_hash = models.CharField("Url", blank=False, max_length=32, unique=True)
expires = models.DateTimeField("Expires")
views
def generate_url(request):
# do actions that result creating the object and mailing it
def load_url(request, hash):
url = get_object_or_404(TempUrl, url_hash=hash, expires__gte=datetime.now())
data = get_some_data_or_whatever()
return render_to_response('some_template.html', {'data':data},
context_instance=RequestContext(request))
urls
urlpatterns = patterns('', url(r'^temp/(?P<hash>\w+)/$', 'your.views.load_url', name="url"),)
//of course you need some imports and templates
It depends on what you want to do - one-shot things like account activation or allowing a file to be downloaded could be done with a view which looks up a hash, checks a timestamp and performs an action or provides a file.
More complex stuff such as providing arbitrary data would also require the model containing some reference to that data so that you can decide what to send back. Finally, allowing access to multiple pages would probably involve setting something in the user's session and then using that to determine what they can see, followed by a redirect.
If you could provide more detail about what you're trying to do and how well you know Django, I can make a more specific reply.
I think the solution lies within a combination of all the suggested solutions. I'd suggest using an expiring session so the link will expire within the time period you specify in the model. Combined with a redirect and middleware to check if a session attribute exists and the requested url requires it you can create somewhat secure parts of your site that can have nicer URLs that reference permanent parts of the site. I use this for demonstrating design/features for a limited time. This works to prevent forwarding... I don't do it but you could remove the temp url after first click so only the session attribute will provide access thus more effectively limiting to one user. I personally don't mind if the temp url gets forwarded knowing it will only last for a certain amount of time. Works well in a modified form for tracking invited visits as well.
It might be overkill, but you could use a uuidfield on your model and set up a Celerybeat task to change the uuid at any time interval you choose.
If celery is too much and it might be, you could just store the time the URL is first sent, use the timedelta function whenever it is sent thereafter, and if the elapsed time is greater than what you want just use a redirect. I think the second solution is very straightforward and it would extend easily. It would be a matter of having a model with the URL, time first sent, time most recently sent, a disabled flag, and a Delta that you find acceptable for the URL to live.
A temporary url can also be created by combining the ideas from #ned-batchelder's answer and #matt-howell's answer with Django's signing module.
The signing module provides a convenient way to encode data in the url, if necessary, and to check for link expiration. This way we don't need to touch the database or session/cache.
Here's a minimal example with an index page and a temp page:
The index page has a link to a temporary url, with the specified expiration. If you try to follow the link after expiration, you'll get a status 400 "Bad Request" (or you'll see the SuspiciousOperation error, if DEBUG is True).
urls.py
...
urlpatterns = [
path('', views.index, name='index'),
path('<str:signed_data>/', views.temp, name='temp'),
]
views.py
from django.core import signing
from django.core.exceptions import SuspiciousOperation
from django.http import HttpResponse
from django.urls import reverse
MAX_AGE_SECONDS = 20 # short expiration, for illustrative purposes
def generate_temp_url(data=None):
# signing.dumps() returns a "URL-safe, signed base64 compressed JSON string"
# with a timestamp
return reverse('temp', args=[signing.dumps(data)])
def index(request):
# just a convenient usage example
return HttpResponse(f'temporary link')
def temp(request, signed_data):
try:
# load data and check expiration
data = signing.loads(signed_data, max_age=MAX_AGE_SECONDS)
except signing.BadSignature:
# triggers an HttpResponseBadRequest (status 400) when DEBUG is False
raise SuspiciousOperation('invalid signature')
# success
return HttpResponse(f'Here\'s your data: {data}')
Some notes:
The responses in the example are very rudimentary, and only for illustrative purposes.
Raising a SuspiciousOperation is convenient, but you could e.g. return an HttpResponseNotFound (status 404) instead.
The generate_temp_url() returns a relative path. If you need an absolute url, you can do something like:
temp_url = request.build_absolute_uri(generate_temp_url())
If you're worried about leaking the signed data, have a look at alternatives such as Django's password reset implementation.