Django cannot call migrate after makemigrations utilizing call_command - django

I am using django 1.11 on python 3.6.
The easiest way I can explain my problem is that I am creating a test database on the fly in my code. I do this with the following script:
from uuid import uuid4 as uuid
db = '_' + uuid().hex + 'db.sqlite3'
os.environ.setdefault('DJANGO_TEST_DB_NAME', db)
print(db)
import django
from django.apps import apps
from django.conf import settings
from django.core.management import call_command
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")
django.setup()
print("Setting up test database\n")
print("Clearing migrations...\n")
_dir = os.getcwd()
_search = ['project', 'email_auth', 'google', 'app2', 'app3']
for _d in _search:
_walk = os.walk(_dir + os.path.sep + _d)
for _walkObj in _walk:
if _walkObj[0].split(os.path.sep)[-1] == 'migrations' or \
(_walkObj[0].split(os.path.sep)[-2] == 'migrations'
and _walkObj[0].split(os.path.sep)[-1] == '__pycache__'):
for _fName in _walkObj[2]:
os.remove(_walkObj[0] + os.path.sep + _fName)
print("Calling manage:makemigrations\n")
call_command('makemigrations', *_search[1:])
apps.clear_cache()
apps.populate(settings.INSTALLED_APPS)
print("Calling manage:migrate...\n")
call_command('migrate')
print("Creating objects...\n")
call_command('create_objects')
print("Starting server...\n")
call_command('runserver')
No matter what I do to the apps object (I tried some hacky things to clear out everything inside of it to reload, but no dice) or anything else I cannot get django to realize that there are migrations created for any of my apps when calling migrate.
I have even attempted just calling into migrate, email_auth and it states that django.core.management.base.CommandError: App 'email_auth' does not have migrations.
I can call migrate outside of this script if I cancel it after the makemigrations portion and it migrates just fine.
I strongly suspect it's not working because somewhere django has the old references to the modules and I have no idea how to update them.
Edit:
Just so there is proof that it is indeed migrating, I uploaded a paste bin of console output: https://pastebin.com/aSructTW

I finally figured out how to do this properly. Please note that there is more to getting the test database to be created/used, however this question's scope is for reloading the apps. If anyone wants code for that let me know. Here is the function to clear out apps:
def resetApps(search=[]):
from collections import OrderedDict, defaultdict
import os
import sys
from django.apps import apps
from django.conf import settings
apps.clear_cache()
apps.all_models = defaultdict(OrderedDict)
apps.app_configs = OrderedDict()
apps.stored_app_configs = []
apps.apps_ready = apps.models_ready = apps.ready = False
apps._pending_operations = defaultdict(list)
# clear out py caches of apps as we need to reload
# modify this to allow removal of .pyc files that aren't in pycache, btw
for _d in search:
_walk = os.walk(_dir + os.path.sep + _d)
for _walkObj in _walk:
if _walkObj[0].split(os.path.sep)[-1] == '__pycache__':
for _fName in _walkObj[2]:
os.remove(_walkObj[0] + os.path.sep + _fName)
# clear out sys modules of required things
_toClear = []
for module in sys.modules:
if module.split('.')[0] in search:
_toClear.append(module)
for module in _toClear:
del sys.modules[module]
apps.populate(settings.INSTALLED_APPS)

Related

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

Not able to inspect Django migrations from test but working from Django command

I want to be able to inspect Django migrations within a test, so I can check basic sanity before running more expansive tests.
I was able to come up with the following in a Django command:
from django.core.management.base import BaseCommand
from django.db.migrations.loader import MigrationLoader
class Command(BaseCommand):
help = "Check migrations"
def handle(self, *args, **options):
self.migration_loader = MigrationLoader(None)
self.migration_loader.build_graph()
for root_node in self.migration_loader.graph.root_nodes():
app_name = root_node[0]
print(app_name, root_node[0])
But when I translate that into a test (using pytest):
from django.db.migrations.loader import MigrationLoader
def test_multiple_leaf_nodes_in_migration_graph():
migration_loader = MigrationLoader(None)
migration_loader.build_graph()
for root_node in migration_loader.graph.root_nodes():
app_name = root_node[0]
print(app_name, root_node[0])
Then the graph (root nodes) returns an empty list.
The project is structured as follows:
django_project/
settings.py
... # other Django core files
tests/
test_above.py
django_app/
models.py
... # other files from this app
management/commands/
command_above.py
pytest.ini
pytest.ini:
[pytest]
DJANGO_SETTINGS_MODULE = django_project.settings
python_files = tests.py test_*.py
The command:
pytest --no-migrations
PS: the whole point of this is to be able to detect migration errors without running them.
Is there anything I need to do so I can "see" migrations within the test?
pytest --no-migrations
Since you disabled migrations, the MigrationLoader won't load the migrations.
To load the migrations, you can override settings for MIGRATION_MODULES just for that test:
from django.test import override_settings
#override_settings(MIGRATION_MODULES={}) # Add this
def test_multiple_leaf_nodes_in_migration_graph():
...

Transferring django settings to Environment Variables

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())

Import Erro Flask Framework

I'm trying to build a CRUD API with product and user and JWT authentication. When I try to run it shows the error -> "ImportError:cannot import name 'Item'" Could you help me. Thank you
from flask import Flask
from flask_jwt import JWT
from flask_restful import Api
from flask_sqlalchemy import SQLAlchemy
from resources.item import Item, ItemList
from resources.user import UserRegister
from security import authenticate, identity
app = Flask(__name__)
api = Api(app)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['PROPAGATE_EXCEPTIONS'] = True
app.config['SECRET_KEY'] = 'luciano'
db = SQLAlchemy(app)
#app.before_first_request
def create_tables():
db.create_all()
jwt = JWT(app, authenticate, identity)
api.add_resource(Item, "/item/<string:nome>")
api.add_resource(ItemList, "/items")
api.add_resource(UserRegister, "/register")
if __name__ == '__main__':
from db import db
db.init_app(app)
app.run(debug=True)
In the terminal I get the following error
Traceback (most recent call last):
File "/home/luciano/Documentos/Api/app.py", line 5, in <module>
from resources.item import Item, ItemList
ImportError: cannot import name 'Item'
My folder structure
Api
/models
item.py
user.py
/resources
item.py
user.py
app.py
security.py
Add init.py which is missing in your directories
Why it is required
In addition to labeling a directory as a Python package and defining __all__, __init__.py allows you to define any variable at the package level. Doing so is often convenient if a package defines something that will be imported frequently, in an API-like fashion. This pattern promotes adherence to the Pythonic "flat is better than nested" philosophy.
What is __init__.py for?
https://docs.python.org/3/tutorial/modules.html
Add an empty __init__.py file to your resources and models directory.
https://docs.python.org/3/tutorial/modules.html#packages

Django equivalent to paster for backend processes

I use pylons in my job, but I'm new to django. I'm making an rss filtering application, and so I'd like to have two backend processes that run on a schedule: one to crawl rss feeds for each user, and another to determine relevance of individual posts relative to users' past preferences. In pylons, I'd just write paster commands to update the db with that data. Is there an equivalent in django? EG is there a way to run the equivalent of python manage.py shell in a non-interactive mode?
I think that's what Custom Management Commands are there for.
Yes, this is actually how I run my cron backup scripts. You just need to load your virtualenv if you're using virtual environments and your project settings.
I hope you can follow this, but after the line # manage.py shell you can write your code just as if you were in manage.py shell
You can import your virtualenv like so:
import site
site.addsitedir(VIRTUALENV_PATH + '/lib/python2.6/site-packages')
You can then add the django project to the path
import sys
sys.path.append(DJANGO_ROOT)
sys.path.append(PROJECT_PATH)
Next you load the django settings and chdir to the django project
import os
from django.core.management import setup_environ
from myproject import settings
setup_environ(settings)
os.chdir(PROJECT_PATH)
After this point your environment will be set just like if you started with manage.py shell
You can then run anything just as if you were in the interactive shell.
from application.models import MyModel
for element in MyModel:
element.delete()
Here is my backup file in full. I've abstracted the process out into functions. This would be named daily_backup and be put into the cron.daily folder to be run daily. You can see how to set up the environment and modify the functionality as needed.
#!/usr/bin/env python
import sys
import os
import site
import logging
from datetime import datetime
PROJECT_NAME = 'myproject'
DJANGO_ROOT = '/var/www/django'
PROJECT_PATH = DJANGO_ROOT + '/' + PROJECT_NAME
VIRTUALENV_PATH = '/var/www/envs/'+ PROJECT_NAME
BACKUP_DIR = '/var/www/backups/%s/daily' % (PROJECT_NAME)
TODAY = datetime.now().strftime('%Y%m%d-%H%M%S')
FILE_NAME = PROJECT_NAME + '_' + TODAY
site.addsitedir(VIRTUALENV_PATH + '/lib/python2.6/site-packages')
sys.path.append(DJANGO_ROOT)
sys.path.append(PROJECT_PATH)
from django.core.management import setup_environ
from myproject import settings
setup_environ(settings)
os.chdir(PROJECT_PATH)
# manage.py shell
from django.conf import settings
logging.basicConfig(level=logging.WARN)
def _setup():
if not os.path.exists(BACKUP_DIR):
logging.debug('Creating backup directory ' + BACKUP_DIR)
os.mkdir(BACKUP_DIR)
os.mkdir(BACKUP_DIR + '/databases')
else:
logging.debug('Using backup directory ' + BACKUP_DIR)
def _remove_old():
logging.debug('Cleaning out old backups')
# keep past 7 days
command = "find %s* -name '%s*' -mtime +7 -exec rm {} \\;" % (BACKUP_DIR, PROJECT_NAME)
os.system(command)
def _create_backup():
logging.debug('Backup database')
if settings.DATABASE_ENGINE == 'mysql':
command = 'mysqldump -u %s --password=%s %s > %s/databases/%s.sql' % (settings.DATABASE_USER, settings.DATABASE_PASSWORD, settings.DATABASE_NAME, BACKUP_DIR, FILE_NAME)
else:
command = '%s/bin/python %s/manage.py dumpdata --indent=4 > %s/databases/%s.json' % (VIRTUALENV_PATH, PROJECT_PATH, BACKUP_DIR, FILE_NAME)
os.system(command)
logging.debug('Backup project')
command = 'tar -czf %s/%s.tgz -C %s %s/' % (BACKUP_DIR, FILE_NAME, DJANGO_ROOT, PROJECT_NAME)
os.system(command)
if __name__ == '__main__':
_setup()
_remove_old()
_create_backup()
Sounds like you need some twod.wsgi in your life: http://packages.python.org/twod.wsgi/