Django - Redirect to another domain from View - django

I'm trying to redirect from mydomain.com to google.com.
There are a couple of answers on stackoverflow that asume the following is working:
return HttpResponseRedirect('google.com')
or
return redirect('google.com')
But it doesn't it. This just redirects the page to itself and appends the google.com part so it comes out like this:
www.mydomain.com/google.com
What throws a 404 of course..
My view now looks like the following:
class MyView(TemplateView):
def get(self, request, *args, **kwargs):
return HttpResponseRedirect('google.com')
Can anyone give me insights in what I'm doing wrong?

They answers are in some sense correct: you do a redirect. But now the web browser needs to perform the redirect.
Usually paths that are not prepended with two consecutive slashes are assumed to be local: so that means it stays at the same domain.
In case you want to go to another domain, you need to add a protocol, or at least two consecutive slashes (such that the old protocol is reused):
return HttpResponseRedirect('https://google.com') # use https
or:
return HttpResponseRedirect('//google.com') # "protocol relative" URL
After all you only return a redirect answer to the browser. The browser can decide not to follow the redirect (some browsers do), or can interpret it in any way they like (although that means that the browser does not really does what we can expect it to do). We can not force a browser to follow the redirect.

Related

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

How to do a class based delete view that allows DELETE method in django 3.1?

In Django 3.1, the typical DeleteView accepts GET and POST.
See https://docs.djangoproject.com/en/3.1/ref/class-based-views/generic-editing/#deleteview
and I reproduce below:
A view that displays a confirmation page and deletes an existing object. The given object will only be deleted if the request method is POST. If this view is fetched via GET, it will display a confirmation page that should contain a form that POSTs to the same URL.
How do I do a DELETEView that's class based view and also accepts DELETE method?
Tldr; I chose to use 303 at the server side so that it can correct redirect to the list view
Long story short is here https://stackoverflow.com/a/24375475/80353
In this SO answer which applies to Spring (a Java Framework), the question had the same issue as me.
Send a DELETE
then server side want to redirect using 302
302 will use precedent method and list typically don't accept DELETE as precedent method. Only POST, GET, and HEAD as precedent method
This seems like a web framework issue. But it's not. It appears to a convention most sensible web frameworks adopt.
There are 3 solutions with drawbacks:
1. override the convention
Allow the backend web framework to accept DELETE as precedent method for 302.
Con: Not nice by convention
2. Let client handle redirection
send back a 200 then client will redirect back to list view
Con: This results in two requests and htmx-delete doesn't work that way. It will send a DELETE method request and then take whatever comes back and immediately swap. I like this so I want to keep this. One request to settle this rather than two.
3. Use 303 for the redirection
After successful delete, do a 303 redirect to list view (I chose this)
Con: 303 doesn't work with HTTP/1.0 and older browsers. But that's not a problem in the year 2021 and will continue to be less of a problem going forward.
In the end I wrote my own deleteview
from django.views.generic.detail import BaseDetailView
class DeleteThingView(BaseDetailView):
http_method_names = ["delete"]
model = Thing
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.delete()
response = redirect(reverse_lazy("things-list"))
response.status_code = 303
return response

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 URLConf Redirect with odd characters

I'm getting ready to move an old Classic ASP site to a new Django system. As part of the move we have to setup some of the old URLs to point to the new ones.
For example,
http://www.domainname.com/category.asp?categoryid=105 should 301 to http://www.domainname.com/some-category/
Perhaps I'm regex stupid or something, but for this example, I've included in my URLconf this:
(r'^asp/category\.asp\?categoryid=105$', redirect_to, {'url': '/some-category/'}),
My thinking is that I have to escape the . and the ? but for some reason when I go to test this, it does not redirect to /some-category/, it just 404s the URL as entered.
Am I doing it wrong? Is there a better way?
To elaborate on Daniel Roseman's answer, the query string is not part of the URL, so you'll probably want to write a view function that will grab the category from the query string and redirect appropriately. You can have a URL like:
(r'^category\.asp', category_redirect),
And a view function like:
def category_redirect(request):
if 'categoryid' not in request.GET:
raise Http404
cat_id = request.GET['category']
try:
cat = Category.objects.get(old_id=cat_id)
except Category.DoesNotExist:
raise Http404
else:
return HttpResponsePermanentRedirect('/%s/' % cat.slug)
(Altered to your own tastes and needs, of course.)
Everything after the ? is not part of the URL. It's part of the GET parameters.

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