language by domain in Django - django

Is there any braindead simple way to select the language specific version of a page on a Django website based on the domain? (the URLs are and will be the same for both languages, except the language code, so I want to have mysite.com/teachers/manage same as mysite.com/en/teachers/manage (done with 'en' default) and mesito.es/teachers to be same as mesito.es/es/teachers - and no, the urls themselves like 'teachers/manage' will never be translated, so I don't want to configure 2 sets of urls for both languages).
Note: All other solutions I've found imply that I also want to translate the URLs, but I know I will never want to translate the URLs and manae 2 sets of them for this particular website.
Note 2 (forgot to add): Most of the website is actually django-cms based, so django-cms specific solutions would be helpful to...
OK, I got what I wanted with this ugly hack through a redirecting view (but still, isn't there a right AND simple way to do this?...):
## mysite.urls:
urlpatterns = patterns('',
(r'^$', 'mysite.views.language_router')
## mysite.views:
def language_router(request):
if request.META['HTTP_HOST'].find('myspanishdomain.com') != -1 \
and request.META['PATH_INFO'] == '/' :
return HttpResponseRedirect('http://www.myspanishdomain.com/es/')
return cms.views.details(request, '')
(site at myenglishdomain.com has the default language english)

Why don't you write your onw tiny middleware class that you add after the LocaleMiddleware, that simply adds the language, if no language has been identified yet based on the rules that you have explained here in that thread.

You may want to have a look at Transurlvania as it allows you to map a domain to a language:
MULTILANG_LANGUAGE_DOMAINS = {
'en': ('www.example-en.com', 'English Site'),
'fr': ('www.example-fr.com', 'French Site')
}

You could use a separate site for each domain, and use a view mixin or a middleware to check the site id and react appropriately.

Related

Best practice of Flask route for app.route("/index") or ("/index.html")

Is there a convention to define app routes in Flask to add suffix ".html"? e.g,
#app.route("/index", methods=['GET'])
def index_func(){
return render_template("index.html")
}
#app.route("/index.html", methods=['GET'])
def index_func(){
return render_template("index.html")
}
Which would be the best practice? Thanks.
The best practice in this case is to use '/' for index and avoid using '/index' and '/index.html' altogether. An index page is another name for a home page which is a synonym for the root page of a site which is '/'. All other routes are necessarily prefixed with it so '/index', '/home', etc are redundant.
As for adding file extensions to routes, this is not only unnecessary but could be miss leading in the future if you want to serve different content types from that route using content-negotiation. For example, what if you wanted to serve a JSON version of the same page for mobile and SPA clients? I'm not aware of any sources that state omitting the file extension is a best practice but it's implicit in that every route example in Flask's documentation omits a file extension. For example, the Rendering Templates example, which is serving an HTML page, does not include a .html suffix in the route.
No. 1 The way you defined a function in python is wrong.
def func(){
}
wont work. Instead you would define a function like this:
def func():
print("Hi")
Then, coming to the route declaration you would use
#app.route("/index")
def index_func():
return render_template("index.html")
Also note when you just want to recieve GET methods you dont have to specify methods=['GET']

Django syndication framework: prevent appending SITE_ID to the links

According to the documentation here: https://djangobook.com/syndication-feed-framework/
If link doesn’t return the domain, the syndication framework will
insert the domain of the current site, according to your SITE_ID
setting
However, I'm trying to generate a feed of magnet: links. The framework doesn't recognize this and attempts to append the SITE_ID, such that the links end up like this (on localhost):
<link>http://localhost:8000magnet:?xt=...</link>
Is there a way to bypass this?
Here's a way to do it with monkey patching, much cleaner.
I like to create a separate folder "django_patches" for these kinds of things:
myproject/django_patches/__init__.py
from django.contrib.syndication import views
from django.contrib.syndication.views import add_domain
def add_domain_if_we_should(domain, url, secure=False):
if url.startswith('magnet:'):
return url
else:
return add_domain(domain, url, secure=False)
views.add_domain = add_domain_if_we_should
Next, add it to your INSTALLED_APPS so that you can patch the function.
settings.py
INSTALLED_APPS = [
'django_overrides',
...
]
This is a bit gnarly, but here's a potential solution if you don't want to give up on the Django framework:
The problem is that the method add_domain is buried deep in a huge method within syndication framework, and I don't see a clean way to override it. Since this method is used for both the feed URL and the feed items, a monkey patch of add_domain would need to consider this.
Django source:
https://github.com/django/django/blob/master/django/contrib/syndication/views.py#L178
Steps:
1: Subclass the Feed class you're using and do a copy-paste override of the huge method get_feed
2: Modify the line:
link = add_domain(
current_site.domain,
self._get_dynamic_attr('item_link', item),
request.is_secure(),
)
To something like:
link = self._get_dynamic_attr('item_link', item)
I did end up digging through the syndication source code and finding no easy way to override it and did some hacky monkey patching. (Unfortunately I did it before I saw the answers posted here, all of which I assume will work about as well as this one)
Here's how I did it:
def item_link(self, item):
# adding http:// means the internal get_feed won't modify it
return "http://"+item.magnet_link
def get_feed(self, obj, request):
# hacky way to bypass the domain handling
feed = super().get_feed(obj, request)
for item in feed.items:
# strip that http:// we added above
item['link'] = item['link'][7:]
return feed
For future readers, this was as of Django 2.0.1. Hopefully in a future patch they allow support for protocols like magnet.

I need to rewrite url in django

I need to rewrite url www.example.com/product/1 to www.example.com/en/product/1 when user chooses english language. (he will click on a select box that will toggle the language and set a session called 'language')
I cannot choose the django 1.4 which supports this feature. We are advised to stick with django 1.3.
Hence I tried a middleware, but as it turns out, the middleware runs for each request resulting in a endless loop.
class urlrewrite():
def process_request(self, request):
if 'i' in request.session:
if request.session.get('i','') != 0:
print "session"
request.session['i'] = request.session['i'] + 1
else:
request.session['i'] = 0
else:
request.session['i'] = 0
print "request.session['i']", request.session['i']
if request.session.get('i','') == SOME_CONSTANT and request.session.get('django_language','') == 'en':
del request.session['i']
return HttpResponseRedirect("en/"+request.META['PATH_INFO'])
Ofcourse, it doesnt work. This runs for every single request.
Kindly help me out.
Thank you
Don't write this yourself, use someone else's hard work.
Try django-cms's solution first.
==== EDIT ====
You do not need to used django-cms, just have it installed and use their Multilingual URL Middleware. This interfaces with django's regular internationalisation machinery.
http://django-cms.readthedocs.org/en/2.3.3/advanced/i18n.html
This problem can be solved by using a little trick in your urls.py file, as shown by this part of the docs: https://docs.djangoproject.com/en/1.4/ref/generic-views/#django-views-generic-simple-redirect-to.
You keep the same view, it will simple have a different URL. I think thats what you want. Make sure you choose the 1.3 version of the docs, I believe there has been some changes.

Decoupling django apps 2 - how to get object information from a slug in the URL

I am trying to de-couple two apps:
Locations - app containing details about some location (town, country, place etc)
Directory - app containing details of places of interest (shop, railway station, pub, etc) - all categorised.
Both locations.Location and directory.Item contain lat/lng coords and I can find items within a certain distance of a specific lat/lng coord.
I'd like to use the following URL structure:
/locations/<location_slug>/directory/<category_slug>/
But I don't want to make my directory app reliant on my location app.
How can I translate this url to use a view like this in my directory app?
items_near(lat, lng, distance, category):
A work around would be to create a new view somewhere that translates this - but where should I put that? if it goes in the Directory app, then I've coupled that with my Location app, and vice versa.
Would a good idea be to place this workaround code inside my project URLs file? Thus keeping clear of both apps? Any issues with doing it like this?
For your urlpattern to work, the view function invoked has to be aware of both Locations and Directories. The short answer is you can put this view function anywhere you want - it's just a python function. But there might be a few logical places for it, outside of your Directory or Location app, that make sense.
First off, I would not put that view code in in your top-level urls.py, as that file is intended for URLconf related code.
A few options of where to put your view:
Create a new view function in a file that lives outside of any particular app. <project_root>/views.py is one possibility. There is nothing wrong with this view calling the item_near(..) view from the Directory app.
# in myproject/urls.py
urlpatterns = (
...
(r'/locations/(?P<location_slug>[\w\-]+)/directory/(?P<category_slug>[\w\-]+)/',
'myproject.views.items_near_from_slug')
)
# in myproject/views.py
from directory.views import items_near
def items_near_from_slug(request, location_slug, category_slug):
location = get_object_or_404(Location, slug=location_slug)
distance = 2 # not sure where this comes from
# And then just invoke the view from your Directory app
return items_near(request, location.lat, location.lng, distance, category_slug)
Create a new app and put the code there, in <my_new_app>/views.py. There is no requirement that a Django app need to have a models.py, urls.py, etc. Just make sure to include the __init__.py if you want Django to load the app properly (if, for instance, you want Django to automatically find templatetags or app specific templates).
Personally, I would go with option 1 only if the project is relatively simple, and <project_root>/views.py is not in danger of becoming cluttered with views for everything. I would go with option 2 otherwise, especially if you anticipate having other code that needs to be aware of both Locations and Directories. With option 2, you can collect the relevant urlpatterns in their own app-specific urls.py as well.
From the django docs here if you're using django >=1.1 then you can pass any captured info into the included application. So splitting across a few files:
# in locations.urls.py
urlpatterns = ('',
(r'location/(?P<location_slug>.*?)/', include('directory.urls')),
#other stuff
)
# in directory.urls.py
urlpatterns = ('',
(r'directory/(?P<directory_slug>.*?)/', 'directory.views.someview'),
#other stuff
)
# in directory.views.py
def someview(request, location_slug = None, directory_slug = None):
#do stuff
Hope that helps. If you're in django < 1.1 I have no idea.
Irrespective of how much ever "re-usable" you make your app, inevitably there is a need for site-specific code.
I think it is logical to create a "site-specific" application that uses the views of the reusable and decoupled apps.

Adding more CoC to Django

I come from a Cake background, and I'm just starting to learn Django now. I'm liking it quite a bit, but I kinda wish it used convention over configuration like cake does. So,
How can I get Cake-style URLs automatically? For example, if I went to mysite.com/posts/view/5 it would load up mysite.posts.views.view and pass an argument 5 to it? I was thinking I could add something like (r'^(.*)/(.*)', 'mysite.$1.$2'), to urls.py, but of course, that won't work.
How can I automatically load up a template? Each view function should automatically load a template like templates/posts/view.html.
Is this even possible, or do I have to hack the core of Django?
Here's my solution, based on what Carl suggested:
urlpatterns = patterns('',
# url pats here
url(r'^(?P<app>\w+)/(?P<view>\w+)/(?P<args>.*)$', 'urls.dispatch')
)
def dispatch(req, app, view, args): # FIXME: ignores decorators on view func!
func = get_callable(app+'.views.'+view)
if args:
ret = func(req, *args.split('/'))
else:
ret = func(req)
if type(ret) is dict:
return render_to_response(app+'/'+view+'.html', ret)
else:
return ret
Seems to be working pretty well with initial tests. Solves both problems with a single function. Probably won't support GET-style arguments tho.
Those points are both implementable without hacking Django core, but either one will require a non-trivial level of familiarity with advanced Python techniques.
You can do the generic URL pattern with a pattern like this:
url(r'^(?P<appname>\w+)/(?P<viewfunc>\w+)/(?P<args>.*)$', 'myresolverfunc')
Then define a 'myresolverfunc' "view" function that takes "appname", "viewfunc", and "args" parameters, and implement whatever logic you want, splitting args on "/" and dynamically importing and dispatching to whatever view function is referenced. The trickiest part is the dynamic import, you can search Django's source for "importlib" to see how dynamic imports are done internally various places.
The automatic template loader can be implemented as a view function decorator similar to the various "render_to" decorators out there, except you'll generate the template name rather than passing it in to the decorator. You'll have to introspect the function object to get its name. Getting the app name will be trickier; you'll probably just want to hardcode it as a module-level global in each views.py file, or else work in conjunction with the above URL dispatcher, and have it annotate the request object with the app name or some such.
I don't you'll need to hack the core of Django for this. It sounds like you might be in need of generic views. Also check out the Generic Views topic guide.
The first example given in the generic views documentation sounds like your first bullet point:
Example:
Given the following URL patterns:
urlpatterns = patterns('django.views.generic.simple',
(r'^foo/$', 'direct_to_template', {'template':'foo_index.html'}),
(r'^foo/(?P<id>\d+)/$', 'direct_to_template', {'template':'foo_detail.html'}),
)
... a request to /foo/ would render the template foo_index.html, and a request to /foo/15/ would render the foo_detail.html with a context variable {{ params.id }} that is set to 15.