Свикнал съм да разработвам уеб приложения на Django и gunicorn.
В случай на Django, всички модули на приложение в приложение на Django могат да получат настройки за внедряване чрез django.conf.settings. „settings.py“ е написан на Python, така че всякакви произволни настройки и предварителна обработка могат да бъдат дефинирани динамично.
В случай на gunicorn, той има три конфигурационни места по реда на приоритет и един екземпляр на регистър на класа на настройките ги комбинира. (Но обикновено тези настройки се използват само за gunicorn, а не за приложение.)
- Параметри на командния ред.
- Конфигурационен файл. (като Django, написан на Python, който може да има всякакви произволни настройки динамично.)
- Настройки на приложението Paster.
В случай на Pyramid, според документацията на Pyramid, настройките за внедряване обикновено могат да бъдат поставени в pyramid.registry.Registry().settings. Но изглежда, че е достъпен само когато съществува екземпляр на pyramid.router.Router(). Това е pyramid.threadlocal.get_current_registry().settings връща None по време на процеса на стартиране в приложение "main.py".
Например, обикновено дефинирам някаква бизнес логика в модулите на модела SQLAlchemy, което изисква настройки за внедряване, както следва.
myapp/models.py
from sqlalchemy import Table, Column, Types
from sqlalchemy.orm import mapper
from pyramid.threadlocal import get_current_registry
from myapp.db import session, metadata
settings = get_current_registry().settings
mytable = Table('mytable', metadata,
Column('id', Types.INTEGER, primary_key=True,)
(other columns)...
)
class MyModel(object):
query = session.query_property()
external_api_endpoint = settings['external_api_uri']
timezone = settings['timezone']
def get_api_result(self):
(interact with external api ...)
mapper(MyModel, mytable)
Но "settings['external_api_endpoint']" поражда изключение TypeError, тъй като "settings" е None.
Мислех си за две решения.
Дефинирайте извикваем, който приема аргумент "config" в "models.py" и "main.py" го извиква с екземпляр на Configurator().
myapp/models.py
from sqlalchemy import Table, Column, Types from sqlalchemy.orm import mapper from myapp.db import session, metadata _g = globals() def initialize(config): settings = config.get_settings() mytable = Table('mytable', metadata, Column('id', Types.INTEGER, rimary_key = True,) (other columns ...) ) class MyModel(object): query = session.query_property() external_api_endpoint = settings['external_api_endpoint'] def get_api_result(self): (interact with external api)... mapper(MyModel, mytable) _g['MyModel'] = MyModel _g['mytable'] = mytable
Или поставете празен модул "app/settings.py" и поставете настройка в него по-късно.
myapp/__init__.py
from pyramid.config import Configurator from .resources import RootResource def main(global_config, **settings): config = Configurator( settings = settings, root_factory = RootResource, ) import myapp.settings myapp.setting.settings = config.get_settings() (other configurations ...) return config.make_wsgi_app()
И двете, и други решения отговарят на изискванията, но се чувствам обезпокоителен. Това, което искам е следното.
развитие.ini
дефинира груби настройки, тъй като development.ini може да има само константи от типа низ.
[app:myapp] use = egg:myapp env = dev0 api_signature = xxxxxx
myapp/settings.py
дефинира подробни настройки въз основа на development.ini, тъй като могат да се задават всякакви произволни променливи (типове).
import datetime, urllib from pytz import timezone from pyramid.threadlocal import get_current_registry pyramid_settings = get_current_registry().settings if pyramid_settings['env'] == 'production': api_endpoint_uri = 'http://api.external.com/?{0}' timezone = timezone('US/Eastern') elif pyramid_settings['env'] == 'dev0': api_endpoint_uri = 'http://sandbox0.external.com/?{0}' timezone = timezone('Australia/Sydney') elif pyramid_settings['env'] == 'dev1': api_endpoint_uri = 'http://sandbox1.external.com/?{0}' timezone = timezone('JP/Tokyo') api_endpoint_uri = api_endpoint_uri.format(urllib.urlencode({'signature':pyramid_settings['api_signature']}))
След това други модули могат да получат произволни настройки за внедряване чрез „import myapp.settings“. Или, ако Registry().settings е за предпочитане от "settings.py", **settings kwargs и "settings.py" могат да бъдат комбинирани и регистрирани в Registry().settings по време на процеса на стартиране на "main.py".
Както и да е, как да получа речника на настройките по време на стартиране? Или Pyramid внимателно ни принуждава да поставим всеки код, който изисква настройки за разгръщане, в извиквания на "views", които могат да получат речник на настройките по всяко време чрез request.registry.settings?
РЕДАКТИРАНЕ
Благодаря, Майкъл и Крис.
Най-накрая разбирам защо Pyramid използва променливи на нишки (регистър и заявка), по-специално обект на регистър за повече от едно Pyramid приложения.
По мое мнение обаче настройките за внедряване обикновено засягат бизнес логиката, която може да дефинира специфични за приложението неща. Тези логики обикновено се поставят в един или повече модули на Python, които може да са различни от „app/init.py“ или „app/views.py“, които могат лесно да получат достъп до Config() или Registry( ). Тези модули на Python обикновено са „глобални“ на ниво процес на Python.
Това означава, че дори когато съществуват повече от едно Pyramid приложения, въпреки техните собствени нишкови локални променливи, те трябва да споделят онези „глобални“ модули на Python, които може да съдържат специфични за приложението неща на ниво процес на Python.
Разбира се, всеки от тези модули може да има "initialize()" callalbe, който се извиква с Configurator() от "main" callable на приложението, или предаване на Registry() или Request() обект през толкова дълги серии от извиквания на функции може да се срещне с обичайните изисквания. Но предполагам, че начинаещите в Pyramid (като мен) или разработчиците, които имат „голямо приложение или толкова много настройки“, може да се почувстват обезпокоителни, въпреки че това е дизайнът на Pyramid.
Така че мисля, че Registry().settings трябва да има само реални „локални за нишки“ променливи и не трябва да има нормални настройки на бизнес логиката. Отговорността за разделянето на множество специфични за приложението модули, класове, променливи за извикване и т.н. трябва да се поеме от разработчика. Засега, от моя гледна точка, ще приема отговора на Крис. Или в "main" callable, направете "execfile('settings.py', settings, settings)" и го поставете в някакво "глобално" пространство.