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.
Related
I have a flask app with the following structure:
/test
├── /application
│ ├── __init__.py
│ ├── routes.py
│ ├── /static
│ ├── /templates
│ └── /dashboard
│ └── __init__.py
├── db.py
├── config.py
├── requirements.txt
├── helpers.py
└── wsgi.py
I am trying to implement a dash dashboard by intitating it through calling a function in application.init.py
where the function is define in dashboard.init.py
my application.init.py
def init_app():
"""Construct core Flask application."""
app = Flask(__name__, instance_relative_config=False)
app.config.from_object('config.Config')
# Ensure responses aren't cached
#app.after_request
def after_request(response):
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
response.headers["Expires"] = 0
response.headers["Pragma"] = "no-cache"
return response
# Custom filter
app.jinja_env.filters["usd"] = usd
Session(app)
assets = Environment()
assets.init_app(app)
with app.app_context():
# Import parts of our core Flask app
from . import routes
from .assets import compile_static_assets
# Import Dash application
from .dashboard import init_dashboard
app = init_dashboard(app)
# Compile static assets
compile_static_assets(assets)
return app
in dashboard.init.py file the function init_dashboard need to access the database and the user id, because the dashboard layout need to use values from the database so that it will show the specific dashboard for the user, but of course this does not work as it return the error "Working outside of request context".
If I will try to create the dashboard in routes for example:
#app.route("/dashapp/")
#login_required
def redirect_to_dashapp():
dash_app = dash.Dash(
server=app,
routes_pathname_prefix="/dashapp/")
"""Create a Plotly Dash dashboard."""
stocks = db.session.query(Records.symbol, Records.number_of_shares, Records.purchase_p, Records.execution_time).filter_by(user_id=session["user_id"]).all()
.....
it will return the error "A setup function was called after the first request was handled. "
As the dash layout need values from the database I can't initiate it in init_dashboard() without accessing the db, and I can't initiate in routes either.
Does anyone has a suggestion on how to properly do it?
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.
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
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.
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/