Okay now, I have
settings.py
SETTING1='value'
SETTING2='value'
After that we realized that these settings SETTING1 and SETTING2 are more specified to app1
So we've added them apps.py
from django.apps import AppConfig
class EXAPPConfig(AppConfig):
name = 'EXAPPConfig'
verbose_name = "EXAPPConfig"
SETTING1 = 'value'
def ready(self):
pass
and call them withviews.py
app_settings = apps.get_app_config('ex_app')
app_settings.SETTING1
according to Django documentation
So how can I override them with override_settings at tests
#override_settings
I tried #patch to patch the config app but failed
You can mock only an attribute of your app config with this:
from unittest.mock import patch
from django.apps import apps
from django.test import TestCase
class EXAPPTest(TestCase):
def test_mocking_app_config(self):
original = apps.get_app_config('ex_app').SETTING1
with patch.object(apps.get_app_config('ex_app'), 'SETTING1', new='definitely-not-original'):
mocked = apps.get_app_config('ex_app').SETTING1
self.assertNotEqual(original, mocked)
Related
Hello everyone I have this issue I am not able to correct:
KeyError: 'A secret key is required to use CSRF.'
I am now using Flask with Blueprint.
I am not using CSRF at all but I think the LoginForm is.
I structured my project with Blueprint.
Before that, everything was find.
Here is my init.py file:
from flask import Flask
from flask_login import LoginManager
from flask_bcrypt import Bcrypt
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
from flask.config import Config
from flask_wtf.csrf import CSRFProtect
db = SQLAlchemy()
migrate = Migrate(db)
bcrypt = Bcrypt()
csrf = CSRFProtect()
login_manager = LoginManager()
login_manager.login_view = "login"
login_manager.login_message_category = "info"
from Flask import models
from Flask.models import User
admin = Admin(name='Admin')
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
admin.init_app(app)
db.init_app(app)
csrf.init_app(app)
login_manager.init_app(app)
migrate.init_app(app)
bcrypt.init_app(app)
db.init_app(app)
from Flask.users.routes import users
app.register_blueprint(users)
return app
This is my config.py file:
import os
class Config:
SECRET_KEY = "ef2006629e09b70e55a6fb95c4e3a538"
SQLALCHEMY_DATABASE_URI = "sqlite:///site.db"
# WTF_CSRF_SECRET_KEY= "bjk567nvhbvj63vg363vghvghv3768vgfbkijvr784"
# CSRF_ENABLED = True
Thank you for your help !
You should create a SECRET_KEY=<Your secret key here> property in your configuration. It must be a difficult string.
I found the problem
I was not calling properly my config.py file
In my init.py file I change the line:
from flask.config import Config
with the line:
from Flask.config import Config
Flask is the name of my file, which is different from flask.
I should have found another name
I am trying to use a custom Baseconverter to ensure that my urls are "clean". I finally got it to work as follows:
My init.py is:
import os
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.login import LoginManager
from flask.ext.openid import OpenID
from config import basedir
from slugify import slugify
app = Flask(__name__)
from werkzeug.routing import BaseConverter
class MyStringConverter(BaseConverter):
def to_python(self, value):
return value
def to_url(self, values):
return slugify(values)
app.url_map.converters['mystring'] = MyStringConverter
app.config.from_object('config')
db = SQLAlchemy(app)
lm = LoginManager()
lm.init_app(app)
lm.login_view = 'login'
oid = OpenID(app, os.path.join(basedir, 'tmp'))
from app import views, models
But if I define the MyStringConverter class and add app.url_map.converters['mystring'] = MyStringConverter at the end of the file instead, I got a LookupError: the converter 'mystring' does not exist error. Why is this?
Is this the correct way to clean up my urls? I'm not sure if I need to do anything to the to_python() method or if I am going about this the right way.
for example:
from flask import Flask
from flask.ext.admin import Admin, BaseView, expose
class MyView(BaseView):
#expose('/')
def index(self):
return self.render('index.html')
app = Flask(__name__)
admin = Admin(app)
admin.add_view(MyView(name='Hello'))
app.run()
but, if I need a new file, called 'views.py', how can I add a view into views.py to admin?
Do I need to use a blueprint?
For my project I actually made a child class of Blueprint that supports flask admin:
from flask import Blueprint
from flask_admin.contrib.sqla import ModelView
from flask_admin import Admin
class AdminBlueprint(Blueprint):
views=None
def __init__(self,*args, **kargs):
self.views = []
return super(AdminBlueprint, self).__init__('admin2', __name__,url_prefix='/admin2',static_folder='static', static_url_path='/static/admin')
def add_view(self, view):
self.views.append(view)
def register(self,app, options, first_registration=False):
admin = Admin(app, name='microblog', template_mode='adminlte')
for v in self.views:
admin.add_view(v)
return super(AdminBlueprint, self).register(app, options, first_registration)
For details you may like to read my blog here: http://blog.sadafnoor.me/blog/how-to-add-flask-admin-to-a-blueprint/
I am very late for this question, but anyway... My guess is that you want to use the Application Factory pattern and use the Flask-Admin. There is a nice discussion about the underlying problems. I used a very ugly solution, instantiating the Flask-Admin in the init.py file:
from flask_admin.contrib.sqla import ModelView
class UserModelView(ModelView):
create_modal = True
edit_modal = True
can_export = True
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
db.init_app(app)
# import models here because otherwise it will throw errors
from models import User, Sector, Article
admin.init_app(app)
admin.add_view(UserModelView(User, db.session))
# attach blueprints
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint, url_prefix='/auth')
return app
You don't need a blueprint for that. In views.py add an import for the admin object you defined in your main project:
from projectmodule import admin
from flask.ext.admin import BaseView, expose
class MyView(BaseView):
#expose('/')
def index(self):
return self.render('index.html')
admin.add_view(MyView(name='Hello'))
and in your main projectmodule file use:
from flask import Flask
from flask.ext.admin import Admin
app = Flask(__name__)
admin = Admin(app)
# import the views
import views
app.run()
e.g. you add import views after the line that sets admin = Admin(app).
I have an flask app with one blueprint (and login/logout to admin).
This is the best solution I found to implement flask admin with some custom features.
My structure as follows:
my_app
main
__init__.py
routes.py
static
templates
__init__.py
config.py
models.py
run.py
Customized admin index view from models.py
from flask_admin import AdminIndexView
class MyAdminIndexView(AdminIndexView):
def is_accessible(self):
return current_user.is_authenticated
def inaccessible_callback(self, name, **kwargs):
return redirect(url_for('main.home'))
Main init.py as follows:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_admin import Admin
from flask_admin.menu import MenuLink
from my_app.config import Config
# create extensions
db = SQLAlchemy()
admin = Admin()
def create_app(config_class=Config): # default configutation
app = Flask(__name__)
app.config.from_object(Config)
#initialize extensions
db.init_app(app)
...
# customized admin views
from my_app.models import MyAdminIndexView
admin.init_app(app,index_view=MyAdminIndexView())
admin.add_link(MenuLink(name='Home', url='/'))
admin.add_link(MenuLink(name='Logout', url='/logout'))
#blueprint
from my_app.main.routes import main
app.register_blueprint(main)
return app
I think this is the most elegant solution I came up so far.
In order to keep clean the __init__.py root file:
# app/__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from app.config import Config
from flask_admin import Admin
db = SQLAlchemy()
admin = Admin(template_mode="bootstrap3")
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
db.init_app(app)
admin.init_app(app)
from app.admin import bp
app.register_blueprint(bp, url_prefix='/admin')
return app
Then in the __init__.py of the Blueprint admin app
# app/admin/__init__.py
from flask import Blueprint
from app import admin, db
from app.models import User
from flask_admin.contrib.sqla import ModelView
bp = Blueprint('admin_app', __name__)
admin.name = 'Admin panel'
admin.add_view(ModelView(User, db.session))
# all the rest stuff
I am starting to write tests for a Django app, which relies on several environment variables. When I am testing it in the shell, I can import os and specify the variables and my tests work just fine. However, when I put them into tests.py, I still get a key error because those variables are not found. here's what my test looks like:
from django.utils import unittest
from django.test.utils import setup_test_environment
from django.test.client import Client
import os
os.environ['a'] = 'a'
os.environ['b'] = 'b'
class ViewTests(unittest.TestCase):
def setUp(self):
setup_test_environment()
def test_login_returning_right_template(self):
""" get / should return login.html template """
c = Client()
resp = c.get('/')
self.assertEqual(resp.templates[0].name, 'login.html')
Is this the wrong place to initialize those variables? I tried to do it on setUp, but with the same result - they are not found. Any suggestions on how to initialize environment variables before running a test suite?
Thanks!
Luka
You should not relay on os.envior in your views. If you have to, do It in your settings.py
MY_CUSTOM_SETTING = os.environ.get('a', 'default_value')
And in views use settings variable:
from django.conf.settings import MY_CUSTOM_SETTING
print MY_CUSTOM_SETTING
Then in your test you can set this setting:
from django.test import TestCase
class MyTestCase(TestCase):
def test_something(self):
with self.settings(MY_CUSTOM_SETTING='a'):
c = Client()
resp = c.get('/')
self.assertEqual(resp.templates[0].name, 'login.html')
Based on Django's documentation I was reading, it seems like signals.py in the app folder is a good place to start with, but the problem I'm facing is that when I create signals for pre_save and I try to import the class from model it conflicts with the import in my model.
# models.py
from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import gettext as _
from signals import *
class Comm_Queue(CommunicatorAbstract):
queue_statuses = (
('P', _('Pending')),
('S', _('Sent')),
('E', _('Error')),
('R', _('Rejected')),
)
status = models.CharField(max_length=10, db_index=True, default='P')
is_html = models.BooleanField(default=False)
language = models.CharField(max_length=6, choices=settings.LANGUAGES)
sender_email = models.EmailField()
recipient_email = models.EmailField()
subject = models.CharField(max_length=100)
content = models.TextField()
# signals.py
from django.conf import settings
from django.db.models.signals import pre_save
from django.dispatch import receiver
from models import Comm_Queue
#receiver(pre_save, sender=Comm_Queue)
def get_sender_email_from_settings(sender, **kwargs):
obj=kwargs['instance']
if not obj.sender_email:
obj.sender_email='%s' % settings.ADMINS[0][1]
This code will not run because I import Comm_Queue inside signals.py and I also import the signals inside models.py.
Can anyone advice on how I could over come this issue?
Regards
If you're using Django<=1.6 I'd recommend Kamagatos solution: just import your signals at the end of your models module.
For future versions of Django (>=1.7), the recommended way is to import your signals module in your app's config ready() function:
my_app/apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = 'my_app'
def ready(self):
import my_app.signals
my_app/__init__.py
default_app_config = 'my_app.apps.MyAppConfig'
Original answer, for Django < 1.7:
You can register the signals by importing signals.py in the app's __init__.py file:
# __init__.py
import signals
This will allow to import models.py from signals.py without circular import errors.
One problem with this approach is that it messes up the coverage results if you're using coverage.py.
Related discussion
Edit: For Django >= 1.7:
Since AppConfig was introduced, the recommended way of importing signals is in its init() function. See Eric Marcos' answer for more details.
To solve your problem you just have to import signals.py after your model definition. That's all.
I also put signals in signals.py file and also have this code snippet that loads all signals:
# import this in url.py file !
import logging
from importlib import import_module
from django.conf import settings
logger = logging.getLogger(__name__)
signal_modules = {}
for app in settings.INSTALLED_APPS:
signals_module = '%s.signals' % app
try:
logger.debug('loading "%s" ..' % signals_module)
signal_modules[app] = import_module(signals_module)
except ImportError as e:
logger.warning(
'failed to import "%s", reason: %s' % (signals_module, str(e)))
This is for project, I'm not sure if it works at app level.
In old Django versions would be fine to put the signals on the __init__.py or maybe in the models.py(although at the end models will be way to large for my taste).
With Django 1.9, it is better I think, to place the signals on a signals.py file and import them with the apps.py, where they are going to be loaded after loading the model.
apps.py:
from django.apps import AppConfig
class PollsConfig(AppConfig):
name = 'polls'
def ready(self):
from . import signals # NOQA
You can also divide your signals on signals.py and handlers.py in another folder within your model named signals as well, but for me that is just over engineering. Take a look at Placing Signals
This only applies if you have your signals in a separate signals.py file
In completely agree with the answer of #EricMarcos but it should be stated that the django docs explicitly advice not to use the default_app_config variable (although it is not wrong). For current versions, correct way would be:
my_app/apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = 'my_app'
def ready(self):
import my_app.signals
settings.py
(Make sure you don't just have your app name in installed apps but instead the relative path to your AppConfig)
INSTALLED_APPS = [
'my_app.apps.MyAppConfig',
# ...
]
I'm guessing that you're doing that so your signals are registered, so that they're found somewhere. I just put my signals right in a models.py file normally.
An alternative is to import the callback functions from signals.py and connect them in models.py:
signals.py
def pre_save_callback_function(sender, instance, **kwargs):
# Do stuff here
model.py
# Your imports here
from django.db.models.signals import pre_save
from yourapp.signals import pre_save_callback_function
class YourModel:
# Model stuff here
pre_save.connect(pre_save_callback_function, sender=YourModel)
Ps: Importing YourModel in signals.py will create a recursion; use sender, instead.
Ps2: Saving the instance again in the callback function will create a recursion. You can make a control argument in .save method to control it.