Setting Heroku DATABASE_URL to Pyramid Config - python

Goal: Set the Heroku DATABASE_URL variable to sqlalchemy.url=postgres://... settings in __init__.py and development.ini file. Currently, I am connecting directly to the database address (which can change).
Issue as reported by Heroku support:
If you have hard coded the database connection string into your ini
file that is probably not the best idea. While it will work for now,
if sometime in the future we need to change where you database is
running (which does happen for various reasons) then your application
will no longer connect to your database. If your database does move,
we do keep the DATABASE_URL updated so your application should use
this. Maybe change sqlalchemy.url to sqlalchemy.url = os.environ.get('DATABASE_URL') if this is not what it is already set to.
However, the address sqlalchemy.url = os.environ.get('DATABASE_URL') does NOT work. It crashes my app. I have even attempted: sqlalchemy.url = postgresql://os.environ.get('DATABASE_URL'), sqlalchemy.url = postgres://os.environ.get('DATABASE_URL'), and finally sqlalchemy.url = postgres://'DATABASE_URL'. All of which do NOT work.
SQLALCEHMY engine_config setup: docs
sqlalchemy.engine_from_config(configuration, prefix='sqlalchemy.',
**kwargs)
Create a new Engine instance using a configuration dictionary.
The dictionary is typically produced from a config file.
The keys of interest to engine_from_config() should be prefixed, e.g.
sqlalchemy.url, sqlalchemy.echo, etc. The ‘prefix’ argument indicates
the prefix to be searched for. Each matching key (after the prefix is
stripped) is treated as though it were the corresponding keyword
argument to a create_engine() call.
The only required key is (assuming the default prefix) sqlalchemy.url,
which provides the database URL.
A select set of keyword arguments will be “coerced” to their expected
type based on string values. The set of arguments is extensible
per-dialect using the engine_config_types accessor.
Parameters: configuration – A dictionary (typically produced from a
config file, but this is not a requirement). Items whose keys start
with the value of ‘prefix’ will have that prefix stripped, and will
then be passed to create_engine. prefix – Prefix to match and then
strip from keys in ‘configuration’. kwargs – Each keyword argument to
engine_from_config() itself overrides the corresponding item taken
from the ‘configuration’ dictionary. Keyword arguments should not be
prefixed.
Outside Example (doesn't work for me):
I believe the issue is with the way settings and engine are setup in my app. I found this tutorial helpful, but my code is different: Environment Variables in Pyramid
What we ultimately want to do is dynamically set the sqlalchemy.url to
the value of our DATABASE_URL environment variable.
learning_journal/init.py is where our .ini file’s configuration
gets bound to our Pyramid app. Before the current settings get added
to the Configurator, we can use os.environ to bring in our
environment’s DATABASE_URL.
# __init__.py
import os
from pyramid.config import Configurator
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
settings["sqlalchemy.url"] = os.environ["DATABASE_URL"]
config = Configurator(settings=settings)
config.include('pyramid_jinja2')
config.include('.models')
config.include('.routes')
config.scan()
return config.make_wsgi_app()
Because we should always try to keep code DRY (and prevent future confusion), remove the sqlalchemy.url keyword from development.ini.
MY CODE:
init.py
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application"""
#global_config argument is a dictionary of key/value pairs mentioned in the [DEFAULT] section of an development.ini file
# **settings argument collects another set of arbitrary key/value pairs
#The main function first creates a SQLAlchemy database engine using sqlalchemy.engine_from_config() from the sqlalchemy. prefixed settings in the development.ini file's [app:main] section. This will be a URI (something like sqlite://):
engine = engine_from_config(settings, 'sqlalchemy.')
Session.configure(bind=engine)
Base.metadata.bind = engine
...
config.include('pyramid_jinja2')
config.include('pyramid_mailer')
config.add_static_view('static', 'static', cache_max_age=3600)
development.ini
#former db:
#sqlalchemy.url = postgres://localhost/NOTSSdb
#works, but unstable should db move:
sqlalchemy.url = postgres://ijfbcvuyifb.....
initialize_db script:
def main(argv=sys.argv):
if len(argv) < 2:
usage(argv)
config_uri = argv[1]
options = parse_vars(argv[2:])
setup_logging(config_uri)
settings = get_appsettings(config_uri, options=options)
engine = engine_from_config(settings)

Related

Retrieving config from a blueprint in Sanic app

I have a Sanic application, and want to retrieve app.config from a blueprint as it holds MONGO_URL, and I will pass it to a repository class from the blueprint.
However, I could not find how to get app.config in a blueprint. I have also checked Flask solutions, but they are not applicable to Sanic.
My app.py:
from sanic import Sanic
from routes.authentication import auth_route
from routes.user import user_route
app = Sanic(__name__)
app.blueprint(auth_route, url_prefix="/auth")
app.blueprint(user_route, url_prefix="/user")
app.config.from_envvar('TWEETBOX_CONFIG')
app.run(host='127.0.0.1', port=8000, debug=True)
My auth blueprint:
import jwt
from sanic import Blueprint
from sanic.response import json, redirect
from domain.user import User
from repository.user_repository import UserRepository
...
auth_route = Blueprint('authentication')
mongo_url = ?????
user_repository = UserRepository(mongo_url)
...
#auth_route.route('/signin')
async def redirect_user(request):
...
The Sanic way...
Inside a view method, you can access the app instance from the request object. And, therefore access your configuration.
#auth_route.route('/signin')
async def redirect_user(request):
configuration = request.app.config
2021-10-10 Update
There are two newer ways to get to the configuration values (or, perhaps more accuratlely getting the application instance from which you can get the configuration). The first version might be more on point to answering the question of how to get to the config from the blueprint. However, the second option is probably the preferred method since it is precisely intended for this kind of use.
Alternative #1
Blueprints have access to the Sanic applications they are attached to beginning with v21.3.
Therefore, if you have a blueprint object, you can trace that back to the application instance, and therefore also the config value.
app = Sanic("MyApp")
bp = Blueprint("MyBlueprint")
app.blueprint(bp)
assert bp.apps[0] is app
The Blueprint.apps property is a set because it is possible to attach a single blueprint to multiple applications.
Alternative #2
Sanic has a built-in method for retrieving an application instance from the global scope beginning in v20.12. This means that once an application has been instantiated, you can retrieve it using: Sanic.get_app().
app = Sanic("MyApp")
assert Sanic.get_app() is app
This method will only work if there is a single Sanic instance available. If you have multiple application instances, you will need to use the optional name argument:
app1 = Sanic("MyApp")
app2 = Sanic("MyOtherApp")
assert Sanic.get_app("MyApp") is app1
I would suggest a slightly different approach, based on the 12 Factor App (very interesting read which, among others, provides a nice guideline on how to protect and isolate your sensitive info).
The general idea is to place your sensitive and configuration variables in a file that is going to be gitignored and therefore will only be available locally.
I will try to present the method I tend to use in order to be as close as possible to the 12 Factor guidelines:
Create a .env file with your project variables in it:
MONGO_URL=http://no_peeking_this_is_secret:port/
SENSITIVE_PASSWORD=for_your_eyes_only
CONFIG_OPTION_1=config_this
DEBUG=True
...
(Important) Add .env and .env.* on your .gitignore file, thus protecting your sensitive info from been uploaded to GitHub.
Create an env.example (be careful not to name it with a . in the beginning, because it will get ignored).
In that file, you can put an example of the expected configuration in order to be reproducible by simply copy, paste, rename to .env.
In a file named settings.py, use decouple.config to read your config file into variables:
from decouple import config
MONGO_URL = config('MONGO_URL')
CONFIG_OPTION_1 = config('CONFIG_OPTION_1', default='')
DEBUG = config('DEBUG', cast=bool, default=True)
...
Now you can use these variables wherever is necessary for your implementation:
myblueprint.py:
import settings
...
auth_route = Blueprint('authentication')
mongo_url = settings.MONGO_URL
user_repository = UserRepository(mongo_url)
...
As a finisher, I would like to point out that this method is framework (and even language) agnostic so you can use it on Sanic as well as Flask and everywhere you need it!
I think you can create a config.py to save your configuration, just like
config.py
config = {
'MONGO_URL':'127.0.0.1:27017'
}
and use it in app.py
from config import config
mongo_url = config['MONGO_URL']
There is a variable named current_app in Flask. You can use current_app.config["MONGO_URL"].
But I am not familiar with Sanic.

Fixtures for Mongoengine with Django not working

I am using Mongoengine(version: 0.9.0 ) with Django(version: 1.8).
This is my settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.dummy'
}
}
MONGO_DBNAME = "mydatabasename"
MONGO_HOSTNAME = "localhost"
connect(MONGO_DBNAME, host=MONGO_HOSTNAME)
I want to have fixtures for the application. I have created initial_data.json in myapp/fixtures/ location.
When I run the command python manage.py dumpdata , I get the following error :
CommandError: Unable to serialize database: settings.DATABASES is improperly configured. Please supply the ENGINE value. Check settings documentation for more details.
Questions:
1) Any workaround for this problem ?
2) Is there any other way to load the initial data ?
References at this link
Thank you
Mongoengine itsn a backend(in django terminology). Its has own models (schemas) and DOM (like ORM in docuemnt db's) but it dont have a Django backend adapters.
You can use it. But there is issue while workind with out-of-box Django solution like Tests, Fixtures, etc.
You need to write your own loader, sadenly but true.
I see 2 options here:
You can try to use Django MongoDB Engine
You can write your own loader for mongodb
Ill write my own fixture loader for tests.
I have a json file where mapped all fixture file ill need to load to db.
So a fast example here:
import bson
import os
from django.conf import settings
from mongoengine.connection import get_db
def _get_db(self):
self.db = get_db()
def _load_fixtures(self, clear_before_load=True):
"""
Load to db a fixtures from folder fixtures/{{DB_NAME}}/{{COLLECTION_NAME}} before each test.
In file fixtures.json mapped collection name and file name for it.
"""
fixture_path = lambda file_name: os.path.join(settings.FIXTURES_DIR, self.db.name, file_name)
with open(settings.COLLECTION_FIXTURES_PATH) as file_object:
db_collections = loads(file_object.read())
for collection_name, filename in db_collections.items():
collection = self.db[collection_name]
if clear_before_load:
collection.remove()
path = fixture_path(filename)
if os.path.exists(path) and os.path.isfile(path):
with open(path, 'r') as raw_data:
collection_data = bson.decode_all(raw_data.read())
for document in collection_data:
collection.save(document)
There is no support for fixtures on mongoengine, and I don't think the mongoengine team is continuing the plugin as of version 0.9.0.
What I ended up doing to load initial data for mongoDB is to create a script called startup.py in my project folder.
startup.py:
from {{app}}.models import Sample
def init():
if Sample.objects(name="test").count() == 0: # a flag to prevent initial data repetition
Sample(name="test").save()
Next is to run this script on Django's startup. The entry point of Django project is when DJANGO_SETTINGS_MODULE is first loaded at wsgi.py:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{{project_name}}.settings")
import {{project_name}}.startup as startup
startup.init()
application = get_wsgi_application()
With this setup, when you run python manage.py runserver, the init() on startup.py will run and the data you set will be inserted to the DB.
Hope this helps.

How to add your app specific settings in pyramid rest framework?

I am new to pyramid.
The Issue is I am not able to figure out how app specific settings (key value pairs) work in pyramid.
This is what I have done after various google searches and other stackoverflow answers:
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
if '__file__' in global_config:
settings.update(
load_sensitive_settings(global_config['__file__'], global_config))
config = Configurator(settings=settings)
config.include('pyramid_chameleon')
# config.add_static_view('static', 'static', cache_max_age=3600)
# config.add_route('home', '/')
config.add_route(
'tags',
'/tags', request_method='POST', accept='application/json', )
config.scan()
return config.make_wsgi_app()
def load_sensitive_settings(configurationPath, defaultByKey):
'Load sensitive settings from hidden configuration file'
# configFolder, configName = os.path.split(configurationPath)
# sensitivePath = os.path.join(configFolder, '.' + configName)
sensitivePath = configurationPath
settings = {}
configParser = ConfigParser.ConfigParser(defaultByKey)
if not configParser.read(sensitivePath):
log.warn('Could not open %s' % sensitivePath)
return settings
settings.update(configParser.items('custom'))
return settings
I have a file where I try to fetch settings like this:
from pyramid.threadlocal import get_current_registry
settings = get_current_registry().settings
value = settings['my_key']
But I always get settings object as None.
This is how I am defining my custom settings in development.ini
[custom]
my_key = ''
This is how I start my server in develpoment
pserve development.ini
I have read that request.settings can give me settings, But that approach is not feasible for me as my key contains the name of a file which is 1.5GBs and it has to be present in memory all the time. It takes around 5 minutes to load that file in server, hence cannot load the file on demand.
Please advice.
Thanks a lot for all the help in advance.
Update:
Thanks to all the answers provided, I finally made it work.
This is how my main function looks like:
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
config = Configurator(settings=settings)
config.include('pyramid_chameleon')
if '__file__' in global_config:
init_config(global_config['__file__'])
And I made a config file, this is how my config file looks like:
import ConfigParser
settings = dict()
def init_config(filename):
config = ConfigParser.ConfigParser()
config.read(filename)
settings_dict = config.items('custom')
settings.update(settings_dict)
Now wherever I want settings, I just do:
from projectname.config import settings
settings.get('my_key')
And I put my app specific settings (development/production.py) like this
[custom]
my_key = value
Regards
HM
Easiest way is putting your settings to the app main section with dot separated names. Example:
[app:main]
websauna.site_name = Trees
websauna.site_tag_line = Enjoy
websauna.site_url = http://localhost:6543
websauna.site_author = Trees team
Then you can do:
my_settings_value = request.registry.settings.get("websauna.site_name", "Default value)
WSGI pipeline does not bring you settings from other sections and you need to reparse the INI file with ConfigParser if you want to access the other sections (as far as I know).
If you need to load a lot of data during development time just store a filename in settings and load the file when you need to access the data, so that you don't slow the web server startup.
Here is my working solution:
config.ini
[APP.CONFIG]
url = http://....
[SMTP.CONFIG]
smtp.server = ...
smtp.port = 25
smtp.login = ...
smtp.password = ...
smtp.from = ...
[DB.CONFIG]
db.database=...
db.host=...
db.port=..
db.user=...
db.password=...
config.py
import configparser
config = configparser.ConfigParser()
config._interpolation = configparser.ExtendedInterpolation()
config.read(encoding='utf-8', filenames=['path to file/config.ini'])
smtp = config['SMTP.CONFIG']
db = config['DB.CONFIG']
mail = config['APP.CONFIG']
And how i use it in APP
from config import db
host = db['db.host']
If, like me, you are using PasteDeploy with Pyramid, the Pyramid docs here explain how you can use a [DEFAULT] section in your .ini configuration file to hold your custom parameters.
You might also benefit from reading the documentation on .ini files, since it gives some snippets which make it all much clearer.

Reusing paste config entry

I have the following Pyramid .ini file:
[DEFAULT]
redis.host = localhost
redis.port = 6379
redis.db = 0
[app:main]
...
# beaker session
session.type = redis
session.url = localhost:6379
In the app:main section's session.url I want to use what's defined under DEFAULT section's redis.host and redis.port.
In my understanding everything under DEFAULT section is global and is passed to other sections. But if I want to reuse a settings from DEFAULT and assign it a different name under other sections how do I do that?
I'm looking at the same way I can reference section entry in buildout .cfg files using ${<section name>:<entry>}.
session.url = %(redis.host)s:%(redis.port)s
Should do the trick.

Implementing Sqlalchemy beaker caching in pyramid framework

As per the example provided by sqlalchemy documentation to cache a sqlalchemy query we are suppose to do this
from caching_query import FromCache
# load Person objects. cache the result under the namespace "all_people".
print "loading people...."
people = Session.query(Person).options(FromCache("default", "all_people")).all()
I have the following configuration for beaker in development.ini
cache.regions = day, hour, minute, second
cache.type = file
cache.data_dir = %(here)s/cache/sess/data
cache.lock_dir = %(here)s/cache/sess/lock
cache.second.expire = 1
cache.minute.expire = 60
cache.hour.expire = 3600
cache.day.expire = 86400
When i use the above example code in my application data is not cached in the cache folder, so i am assuming memory based caching is the default, Is it possible to switch sqlalchemy cache type to file based? or am i getting it wrong?
Your question is missing some details, but let me try:
the first parameter passed to FromCache() is a name of a Beaker cache region, it should match one of the configured regions, which is not the case here. Or perhaps you configure default region in the code (I'd expect BeakerException being thrown if region is unknown)?
you need pyramid_beaker module installed and included in Pyramid's project configuration. I suggest you follow pyramid_beaker manual's Setup section.
you need some extra code in __init__.py of your application in order to propagate .ini file settings to Beaker. This is described in Beaker cache region support section of the manual.
And here's a working sample from my current project, configuring both Beaker-based sessions and caching (all irrelevant parts removed):
from pyramid.config import Configurator
from pyramid_beaker import set_cache_regions_from_settings
from pyramid_beaker import session_factory_from_settings
def main(global_config, **settings):
# Configure Beaker caching/sessions
set_cache_regions_from_settings(settings)
session_factory = session_factory_from_settings(settings)
config = Configurator(settings=settings)
config.set_session_factory(session_factory)
config.include('pyramid_beaker')
config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('home', '/')
config.scan()
return config.make_wsgi_app()

Categories