Django dev server does not automatically reload after saving files - django

I tested Django 3 a few months ago, and it all worked well. The browser refreshed itself after I made a change to files (.html and .py).
Now I have an issue with a newly created Django project, the browser doesn't automatically reload after I saved a change that I made on my local machine.
OS: Windows 11
Editor: PyCharm / VS Code
Django 4.0.4
Python 3.10.4
Directory structure
project/
├── project/
│ ├── ...
│ ├── settings.py
│ ├── urls.py
│ └── ...
├── first_app/
│ ├── ...
│ ├── urls.py
│ ├── views.y
│ └── ...
├── templates/
│ └── first_app/
│ └── index_view.html
└── manage.py
Default settings.py file with
....
INSTALLED_APPS = [
...
'first_app',
...
]
'DIRS': [BASE_DIR / 'templates']
....
project/urls.py
....
urlpatterns = [
path('', include('first_app.urls')
]
....
first_app/views.py
....
class IndexView(generic.TemplateView):
template_name = 'first_app/index_view.html'
...
first_app/urls.py
....
urlpatterns = [
path('', views.IndexView.as_view(), name='index')
]
....
templates/first_app/index_view.html
....
<p>
Test paragraph 1
</p>
....
I run the server locally with py manage.py runserver
When I change 'paragraph' to 'example' in index_view.html and save it, it is supposed to automatically refresh the browser and display Test example 1. But the browser doesn't reload.
I have created new Django projects several times and the results are still the same.
Am I missing something?
I have tried the solutions on the following questions, but none of the solutions work for me.
https://stackoverflow.com/questions/68974685/why-isnt-the-django-dev-server-reloading/71618373\
Django server not reloading on file changes
Thanks.
Update:
py manage.py runserver output
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
May 25, 2022 - 21:40:08
Django version 4.0.4, using settings 'project.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

(Note: I'm capturing the summary of the discussion in the comments -- you found the actual problem in the end)
By default, Django is only able to reload the test server on code or template changes, it is not able to refresh the browser automatically.
If your browser was refreshing the page by itself for your previous projects, either it was triggered by a signal from your IDE, or by some javascript in your pages that was forcing a refresh.
It turns out you had the third-party package django-browser-reload installed in your previous projects, which injects a small script into your pages that refresh the page whenever the Django test server reloads. Adding that package to your new project will resolve the issue.

Related

Is it possible to use FastAPI with Django?

I'm a Django developer and recently stumbled onto the FastAPI framework.
Then I decided to give it a shot. But usually when you talk about building RESTful APIs with Django you usually use the Django Rest Framework (DRF).
Is anybody aware if it is possible to substitute DRF by FastAPI using Django perks, like its ORM, and still have access to all of FastAPI's async features?
Up until now I only found one article on this. But in the process of integration the author lost most of the features of FastAPI.
You can find it here.
In the FastAPI docs, they do mention that it is possible to redirect certain request to a WSGI application here.
Short Answer
Yes it's possible with WSGIMiddleware.
For example, you can use all Django features (yes admin too) with mounting, with this example code.
import os
from importlib.util import find_spec
from configurations.wsgi import get_wsgi_application
from fastapi import FastAPI
from fastapi.middleware.wsgi import WSGIMiddleware
from fastapi.staticfiles import StaticFiles
from api import router
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myapp.settings")
os.environ.setdefault("DJANGO_CONFIGURATIN", "Localdev")
application = get_wsgi_application()
app = FastAPI()
app.mount("/admin", WSGIMiddleware(application))
app.mount("/static",
StaticFiles(
directory=os.path.normpath(
os.path.join(find_spec("django.contrib.admin").origin, "..", "static")
)
),
name="static",
)
Also this one is from WSGIMiddleware documentation, it's a more straight-forward example (This one is for Flask but it demonstrates the same idea.).
from fastapi import FastAPI
from fastapi.middleware.wsgi import WSGIMiddleware
from flask import Flask, escape, request
flask_app = Flask(__name__)
#flask_app.route("/")
def flask_main():
name = request.args.get("name", "World")
return f"Hello, {escape(name)} from Flask!"
app = FastAPI()
#app.get("/v2")
def read_main():
return {"message": "Hello World"}
app.mount("/v1", WSGIMiddleware(flask_app))
Update
While it is possible in the approach listed below, I genuinely think that we should avoid coupling different frameworks in such a monolith. Doing so could lead to unexpected bugs and make it harder to scale.
Instead, we could build 1 backend service in FastAPI, 1 Django Admin service for example, and then use NGINX to route traffic to these backend services. Using NGINX to route traffic to different backend services in production is common anyway.
Integration Of FastAPI With Django (WSGI)
https://github.com/jordaneremieff/django-fastapi-example.git
After hours of searching finally I found a great implementation in the link above. It worked seamlessly for me!
Testing
In order to make this simple to test, below are some few adjustments I made to the above reference:
api/models.py
class Item(models.Model):
title = models.CharField(max_length=50)
description = models.TextField()
# owner = models.ForeignKey(
# settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="items"
# )
api/schemas.py
class Item(ItemBase):
# id: int
# owner_id: int
class Config:
orm_mode = True
POST
curl -d "{\"title\":\"le titre\", \"description\":\"la description\"}" -H "Content-Type: application/json" -X POST http://127.0.0.1:8000/api/items
GET
curl http://127.0.0.1:8000/api/items
Thank you for the awesome answers. Here is a little tweaked answer where I have fixed some imports as well as I have used a model from a Django app.
from fastapi import FastAPI
from fastapi.middleware.wsgi import WSGIMiddleware
from django.core.wsgi import get_wsgi_application
import os
from importlib.util import find_spec
from fastapi.staticfiles import StaticFiles
from django.conf import settings
# Export Django settings env variable
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
# Get Django WSGI app
django_app = get_wsgi_application()
# Import a model
# And always import your models after you export settings
# and you get Django WSGI app
from accounts.models import Account
# Create FasatAPI instance
app = FastAPI()
# Serve Django static files
app.mount('/static',
StaticFiles(
directory=os.path.normpath(
os.path.join(find_spec('django.contrib.admin').origin, '..', 'static')
)
),
name='static',
)
# Define a FastAPI route
#app.get('/fastapi-test')
def read_main():
return {
'total_accounts': Account.objects.count(),
'is_debug': settings.DEBUG
}
# Mount Django app
app.mount('/django-test', WSGIMiddleware(django_app))
Hint: I created a file named app.py in my Django project's root directory and it worked. Here is my directory structure:
.
├── accounts
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   ├── 0001_initial.py
│   │   ├── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── app.py
├── db.sqlite3
├── project
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── manage.py
And run your FastAPI app:
(myvenv) ➜ project uvicorn --host 0.0.0.0 --port 8000 app:app --reload
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO: Started reloader process [48366] using statreload
INFO: Started server process [48368]
INFO: Waiting for application startup.
INFO: Application startup complete.
Hopefully, this will work for you. Now accessing /django-test will serve your Django project and /fastapi-test will serve the FastAPI part.
This configuration serves Django static files too and we can use our Django models in our FastAPI code as well. I'll test it further and I'll update this answer if I find any possibilities for improvement.
As Sumitrhan pointed out in a commet: There is also Django Ninja a Project that uses very similar concepts like Fast API (Routes, Pydandantic Model Validation) but simply is a Django App.
Given that current Django supports also async views in I see no point in mixing django and FastApi, to get the same feature set only much more complicated as not as good integrated.
there is good resource as follows:
Combine the power of FastAPI and Django

Serving static files in Flask production

I have the static folder in my app folder and that's where I have js, css, images etc. folders.
I reference them like this in my templates:
<script src="/static/js/main.js"></script>
and it works locally. But, when the app is published to production, I get a 404 for all static files. I'm new to flask so I guess I'm serving them wrong.
I don't have the static folder set as a designated static files folder anywhere in the app - is it done automatically if it's called 'static' or do I need to set it in code?
My project structure:
app/
├── static/
│ └── js/
| |__ css/
├── templates/
│ └── index.html
└── __init__.py
application.py
application.py:
from app import create_app
application = create_app()
init.py:
from flask import Flask
from config import BaseConfig
def create_app():
server = Flask(__name__)
server.config.from_object(BaseConfig) # this is just for the secret key
return server
Flask automatically adds a static view that takes a path relative to the $YOUR_APP/static directory and serves it.
Typically you would include your js/css files in your templates like so:
{{ url_for('static', filename='main.js') }}
First part of the solution was Bert's answer above which described how to correctly reference assets from the templates.
The other part I was missing was this:
server = Flask(__name__, static_url_path="", static_folder="static")
instead of what I had before:
server = Flask(__name__)
Adding static_url_path and static_folder fixed the issue for me.

Django URL patterns to serve external webpack bundle

I'm writing a django rest framework API (backend) with a react SPA frontend. For the production environment, the front end is served up via nginx at http://example.com/ and the backend is proxied to gunicorn and served from the same domain with a different path - http://example.com/api/
This is all working fine in production, there are no CORS issues as the frontend and backend are both served up under the same domain via nginx.
For local development, I want to be able to replicate a similar setup using ./manage.py runserver and have django serve up the frontend from project/frontend/dist (built by npm build seperately).
Has anyone got any urlpattern wizardry that will allow this to happen and also let the react-router play nicely? I just can't figure it out and it's starting to drive me nuts...
The project structure is something like this if it helps in any explanations.
Project
|
── backend
│   ├── apps
│   ├── config
| | ├── settings.py
| | ├── settings_local.py
| | ├── urls.py
│   ├── manage.py
│   ├── requirements.txt
│   └── venv
├── frontend
│   ├── dist (contains the npm build webpack)
│   ├── node_modules
│   ├── package.json
│   ├── package-lock.json
│   ├── scripts
│   ├── src
│   ├── webpack.config.js
Edit #1
Thanks to another post I was able to get partially the way there by adding this to my urls.py
if settings.DEBUG:
from django.views.generic import RedirectView
from django.contrib.staticfiles.views import serve
bundle_path = os.path.abspath(os.path.join(root_dir, 'frontend', 'dist'))
settings.STATICFILES_DIRS += (bundle_path,)
urlpatterns += [url(r'^$', serve, kwargs={'path': 'index.html'})]
urlpatterns += [url(r'^(?!/?static/)(?!/?media/)(?P<path>.*\..*)$',
RedirectView.as_view(url='/static/%(path)s', permanent=False))]
The only issue I have here is that if I go directly to one of the JS router links, then django tries to interpret it and can't find the route.
What I need now is a catch-all that will redirect to '/' but also keep any extra URL path information on the url
Finally got this to work with all the routing.
Added the webpack dist folder to settings.STATICFILES_DIRS
Added the webpack frontend path to settings.TEMPLATES
Added 3 new urlpatterns to urls.py ( based on a conditional, settings.DEBUG)
urlpatterns += [
url(r'^(?!/?static/)(?!/?media/)(?P<path>.*\..*)$',
RedirectView.as_view(url='/static/%(path)s', permanent=False)),
url(r'^$', TemplateView.as_view(template_name='dist/index.html'), name='frontend'),
url(r'^(?P<path>.*)/$', TemplateView.as_view(template_name='dist/index.html'), name='frontend'),
]
The first redirects any of the asssets for the bundle to be served using django's static files.
The second pattern serves the root path / as a template from the webpack dist folder
The final piece handles all the leftover JS routing paths to go back to the initial root view.
You can use nginx reverse proxy to serve them.
No need config Django's url.
See example here:
http://www.codingpedia.org/ama/how-to-configure-nginx-in-production-to-serve-angular-app-and-reverse-proxy-nodejs

Use SQLite for Django locally and Postgres on server

I am starting with Django development as a hobby project. So far I have happily worked with SQLite, both in development (i.e. with py manage.py runserver) and in deployment (on Nginx + uWSGI). Now I would also like to learn to use a more robust PostgreSQL database. However, if possible, I would like to skip installing it locally, to avoid installing Postgres on Windows.
I was wondering if it was possible, by means of Django, to use SQLite whenever I use the built-in server and Postgres in deployment, without changing the project code. Couldn't find how to do it.
I can use a workaround and make my deployment procedure change the settings on server each time I deploy. But that's kind of a hack.
You could split your settings.py in multiple settings file e.g.
[projectname]/
├── [projectname]/
│ ├── __init__.py
│ ├── settings/
│ │ │── base.py
│ │ │── development.py
│ │ │── production.py
│ ├── urls.py
│ └── wsgi.py
└── manage.py
your base.py includes all code from your current settings.py. specify the different databases in your development.py(sqlite3) and in your production.py (postgresql) and import the base.py in each
from .base import *
Last but not least you'll have to tell django which file it should use. Add
export DJANGO_SETTINGS_MODULE="projectname.settings.development"
to your postactivate of your virtualenv on your development server and
export DJANGO_SETTINGS_MODULE="projectname.settings.production"
on your production server. Don't forget to
unset DJANGO_SETTINGS_MODULE
in your predeactivate.
More infos are here: http://www.marinamele.com/taskbuster-django-tutorial/settings-different-environments-version-control
btw. a great tutorial with a lot of best practices
replace settings.py with
settings/
│── __init__.py
│── base.py
│── development.py
│── production.py
in __init__.py
import os
app_stage = os.environ.get('DJANGO_APP_STAGE', 'dev')
if app_stage == 'prod':
from .production import *
else:
from .development import *
And finally when you launch app for production, make sure you set env DJANGO_APP_STAGE='prod'
Create your database settings in respective files and you are good to go.
Very late reply, but I hope this helps someone in the future.
I found this article on Digital Ocean that has the database option split happening in one settings file. https://docs.digitalocean.com/tutorials/app-deploy-django-app/#configuring-database-access
Makes use of a DEVELOPMENT_MODE system environment variable to split settings. They also included a check to prevent a connection when a command is run like collectstatic.
settings.py file:
import os
import sys
import dj_database_url
DEVELOPMENT_MODE = os.getenv("DEVELOPMENT_MODE", "False") == "True"
if DEVELOPMENT_MODE is True:
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
}
}
elif len(sys.argv) > 0 and sys.argv[1] != 'collectstatic':
if os.getenv("DATABASE_URL", None) is None:
raise Exception("DATABASE_URL environment variable not defined")
DATABASES = {
"default": dj_database_url.parse(os.environ.get("DATABASE_URL")),
}
Note: This is also making use of the Django Database URL package, but you could easily adapt for separated database arguments.

Moving django apps into subfolder and url.py error

I have a question about putting django apps into "apps" subdirectory. I have the app called “faktura” in a project_root. I didn’t like the fact it lies there and I want to store all my apps under “apps” subdirectory.
So, I found out that I could extend python path to “apps” subdir, so after looking in the internet, I added this string to settings.py: sys.path.insert(0, os.path.join(PROJECT_PATH, "apps")). Then I added the app to INSTALLED_APPS like “faktura”. Everything worked smooth until I added url(r'^faktura/', include('faktura.urls')) to urls.py in the root. Since that, Django throws the error message “No module named faktura” full taceback is here: http://dpaste.com/737380/
What can be wrong here, why only urls.py can’t find the app? And does it can’t find this app if I added it to the PATH? I spent a morning trying to figure out what’s wrong and now I need your help.
I don't know why the previous answer got -1 aside from maybe a few redundant lines that can be corrected. Anyway, I found a slightly different method that doesn't involve adding anything to the python path.
This is my final directory structure, I will explain in a moment:
mysite
├── mysite
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── apps
│ ├── __init__.py
│ └── myfirstapp
│ ├── __init__.py
│ ├── admin.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
└── manage.py
No matter if you have just created your project or if you want to move your apps, create the apps subdirectory that should contain your apps. The trick is to add an __init__.py to that directory.
mkdir apps
touch apps/__init__.py
Now you can move your existing apps into the apps subdirectory. If you would like to create a new one instead here are the commands:
python manage.py mysecondapp
mv mysecondapp apps/
Warning: Don't be tempted to call python manage.py ./apps/mysecondapp. For some reason this deletes all other apps in that directory. I just lost a day of work this way.
Next, you will need to fix a few imports. Your settings.py should be prefixed with apps:
INSTALLED_APPS = (
...
'apps.myfirstapp',
'apps.mysecondapp'
)
Lastly, fix your project's urls.py to prefix apps:
urlpatterns = patterns('',
url(r'^myfirstapp', include('apps.myfirstapp.urls')),
...
)
Depending on how you wrote them, you might also have to fix a few imports inside your app. Either just use from models import MyFirstModel or also prefix it using from apps.myfirstapp.models import MyFirstModel.
In short, if you make your apps directory a python package (by adding __init__.py), you can use it as part of the import path. This should work regardless of the deployment method with no extra configuration.
Use BASE_DIR variable from the settings.py. It should be already defined:
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
Better not use the __file__ attribute in manage.py and wsgi.py, as they are located in different directories.
So, just add the following to manage.py and wsgi.py (and to the celery.py if you use Celery):
from django.conf import settings
sys.path.append(os.path.join(settings.BASE_DIR, "apps"))
You will end up with the following project structure:
project
├── project
│ ├── __init__.py
│ ├── celery.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── apps
│ ├── app1
│ └── app2
└── manage.py
To keep your Django applications in a subfolder (such as apps/), first add the following to your settings.py:
import os
PROJECT_ROOT = os.path.dirname(__file__)
Then in manage.py:
Right under #!/usr/bin/env python add:
import sys
from os.path import abspath, dirname, join
from site import addsitedir
Right before if __name__ == "__main__" : add:
sys.path.insert(0, join(settings.PROJECT_ROOT, "apps"))
#Radu Gheorghiu's answer; It is not necessary to edit settings.py, and the insert path line can be condensed to 1 line of code.
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "apps"))
I sourced this answer from http://obroll.com/nested-application-inside-apps-sub-folder-in-django-1-3/