I'm working on a Django web app and it's running really fine and well, but I'm facing a small issue where after I deployed the app on a VPS, the Django Rest Framework default API URL is pointing at the home IP address like in the image below.
The issue here is that when I'm running my app on the server, the above highlighted URL is directing me toward my home IP address on my local machine.
How can I change this URL with the IP address of the VPS or domain? Is that possible?
Edit - 1:
Below is the code of my urls.py file if it can help
from django.views.static import serve
router = routers.DefaultRouter()
router.register(r'artfairs', views.ArtFairViewSet)
router.register(r'inquiries', views.InquiryViewSet)
router.register(r'mediums', views.MediumViewSet)
router.register(r'origins', views.OriginViewSet)
router.register(r'styles', views.StyleViewSet)
router.register(r'types', views.TypeViewSet)
router.register(r'users', views.UserViewSet)
router.register(r'artists', views.ArtistViewSet)
router.register(r'artworks', views.ArtworkViewSet)
router.register(r'shipping-addresses', views.ShippingAddressViewSet)
router.register(r'user-addresses', views.UserAddressViewSet)
router.register(
r'artworks-by-artist',
views.ArtworksByArtistViewSet,
basename='artworks-by-artist',
)
router.register(
r'cart',
views.CartViewSet,
basename='cart',
)
router.register(
r'latest-artworks',
views.LatestArtworksViewSet,
basename='latest-artworks',
)
router.register(
r'wishlist',
views.WishListViewSet,
basename='wishlist',
)
urlpatterns = [
path('', include(router.urls)),
# media and static files paths
url(r'^media/(?P<path>.*)$', serve,
{'document_root': settings.MEDIA_ROOT}),
url(r'^static/(?P<path>.*)$', serve,
{'document_root': settings.STATIC_ROOT}),
# paths related to JWT Authentication
path('api/token/', TokenObtainPairView.as_view(),
name='token_obtain_pair'),
path('api/token/refresh/',
TokenRefreshView.as_view(),
name='token_refresh'),
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + static(
settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
After a lot of research, I was finally able to find my answer in these 2 links. The first one was from DRF documentation while the other one is a question previously asked on stackoverflow.
Routers Page in Django Rest Framework Documentation
How to change the Django Rest Framework's default url to a custom
I edited my location settings to look like this:
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://unix:/root/<my_app_directory>/<my_app.sock>;
}
Related
I have a single page application created in Vue.js that utilizes the HTML5 History Mode for routing, and the html file is served with Django.
The urls.py of django is like so:
urlpatterns = [
url(r'^$', views.home),
url(r'^admin/', admin.site.urls),
url(r'^api-token-auth/', obtain_jwt_token),
]
And views.home:
def home(request):
return render(request, 'index.html')
Consider the following scenario:
User visits the home page (i.e., /)
Since, the home page responds with required index.html for the Single page Vuejs app, it works like its supposed to.
From there the user navigates to the about page (i.e., /username/12).
Its still working fine, as its navigating with the Vue router.
Now, the user refreshes the page.
Since there's no /username/12 in the urls.py patterns, it will show Page not found (404).
Now, I could provide another pattern in urls.py to catch all pattern in the last order as this:
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api-token-auth/', obtain_jwt_token),
url(r'^.*$', views.home),
]
But other urls like the media or static urls will also point to the same catch all pattern regex. How can I solve this problem?
I have a similar problem.
How can I use vue-router and django rest
framework the same time?
This is my solution to this problem. Hope it helps you.
Expected results:
http://127.0.0.1:8000/ <-- TeamplateView index.html using vue
http://127.0.0.1:8000/course <-- vue-router
http://127.0.0.1:8000/api <-- rest framework
http://127.0.0.1:8000/admin <-- django admin
and I try this and it works!
urls.py
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^api/', include(router.urls)),
url(r'^.*$', TemplateView.as_view(template_name="index.html")),
]
The order is important,
url(r'^.*$', TemplateView.as_view(template_name="index.html")),
is the last one
and this is my vue router
const router = new VueRouter({
mode: 'history',
base: __dirname,
routes: [{
path: '/courses',
component: CourseSet
}, {
path: '/',
component: hello
}]
})
My project on GitHub
Since you have mentioned "single page":
The server is supposed to serve just one page the index.html (or whatever else you would like to call it).
The server and the web application (front-end) code would communicate via api calls, such that the server provides resources and the web-app takes care of using that resource.
In case of a missing resource, the server still must not respond with a separate page, it should still respond with a message that the web-app can use.
I have a single page application created in Vue.js that utilizes the HTML5 History Mode for routing
I believe you are using vue-router, which simulates the single-page app to be a full-featured, multi-page application.
You may want to take a look at this and this, but the above holds true for a single page application.
You shared your urlpatterns:
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api-token-auth/', obtain_jwt_token),
url(r'^.*$', views.home),
]
But other urls like the media or static urls will also point to the same catch all pattern regex. How can I solve this problem?
A way you can manage that would be either by, serving on a route other than '/' like mentioned above for /app.
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api-token-auth/', obtain_jwt_token),
url(r'^.*$/app', views.home),
]
and in your router.js file:
new Router({
mode: 'History',
base: '/app'
routes: [
{
path: '/',
name: 'name',
component: ComponentName
}
]
})
Or prefixing the purpose served by the urls like
urlpatterns = [
url(r'^api/admin/', admin.site.urls),
url(r'^api/api-token-auth/', obtain_jwt_token),
url(r'^.*$', views.home),
url(r'^.*$/assets', your-static-assets)
]
I'm using VueJS router enabled history mode with Django 2.x by this way:
From app urls.py you need to repath your frontend url like this:
# urls.py
from django.urls import path, include
from django.urls import re_path
urlpatterns = [
path('', TemplateView.as_view(template_name="application.html"), name="app", ),
# ... rest of your urls
]
urlpatterns += [
re_path('^.*$', TemplateView.as_view(template_name="application.html")),
]
Now history mode working smoothly!
http://sample.com/#/users/login/
Become:
http://sample.com/users/login/
I solved the issue by using the negative look-ahead regex, so basically anything inside (?!ignore1|ignore2) will be ignored.
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api-token-auth/', obtain_jwt_token),
url(r'^(?!admin|api-token-auth|static).*$', views.home),
]
Note: I tried not including admin and api-token-auth inside the negative look-ahead and it didn't work for me, it will still route path that starts with admin to the views.home, not sure why as according to my understanding, django should match the earlier ones first.
Since index.html is templated (a dynamic page) because it's served from your views instead of static, this becomes a bit weird.
For production, definitely use nginx or apache to serve the static content in a similar fashion.
For development, this is what I would do:
from django.contrib.staticfiles.views import serve
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api-token-auth/', obtain_jwt_token),
]
# Serve your static media (regex matches *.*)
if settings.DEBUG:
urlpatterns.append(url(r'(?P<path>.*\..*)$', serve))
# Serve your single page app in every other case
urlpatterns.append(url(r'^.*$', views.home))
The answer provided above by #billi works fine, up to a point. My problem is that for my SPA I want (need?) to use the Vue <router-view> and <router-link> system. But the router.js maps paths to Vue components, and some of my paths are to Django views via Django urls.py - for example, this will result in a blank page if navigating to /api/places from the Vue router nav, but will resolve correctly to a Django if refreshed.
Ideas?
urls.py
urlpatterns = [
# Django
path('admin', admin.site.urls),
url(r'^api-auth/', include('rest_framework.urls')),
url(r'^api/places$', views.PlaceList.as_view()),
# catchall to Vue single page app
url(r'^.*$', TemplateView.as_view(template_name='myapp/spa.html'), name='home'),
]
router.js
export default new Router({
mode: 'history',
routes: [
{path: '/search', component: Search},
{path: '/about', component: About},
{path: '/', component: Home},
{path: '/api/places', }
]
})
App.vue
<template>
<div id="app">
<div>
<router-link to="/search">Search</router-link> ::
<router-link to="/about">About</router-link>
<router-link to="/">Home</router-link> ::
<router-link to="/api/places">Place API</router-link> ::
</div>
<router-view></router-view>
</div>
</template>
Don't know if anyone still looking for the solution.
I build Django Rest API and Vue front totally in a stand alone way. Then when i tried to put the dist folder items in static files of Django and tried to server through gunicorn and NGINX I just could not get the spa working.
Finally did this and very surprisingly it works.
Basically,
forward error page to the index.html of Vue.
Here's my NGINX config:
location /static/ {
root /root/bdn/bdn/server/;
}
location /media/ {
root /root/bdn/bdn/server/;
}
location ^~ /admin/ { # Define routes to be directed to backend as proxy
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://unix:/run/gunicorn.sock;
}
location ^~ /api/ { # Define routes to be directed to backend as proxy
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://unix:/run/gunicorn.sock;
}
location ^~ /api-auth/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://unix:/run/gunicorn.sock;
}
location ^~ /{
root /root/bdn/bdn/server/templates/;
index index.html;
}
error_page 404 /; # THIS IS WHAT IT CAME DOWN TO AFTER HOURS AND HOURS OF SEARCHING
Question says it almost all.
E.g. changing default url (http://127.0.0.1:8000) to a custom (https://api.example.com/v1)
I'm using HyperlinkedModels and everything seems to work properly in development. Moving the app to another server with custom url is giving me problems.
How do I change the default url:
To a custom one, let's say:
https://api.example.org/v1/
You are mixing two questions in one:
How to run django-rest-framework project on a different domain
How to change URL path of API
To answer the first one I'd say, "Just do it". Django's reverse uses request's domain to build absolute URL.
UPDATE: don't forget to pass Host header from nginx/apache. Below is a sample nginx config:
server {
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://127.0.0.1:8000;
}
}
The second (path, mount point) is set in the urls.py:
from django.conf.urls import url, include
from django.contrib import admin
from rest_framework import routers
from quickstart import views
router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'groups', views.GroupViewSet)
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^v1/', include(router.urls)), # <-------------- HERE
]
I have the following urlpatterns:
urlpatterns = patterns('',
url(r'^$', 'opeiaa.views.home', name='home'),
url(r'^store/$', 'opeiaa.views.store', name='store'),
url(r'^resume/$', 'opeiaa.views.resume', name='resume'),
url(r'^contact/$', 'opeiaa.views.contact', name='contact'),
url(r'^gallery/', include('gallery.urls')),
url(r'^admin/', include(admin.site.urls)),
)
... and am using this kind of template tag:
<a class='nav-link' href='{% url 'contact' %}'>Contact</a>
The URL gets rendered in the page as http://localhost:8000/contact/. Everything works fine, when using ./manage.py runserver for testing...
... but then I run ./manage.py runfcgi - then when I navigate to the contact page, the URL in the navigation points to http://localhost:8000/contact/contact/! I have tried putting a slash at the start to make the URL absolute, but the URLs appear to be absolute without it.
I am using nginx as a frontend, and the relevant config from there is:
location / {
include fastcgi_params;
fastcgi_pass unix:/tmp/django.sock;
fastcgi_param PATH_INFO $fastcgi_script_name;
fastcgi_pass_header Authorization;
fastcgi_intercept_errors off;
}
I am using Django 1.6 & Python 2.7.4. Anyone have any insight?
8 months later I had figured this out when it happened on another one of my sites, because SCRIPT_NAME was being set before include fastcgi_params!
The final working config snippet:
location / {
include fastcgi_params;
fastcgi_param SCRIPT_NAME "";
fastcgi_pass unix:/tmp/django.sock;
fastcgi_param PATH_INFO $fastcgi_script_name;
fastcgi_pass_header Authorization;
fastcgi_intercept_errors off;
}
I created a Client ID and Client Secret from the google cloud api console and added a record in the Social apps table for django-allauth
I also added WEB ORIGIN:
mysite.com (prod)
http://localhost:8000 (dev)
and REDIRECT URI
http:mysite.com/accounts/google/login/callback/ (prod)
localhost:8000/accounts/google/login/callback/ (dev)
in the google api console.
Sign in with Google works great in development and redirects to the localhost callback url on successful sign-in. But I'm getting a redirect_uri_mismatch error in prod.
These are the error details from the google error page:
Request Details
cookie_policy_enforce=false
scope=https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email
response_type=code
access_type=online
redirect_uri=http://127.0.0.1:8000/accounts/google/login/callback/
state=SOME_RANDOM_NUMBER
display=page
client_id=MY_CLIENT_ID
The redirect_uri is still set to 127.0.0.1 instead of http:mysite.com/accounts/google/login/callback/
So how do I set the proper redirect_uri?
This is my settings.py pertaining to django-allauth
INSTALLED_APPS = (
#the usual stuff
'allauth',
'allauth.account',
'allauth.socialaccount',
'allauth.socialaccount.providers.google',
)
import django.conf.global_settings as DEFAULT_SETTINGS
TEMPLATE_CONTEXT_PROCESSORS = DEFAULT_SETTINGS.TEMPLATE_CONTEXT_PROCESSORS + (
"allauth.account.context_processors.account",
"allauth.socialaccount.context_processors.socialaccount",
)
AUTHENTICATION_BACKENDS = (
"django.contrib.auth.backends.ModelBackend",
"allauth.account.auth_backends.AuthenticationBackend",
)
ACCOUNT_EMAIL_REQUIRED = True
LOGIN_REDIRECT_URL = "/"
Here's urls.py
urlpatterns = patterns('',
url(r'^accounts/', include('allauth.urls')),
)
I haven't made any other django-allauth settings.
I saw the docs and couldn't find where to make the change.
I found that the issue occurs because the nginx proxy, which sits in front of the python app server, sets the HTTP Host header to localhost.
So when allauth tries to do request.build_absolute_uri, HTTP_HOST is localhost.
So I set the Setting proxy_set_header in the nginx configuration file which fixed the issue.
proxy_set_header Host $http_host;
Also see Facebook oauth authentication is redirecting to localhost instead of to my domain for the same issue in a different avatar.
I am looking to use Flower (https://github.com/mher/flower) to monitor my Celery tasks in place of the django-admin as reccomended in their docs (http://docs.celeryproject.org/en/latest/userguide/monitoring.html#flower-real-time-celery-web-monitor). However, because I am new to this I am a little confused about the way Flower's page is only based on HTTP, and not HTTPS. How can I enable security for my Celery tasks such that any old user can't just visit the no-login-needed website http://flowerserver.com:5555 and change something?
I have considered Celery's own documentation on this, but they unfortunately there is no mention of how to secure Flower's api or web ui. All it says: [Need more text here]
Thanks!
Update: My question is in part a duplicate of here: How do I add authentication and endpoint to Django Celery Flower Monitoring?
However, I clarify his question here by asking how to run it using an environment that includes nginx, gunicorn, and celery all on the same remote machine. I too am wondering about how to set up Flower's outside accessible url, but also would prefer something like https instead of http if possible (or some way of securing the webui and accessing it remotely). I also need to know if leaving Flower running is a considerable security risk for anyone who may gain access to Flower's internal API and what the best way for securing this could be, or if it should just be disabled altogether and used just on an as-needed basis.
You can run flower with --auth flag, which will authenticate using a particular google email:
celery flower --auth=your.email#gmail.com
Edit 1:
New version of Flower requires couple more flags and a registered OAuth2 Client with Google Developer Console:
celery flower \
--auth=your.email#gmail.com \
--oauth2_key="client_id" \
--oauth2_secret="client_secret" \
--oauth2_redirect_uri="http://example.com:5555/login"
oauth2_redirect_uri has to be the actual flower login url, and it also has to be added to authorized redirect url's in Google Development Console.
Unfortunately this feature doesn't work properly in current stable version 0.7.2, but it is now fixed in development version 0.8.0-dev with this commit.
Edit 2:
You can configure Flower using basic authentication:
celery flower --basic_auth=user1:password1,user2:password2
Then block 5555 port for all but localhost and configure reverse proxy for nginx or for apache:
ProxyRequests off
ProxyPreserveHost On
ProxyPass / http://localhost:5555
Then make sure proxy mod is on:
sudo a2enmod proxy
sudo a2enmod proxy_http
In case you can't set it up on a separate subdomain, ex: flower.example.com (config above), you can set it up for example.com/flower:
run flower with url_prefix:
celery flower --url_prefix=flower --basic_auth=user1:password1,user2:password2
in apache config:
ProxyPass /flower http://localhost:5555
Of course, make sure SSL is configured, otherwise there is no point :)
I have figured out it using proxy on Django side https://pypi.org/project/django-revproxy/. So Flower is hidden behind Django auth which is more flexible than basic auth. And you don't need rewrite rule in NGINX.
Flower 0.9.5 and higher
URL prefix must be moved into proxy path: https://github.com/mher/flower/pull/766
urls.py
urlpatterns = [
FlowerProxyView.as_url(),
...
]
views.py
class FlowerProxyView(UserPassesTestMixin, ProxyView):
# `flower` is Docker container, you can use `localhost` instead
upstream = 'http://{}:{}'.format('flower', 5555)
url_prefix = 'flower'
rewrite = (
(r'^/{}$'.format(url_prefix), r'/{}/'.format(url_prefix)),
)
def test_func(self):
return self.request.user.is_superuser
#classmethod
def as_url(cls):
return re_path(r'^(?P<path>{}.*)$'.format(cls.url_prefix), cls.as_view())
Flower 0.9.4 and lower
urls.py
urlpatterns = [
re_path(r'^flower/?(?P<path>.*)$', FlowerProxyView.as_view()),
...
]
views.py
from django.contrib.auth.mixins import UserPassesTestMixin
from revproxy.views import ProxyView
class FlowerProxyView(UserPassesTestMixin, ProxyView):
# `flower` is Docker container, you can use `localhost` instead
upstream = 'http://flower:5555'
def test_func(self):
return self.request.user.is_superuser
I wanted flower on a subdirectory of my webserver, so my nginx reverse proxy configuration looked like this:
location /flower/ {
proxy_pass http://localhost:5555/;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
}
Now I can get to flower (password-protected) via www.example.com/flower
Most of this is derived from the Flower documentation page about configuring an nginx reverse proxy:
http://flower.readthedocs.org/en/latest/reverse-proxy.html
I followed #petr-přikryl's approach using a proxy view. However I couldn't get it to verify authentication (I don't think test_func is ever called). Instead I chose to embed this in the Django Admin views and use AdminSite.admin_view() (as described here) to wrap the view with Django Admin authentication.
Specifically, I made the following changes:
# Pipfile
[packages]
...
django-revproxy="*"
# admin.py
class MyAdminSite(admin.AdminSite):
# ...
def get_urls(self):
from django.urls import re_path
# Because this is hosted in the root `urls.py` under `/admin` this
# makes the total prefix /admin/flower
urls = super().get_urls()
urls += [
re_path(
r"^(?P<path>flower.*)$",
self.admin_view(FlowerProxyView.as_view()),
)
]
return urls
# views.py
from __future__ import annotations
from django.urls import re_path
from revproxy.views import ProxyView
class FlowerProxyView(ProxyView):
# Need `/admin/` here because the embedded view in the admin app drops the
# `/admin` prefix before sending the URL to the ProxyView
upstream = "http://{}:{}/admin/".format("localhost", 5555)
Lastly, we need to make sure that --url_prefix is set when running flower, so I set it to run like this in our production and dev environments:
celery flower --app=my_app.celery:app --url_prefix=admin/flower
To offload the django app, I suggest you use the X-Accel-Redirect header in order to use nginx to proxy the Flower server. It goes as follow:
the user requests the flower path (e.g. /task)
nginx proxy_pass the request to your app, as usual
your django app chooses to accept or reject the request (e.g. based on authentification)
if your app accepts the request, it returns a response with X-Accel-Redirect HTTP-header together with a string of an internal location, i.e. a path that cannot be accessed directly by the user
nginx intercepts the response instead of forwarding it to the user and uses it as a new path with the possibility this time to access internal locations, in our case the Flower server
If the request is rejected, simply do not use X-Accel-Redirect and handle the case as any other rejected request you'd implement.
nginx.conf:
upstream celery_server {
server /var/run/celery/flower.sock;
}
upstream app_server {
server /var/run/gunicorn/asgi.sock;
}
server {
listen 80;
location /protected/task {
internal; # returns 404 if accessed directly
proxy_http_version 1.1;
proxy_redirect off;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_pass http://celery_server/task;
}
location / {
proxy_http_version 1.1;
proxy_redirect off;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $server_name;
proxy_pass http://app_server;
}
}
views.py:
from django.contrib.admin.views.decorators import staff_member_required
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
class XAccelRedirectResponse(HttpResponse):
def __init__(self, path, *args, **kwargs):
super().__init__(*args, **kwargs)
self['X-Accel-Redirect'] = '/protected' + path
del self['Content-Type'] # necessary
# I chose to only allow staff members, i.e. whose who can access the admin panel
#staff_member_required
#csrf_exempt
def task_app(request, path):
query_str = request.META['QUERY_STRING'] # you must keep the query string
return XAccelRedirectResponse(f'/task/{path}?{query_str}')
urls.py:
from django.urls import re_path
from app import views
urlpatterns = [
re_path('task/(?P<path>.*)', views.task_app, name='task'),
]
Flower
It is important to change the url-prefix of Flower:
celery flower --unix-socket="/var/run/celery/flower.sock" --url-prefix="task"
Yep there's not auth on flower, since it's just talking to the broker, but if you run it over SSL then basic auth should be good enough.
How would HTTP and HTTPS affect Celery security? What user logins are you referring to?
Flower monitors to a Celery queue by attaching to the workers. When setting up Flower you need to provide connection string [broker]://[user_name]:[password]#[database_address]:[port]/[instance]. User name and password are the credential to log into the database of your choice.
If you're referring to this login, wouldn't simply disable/remove their logins be suffice?
This is a reply to Petr Přikryl's post. django-revproxy fails to work on my Django 4.1.x project. I am encountering error AttributeError: 'HttpResponse' object has no attribute '_headers'. Many others are facing the same issue. brianmay in the issue thread claims, "I think this project is basically dead, sorry."
I went with a different library to serve as a workaround.
Install django-proxy
This is what my code looks like.
# urls.py
from django.urls import re_path
from myapp.views import flower
urlpatterns = [
re_path("flower/(?P<path>.*)", flower),
]
# views.py
from django.views.decorators.csrf import csrf_exempt
from proxy.views import proxy_view
#csrf_exempt
def flower(request, path):
extra_requests_args = {}
remoteurl = f"http://localhost:5555/flower/" + path
return proxy_view(request, remoteurl, extra_requests_args)
Then run celery with
$ celery --app myproject flower --loglevel INFO --url_prefix=flower
You can then view it in your browser, served through Django, at http://localhost:8000/flower/.
Additional notes:
--url_prefix= is important because this will allow the proxy to serve the static files that flower requests.
If you are using docker compose, then you will likely need to change the hostname in the remoteurl string in the flower function to reflect the same of the service. For example, my service is appropriately called flower in my docker-compose.yaml file. Therefore, I would change the string from f"http://localhost:5555/flower/" to f"http://flower:5555/flower/"