Transferring django settings to Environment Variables - django

I set the django project settings and added values to environment variables at my local ubuntu mashine and AWS Ubuntu server using .bashrc file at the root folder.
...
export DEBUG="True"
...
settings.py
...
SECRET_KEY = os.environ.get('SECRET_KEY')
DEBUG = os.environ.get('DEBUG', False)
...
At local all working good, but at production server values are not importing. Why doesn't it work on both machines? How do I set up production?
Im running production server using asgi server daphne, accordingly this tutorial
upd
asgi.py
import os
import django
from channels.routing import get_default_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myapp.settings")
django.setup()
application = get_default_application()

So, what I usually do is: I have a script which runs when Django is setup and creates a .env file with some values.
project_setup.py
import os
import random
import string
def write_dot_env_file(env_file):
settings = get_settings()
with open(env_file, 'w') as f:
for k, v in settings.items():
f.write(f'{k.upper()}={v}\n')
def get_settings():
return {
'SECRET_KEY': generate_secret_key(),
'DEBUG': True,
'ALLOWED_HOSTS': '*',
}
def generate_secret_key():
specials = '!##$%^&*(-_=+)'
chars = string.ascii_lowercase + string.digits + specials
return ''.join(random.choice(chars) for _ in range(50))
def main():
env_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), '.env')
if not os.path.isfile(env_file):
write_dot_env_file(env_file)
else:
pass
if __name__ == '__main__':
main()
Then on my manage.py and wsgi.py just before Django setting the path of the settings you do:
manage.py
#!/usr/bin/env python
import os
import sys
from project_setup import main as project_setup_main
if __name__ == '__main__':
project_setup_main()
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'yoursettings')
...
And that's it. When Django runs it'll create a .env file for you with the minimum requirements. Or you can just create the function generate_secret_key inside your settings.py and use it as a default for your SECRET_KEY declaration, like: SECRET_KEY = os.environ.get('SECRET_KEY', get_secret_key())

Related

How to determine flask_app name

Sorry if this is a bit basic but wanted to validate what my flask_app name would be that I set in my .env file when running locally.
I run my app using a wsgi.py file in root with the following contents:
from app import create_app, db
application = create_app()
if __name__ == '__main__':
application.run()
However I then have a app/init.py
import os
from config import Config
from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
#not sure if i mucked this up
db.init_app(app)
from app.main import main_blueprint
app.register_blueprint(main_blueprint)
return app
Which I run with flask run. Therefore is my app name “app” or “application” or even something else?
You want flask_app variable to be the name of the file which runs the app, so for you in your .env file it looks like you should have:
FLASK_APP = run.py
where run.py is the name of the file containing the first block of code in your question, assuming that your .env file is also in your root folder, let me know if that helps.

Flask __init__.py structure on Heroku

from .library.routes import library
2022-05-25T11:06:23.522123+00:00 app[web.1]: ImportError: attempted relative import with no known parent package
This is the log shown by heroku, and I encountered this problem again and again and again.
How could I solve this and deploy my site on heroku?
init.py
from flask import Flask
from .library.routes import library
from .chat.routes import chat
from .extensions import db, bcrypt, login, socketio
from .api.library_api import library_api
from .api.chat_api import chat_api
from .models.chat import user
from .user.routes import user_route
from .api.login_api import login_api
from .api.socket_api import socket_api
def create_app():
app = Flask(__name__)
app.secret_key = "Why would I show it to stackoverflow?"
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///library.sqlite3"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config["SQLALCHEMY_BINDS"] = {
'chat': "sqlite:///chat.sqlite3",
"library": "sqlite:///library.sqlite3"
}
db.init_app(app)
bcrypt.init_app(app)
login.init_app(app)
socketio.init_app(app)
login.blueprint_login_views = {
'chat': '/login',
'library': '/login'
}
with app.app_context():
db.create_all()
app.register_blueprint(library)
app.register_blueprint(chat)
app.register_blueprint(library_api)
app.register_blueprint(chat_api)
app.register_blueprint(user_route)
app.register_blueprint(login_api)
app.register_blueprint(socket_api)
return app
Procfile
web: gunicorn __init__:app --log-file=-
web: gunicorn --worker-class eventlet -w 1 __init__:app
This init.py is really hindering my development (especially when it comes to debugging due to it running 1000% slower than using a one big app.py) but I want to keep the blueprint approach for ease of organization

Django os.getenv('SECRET_KEY') throwing "The SECRET_KEY setting must not be empty."

I'm setting up Django using os.getenv to prepare it for deploying using Docker but it seems it is not reading the .env file. Any idea why is not reading it?
Here is the setup:
.env
SECRET_KEY=foo
DEBUG=True
ALLOWED_HOSTS=localhost,127.0.0.1
settings.py abstraction
import os
from pathlib import Path
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SECRET_KEY = os.getenv('SECRET_KEY')
DEBUG = os.getenv('DEBUG')
ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS')
You can use python-decouple to get the environment variable stored in the root of your project in a .env file.
from decouple import config
SECRET_KEY = config('SECRET_KEY')
DEBUG = config('DEBUG', default=False, cast=bool)
EMAIL_HOST = config('EMAIL_HOST', default='localhost')
EMAIL_PORT = config('EMAIL_PORT', default=25, cast=int)
Note: for changes to apply you need to restart the server.
I'm using python-dotenv in order to implement dotenv functionality. If you want Django to find your .env file, you need to modify manage.py and wsgi.py files.
# manage.py
import os
import sys
import dotenv
def main():
"""Run administrative tasks."""
# dotenv settings
dotenv.load_dotenv(
os.path.join(os.path.dirname(__file__), '.env')
)
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
if os.getenv('DJANGO_SETTINGS_MODULE'):
os.environ['DJANGO_SETTINGS_MODULE'] = os.getenv('DJANGO_SETTINGS_MODULE')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()
# wsgi.py
import os
import dotenv
from django.core.wsgi import get_wsgi_application
# dotenv settings
dotenv.load_dotenv(
os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')
)
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
if os.getenv('DJANGO_SETTINGS_MODULE'):
os.environ['DJANGO_SETTINGS_MODULE'] = os.getenv('DJANGO_SETTINGS_MODULE')
application = get_wsgi_application()
When deploying via docker-compose, you can specify in the docker-compose file.bml in the container settings [web]:
web:
...
env_file:
- ./.env
It worked for me without using additional packages. In my case , the file .env is located in the directory where docker-compose.yml is located
To get environment variables from docker or from the AWS Elastic Beanstalk i use
os.environ.get('SECRET_KEY'), this is generally more reliable than os.environ['SECRET_KEY']

Flask failed to load configuration from config.py

My __init__.py file
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import app_config
db = SQLAlchemy()
def create_app(config_name):
app = Flask(__name__, instance_relative_config=True)
app.config.from_object(app_config[config_name])
app.config.from_pyfile('config.py')
db.init_app(app)
#app.route('/')
def index():
return 'Hello, user'
return app
I will find tutorial for flask
build crud app flask
My config.py file:
import os
# Set path for our db
basedir = os.path.dirname(os.path.abspath(__file__))
class Config:
DEBUG = True
class DevConfig(Config):
DEBUG = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir,
'bookshell.db')
SQLALCHEMY_ECHO = True
SQLALCHEMY_TRACK_MODIFICATIONS = False
class ProdConfig(Config):
DEBUG = False
class TestConfig(Config):
DEBUG = True
app_config = {
'development': DevConfig,
'production': ProdConfig,
'testing': TestConfig
}
run.py file
import os
# Import from app/__init__
from app import create_app
config_name = os.getenv('FLASK_CONFIG', 'development')
app = create_app(config_name)
if __name__ == '__main__':
app.run()
So, I tried to configure my own config.py file, but I get an error
FileNotFoundError: [Errno 2] Unable to load configuration file (No such file or directory): '/Users/yevhensurzhenko/Desktop/Simple_login_form/instance/config.py'
But when I change some configuration, and when try to reload page it give me
405 error method not allowed
or
app.config.from_object(app_config[config_name])
KeyError: <flask.cli.ScriptInfo object at 0x1038a5b70>
So, I made some changes, like
In config.py add
app_config = {
..
'default': DevConfig
}
In __init__.py create instance dir.
from instance import config
And now I get output from func that I create in __init__.py, in create__app()...
#app.route('/')
def index():
return 'Hello, user'
But, FLASK_CONFIG not change, it work only production, wtf:(

Flask - Attempted relative import in non-package

Following this tutorial on how to structure a Flask app, I have:
project/
__init__.py
app.py
models/
__init__.py
base.py
base.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
models/__init__.py
from .base import db
def init_app(app):
db.init_app(app)
project/__init__.py
from flask import Flask
def create_app()
from . import models, routes, services
app = Flask(__name__)
models.init_app(app)
# routes.init_app(app)
# services.init_app(app)
return app
finally, in app.py, I try to run it:
from . import create_app
app = create_app()
if __name__ == '__main__':
app.run(use_reloader=True, threaded=True, debug=True)
but I'm getting the error:
from . import create_app
ValueError: Attempted relative import in non-package
Am I building it right, what am I doing wrong?
I guess you are running your program by:
python project/app.py
In this case, you are not treat your "project" as a python package, which will raise the error you got. Instead, you can run your project with:
FLASK_APP=project.app flask run