I need to rewrite url in django - 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.

Related

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.

language by domain in 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.

django-paypal ipn works fine but signal is not received

I have this code at the end of my models.py file
from paypal.standard.ipn.signals import payment_was_successful
def confirm_payment(sender, **kwargs):
# it's important to check that the product exists
logging.debug('** CONFIRMED PAYMENT ***') #never reached this point
try:
bfeat = BuyingFeature.objects.get(slug=sender.item_number)
except BuyingFeature.DoesNotExist:
return
# And that someone didn't tamper with the price
if int(bfeat.price) != int(sender.mc_gross):
return
# Check to see if it's an existing customer
try:
customer = User.objects.get(email=sender.payer_email)
except User.DoesNotExist:
customer = User.objects.create(
email=sender.payer_email,
first_name=sender.first_name,
last_name=sender.last_name
)
# Add a new order
CustomerOrder.objects.create(customer=customer, feature=bfeat, quantity=1, paypal_email=sender.payer_email, invoice=sender.invoice, remarks='')
payment_was_successful.connect(confirm_payment)
The whole process runs ok. Payment is complete. return_url and cancel_url work fine. notify_url was tested from the paypal sandbox's test tools and works ok. However, signal is never received.
Signal code is placed at the end of the models.py and django-paypal code is placed inside my project's directory.
(code was 'stolen' from here)
I must be doing something completely wrong. Any help would be appreciated!
In django-paypal there are two signals for basic transactions:
payment_was_successful
payment_was_flagged
You must handle both signals.
I had this problem - and having chased around a few similar questions have found a resolution for my specific case. I mention it here in case anyone else is hitting this wall.
I've not researched it thoroughly, but it looks as though it's highly dependent on which version/repository you source your copy of django-paypal from. Specifically, the version I downloaded wasn't updated to accommodate the {% csrf_token %} idiom. To get this to work, I had to add the #csrf_exempt decorator to two views:
the ipn view in paypal.standard.views
the view loaded by the return url in my django paypal dictionary (... this one flags a highly accurate error if you have debug on).
Is django-paypal there in the settings.INSTALLED_APPS?
I don't see any other reason why the signal wouldn't be fired, otherwise.

Django: creating/modifying the request object

I'm trying to build an URL-alias app which allows the user create aliases for existing url in his website.
I'm trying to do this via middleware, where the request.META['PATH_INFO'] is checked against database records of aliases:
try:
src: request.META['PATH_INFO']
alias = Alias.objects.get(src=src)
view = get_view_for_this_path(request)
return view(request)
except Alias.DoesNotExist:
pass
return None
However, for this to work correctly it is of life-importance that (at least) the PATH_INFO is changed to the destination path.
Now there are some snippets which allow the developer to create testing request objects (http://djangosnippets.org/snippets/963/, http://djangosnippets.org/snippets/2231/), but these state that they are intended for testing purposes.
Of course, it could be that these snippets are fit for usage in a live enviroment, but my knowledge about Django request processing is too undeveloped to assess this.
Instead of the approach you're taking, have you considered the Redirects app?
It won't invisibly alias the path /foo/ to return the view bar(), but it will redirect /foo/ to /bar/
(posted as answer because comments do not seem to support linebreaks or other markup)
Thank for the advice, I have the same feeling regarding modifying request attributes. There must be a reason that the Django manual states that they should be considered read only.
I came up with this middleware:
def process_request(self, request):
try:
obj = A.objects.get(src=request.path_info.rstrip('/')) #The alias record.
view, args, kwargs = resolve_to_func(obj.dst + '/') #Modified http://djangosnippets.org/snippets/2262/
request.path = request.path.replace(request.path_info, obj.dst)
request.path_info = obj.dst
request.META['PATH_INFO'] = obj.dst
request.META['ROUTED_FROM'] = obj.src
request.is_routed = True
return view(request, *args, **kwargs)
except A.DoesNotExist: #No alias for this path
request.is_routed = False
except TypeError: #View does not exist.
pass
return None
But, considering the objections against modifying the requests' attributes, wouldn't it be a better solution to just skip that part, and only add the is_routed and ROUTED_TO (instead of routed from) parts?
Code that relies on the original path could then use that key from META.
Doing this using URLConfs is not possible, because this aliasing is aimed at enabling the end-user to configure his own URLs, with the assumption that the end-user has no access to the codebase or does not know how to write his own URLConf.
Though it would be possible to write a function that converts a user-readable-editable file (XML for example) to valid Django urls, it feels that using database records allows a more dynamic generation of aliases (other objects defining their own aliases).
Sorry to necro-post, but I just found this thread while searching for answers. My solution seems simpler. Maybe a) I'm depending on newer django features or b) I'm missing a pitfall.
I encountered this because there is a bot named "Mediapartners-Google" which is asking for pages with url parameters still encoded as from a naive scrape (or double-encoded depending on how you look at it.) i.e. I have 404s in my log from it that look like:
1.2.3.4 - - [12/Nov/2012:21:23:11 -0800] "GET /article/my-slug-name%3Fpage%3D2 HTTP/1.1" 1209 404 "-" "Mediapartners-Google
Normally I'd just ignore a broken bot, but this one I want to appease because it ought to better target our ads (It's google adsense's bot) resulting in better revenue - if it can see our content. Rumor is it doesn't follow redirects so I wanted to find a solution similar to the original Q. I do not want regular clients accessing pages by these broken urls, so I detect the user-agent. Other applications probably won't do that.
I agree a redirect would normally be the right answer.
My (complete?) solution:
from django.http import QueryDict
from django.core.urlresolvers import NoReverseMatch, resolve
class MediapartnersPatch(object):
def process_request(self, request):
# short-circuit asap
if request.META['HTTP_USER_AGENT'] != 'Mediapartners-Google':
return None
idx = request.path.find('?')
if idx == -1:
return None
oldpath = request.path
newpath = oldpath[0:idx]
try:
url = resolve(newpath)
except NoReverseMatch:
return None
request.path = newpath
request.GET = QueryDict(oldpath[idx+1:])
response = url.func(request, *url.args, **url.kwargs)
response['Link'] = '<%s>; rel="canonical"' % (oldpath,)
return response

What is the cleanest way to add code to contrib.auth

I've migrated an old joomla installation over to django. The password hashes is an issue though. I had to modify the get_hexdigest in contrib.auth.models to have an extra if statement to reverse the way the hash is generated.
# Custom for Joomla
if algorithm == 'joomla':
return md5_constructor(raw_password + salt).hexdigest()
# Djangos original md5
if algorithm == 'md5':
return md5_constructor(salt + raw_password).hexdigest()
I also added the following to the User model to update the passwords after login if they have the old joomla style:
# Joomla Backwards compat
algo, salt, hsh = self.password.split('$')
if algo == 'joomla':
is_correct = (hsh == get_hexdigest(algo, salt, raw_password))
if is_correct:
# Convert the password to the new more secure format.
self.set_password(raw_password)
self.save()
return is_correct
Everything is working perfectly but I'd rather not edit this code directly in the django tree. Is there a cleaner way to do this in my own project?
Thanks
Your best bet would be to roll a custom auth backend and rewrite get_hexdigest in there. Never done it myself, but documentation on how to do so is available at http://docs.djangoproject.com/en/dev/topics/auth/#authentication-backends.
Thanks for the guidance. For anyone who needs to go the other way (DJango to Joomla) with DJ passwords, the DJ format is Sha1$salt$crypt.
Joomla standard auth plugin and joomla core JUserHelper do not implement the same SHA1 algorithum but it is fairly easy to patch into joomla.php in that plugin, where the plugin normally does an explode on ':'. Do a three-part explode with '$' and use salt = [1], compare that against $encrypted = sha1($salt.$plaintext), match that against the crypt [2].