Python - Error importing class from another file - python

Can anyone guide on resolving the error ? The manager class method will need capability to create instance of User Class but it is not finding the module
Below is the folder structure and code and other details. init.py file is empty
Internal folder
->main.py
->__init__.py
->User Folder
-> user.py
-> models.py
-> usermanager.py
-> __init__.py
ModuleNotFoundError: No module named 'models'
at <module> (/home/vmagent/app/internal/user/usermanager.py:4)
at <module> (/home/vmagent/app/main.py:2)
at import_app (/env/lib/python3.6/site-packages/gunicorn/util.py:352)
at load_wsgiapp (/env/lib/python3.6/site-
packages/gunicorn/app/wsgiapp.py:52)
at load (/env/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py:65)
at wsgi (/env/lib/python3.6/site-packages/gunicorn/app/base.py:67)
at load_wsgi (/env/lib/python3.6/site-
packages/gunicorn/workers/base.py:135)
at init_process (/env/lib/python3.6/site-
packages/gunicorn/workers/base.py:126)
at spawn_worker (/env/lib/python3.6/site-
packages/gunicorn/arbiter.py:578)
main.py code
from internal.account import user,usermanager
def registerBlueprint(app, blueprint, manager):
blueprint.manager = manager
app.register_blueprint(blueprint)
registerBlueprint(app, user.request, usermanager)
user.py code
request = Blueprint('flex.user', __name__)
#request.route('/v1/user', methods=['POST'])
def CreateAccount():
test = request.manager.UserManager()
return test.CreateUser()
usermanager.py
from models import User
class UserManager:
def __init__(self):
pass
def CreateUser(self):
logger.log_text("AccountManger")
data = json.dumps(dict(
email="Hello#123.com",
password="Test124"
))
user = User(data)
user.create()
responseObject = dict(status='success', message='Successfully
registered.')
return make_response(jsonify(responseObject)), 201

Python paths can get kind of tricky. I don't claim that this is the best approach, but it's what I hacked together on my lunch. Perhaps it can get you where you need to go.
When you run main.py, you're in the internal/ directory. You need to go up a level and then access the user/ directory. That's what happens with sys.path.append('../'). The module usermanager.py is in the user/ directory, so to import from user.py, we want to point to the current directory. Hence the period in from .user import User.
Here's the directory structure I used. I believe it is the same as what you have.
C:.
├───internal
│ main.py
│ __init__.py
│
└───user
│ models.py
│ user.py
│ usermanager.py
└───__init__.py
__init__.py
The __init__.py files are empty. Their presence indicates to Python that the directories are modules1.
main.py
import sys
sys.path.append('../')
from user import user
from user import usermanager
from user import models
my_user = user.User()
my_user.message()
my_user_manager = usermanager.UserManager()
my_user_manager.message()
my_user_manager.CreateUser()
my_model = models.Model()
my_model.message()
models.py
class Model():
def message(self):
print("This is a Model!")
user.py
class User():
def message(self):
print("This is a User!")
usermanager.py
from .user import User
class UserManager():
def message(self):
print("This is a UserManager!")
def CreateUser(self):
new_user = User()
print("I created the user: ", new_user)
Now, when I call main.py I get:
c:\project\internal>python main.py
This is a User!
This is a UserManager!
I created the user: <user.user.User object at 0x0000000002A61EB8>
This is a Model!
Everyone is talking to everyone else. Hopefully you can extrapolate that to your use case! If not, Chris Yeh has an article "The Definitive Guide to Python import Statements" you may find helpful.
1 See What is __init__.py for? to learn more about how that works.

There is no models module in the standard library, and you do not have a file named models.py listed here, so python cannot find it. If models.py is in some subdirectory, that subdirectory needs to be in your python path, and you need to be sure that they contain an __init__.py file. That is the meaning of the error you're encountering here.

Related

Is it possible to get django info without running it

I have a django model, the whole code is completed. but I want to access my model info. a code like this to get field names.
for f in myModel._meta.fields:
print(f.get_attname())
is it possible to do it from an external python script without running django server?
other possible automated ways of doing this and saving results to a file are also appreciated.
try1
because Im using docker I ran it up. and from django container I started python shell
>>> from django.conf import settings
>>> settings.configure()
>>> import models
it gave django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.
try2
by #Klaus D advice in comments I tried management command. so I created
users/
__init__.py
models.py
management/
__init__.py
commands/
__init__.py
_private.py
modelInfo.py
structure. in modelInfo.py I did
from django.core.management.base import BaseCommand, CommandError
from users import views2
def savelisttxtfile(the_list, path_, type_='w', encoding="utf-8"):
with open(path_, type_, encoding=encoding) as file_handler:
for item in the_list:
file_handler.write("{}\n".format(item))
class Command(BaseCommand):
def handle(self, *args, **options):
dic=[]
for f in views2.ChertModel._meta.fields:
print(f.get_attname())
dic.append(f.get_attname())
savelisttxtfile(dic,"F:\projects\sd.txt")
and from another python file I tried
os.chdir(r'F:\projects\users\management\commands')
from subprocess import run
import sys
run([sys.executable, r'F:\projects\users\management\commands\modelInfo.py'])
and it returned
CompletedProcess(args=['C:\\ProgramData\\Anaconda3\\python.exe', 'F:\projects\users\management\commands\modelInfo.py'], returncode=1)
and the results were not save in sd.txt
thanks to #klaus D and management command documentation I made this structure
users/
__init__.py
models.py
management/
__init__.py
commands/
__init__.py
_private.py
modelInfo.py
and in modelInfo.py I did
from django.core.management.base import BaseCommand, CommandError
from users import views2
def savelisttxtfile(the_list, path_, type_='w', encoding="utf-8"):
with open(path_, type_, encoding=encoding) as file_handler:
for item in the_list:
file_handler.write("{}\n".format(item))
class Command(BaseCommand):
def handle(self, *args, **options):
dic=[]
for f in views2.ChertModel._meta.fields:
print(f.get_attname())
dic.append(f.get_attname())
savelisttxtfile(dic,"F:\projects\sd.txt")
and to run it I went to manage.py location and executed python manage.py modelInfo to launch it.
Regarding your "try1" it seems to be a little bit trickier to start a python shell like python manage.py shell than what you propose there.
Fortunately you can do this:
python manage.py shell < your_script.py
and your script will be executed as if typed directly into the "django shell". Keep in mind that you still need to import your models relative to your project, i.e. from myapp.models import mymodel.

Django: __init__.py package breaks migrations

In my app, I have file structure:
myapp/
...
models.py
helpers/
__init__.py
RandomFileName.py
...
In RandomFileName.py I have helper class that generates random file names for my models:
class RandomFileName(object):
...
In models I want to treat helpers/ directory as a module:
from myapp.helpers import RandomFileName
class MyImage(models.Model):
...
image = models.ImageField(upload_to=RandomFileName('images/'))
...
Then, I run python3 manage.py makemigrations myapp
Looks good.
Then, I run python3 manage.py migrate and get an error:
in Migration
('image', models.ImageField(upload_to=myapp.helpers.RandomFileName.RandomFileName('images/'))),
AttributeError: type object 'RandomFileName' has no attribute 'RandomFileName'
Why is the RandomFileName doubled in migrations? Where did I go wrong?
Somehow your init.py file could have imported your object. Check. If not, then simply doing myapp.helpers.RandomFileName('images/') instead (based on the exception message) will fix the issue.

Creating Flask-SQLAlchemy instance with metadata from Blueprint

TL;DR: How do I use a metadata object from a Blueprint to create the Flask-SQLAlchemy instance? The only place I can see to provide the declarative base metadata object is in the initial SQLAlchemy() call. But when I import it from the Blueprint in my extensions.py file, the Blueprint's code needs the db object and the loading fails due to a circular import.
I have several model classes which I would like to use both in and outside of Flask. I am using the declarative method to do this, and my app is set up to use the App Factory model and Blueprints. The way the models get registered with SQLAlchemy is through the use of the metadata argument when the db object is created. In the context of my application, it makes sense to declare the metadata object in a Blueprint, instead of in the main app Blueprint. (This is where most of the code which references it will be, including the non-Flask utility script used for initially populating the database.) However, importing the model class from the second Blueprint ends up in a circular import.
$ flask db migrate
Error: While importing "my_app", an ImportError was raised:
Traceback (most recent call last):
File "my_app/venv/lib/python3.7/site-packages/flask/cli.py", line 235, in locate_app
__import__(module_name)
File "my_app/my_app.py", line 1, in <module>
from app import create_app
File "my_app/app/__init__.py", line 7, in <module>
from app.extensions import *
File "my_app/app/extensions.py", line 10, in <module>
from turf.models import metadata
File "my_app/turf/__init__.py", line 1, in <module>
from .routes import bp
File "my_app/turf/routes.py", line 14, in <module>
from app.extensions import db
ImportError: cannot import name 'db' from 'app.extensions' (my_app/app/extensions.py)
As mentioned in this general question on circular imports for Blueprints, a solution which works is to import the db object from inside each function in the second Blueprint, thus side-stepping the import during initialization of the extensions.py file. But in addition to being annoying, this just feels extremely hacky.
Ideally, I would be able to pass the metadata object I've created to SQLAlchemy's init_app() method. That would resolve this issue at a stroke. Unfortunately, init_app() doesn't take a metadata argument. Is there some other way to register metadata with an SQLAlchemy instance after initialization? Or have I missed some other key element of the declarative model approach?
I should say that the non-Flask portion of this is working just fine. My utility script is able to import the models and use them to add objects to the database. It's only the Flask imports which are giving me trouble.
Here is the hierarchy:
.
├── app
│   ├── __init__.py
│   └── extensions.py
└── turf
   ├── __init__.py
   ├── models.py
   └── routes.py
And the relevant code which fails due to circular import:
app/__init__.py:
from app.extensions import *
def create_app():
app = Flask(__name__)
with app.app_context():
import turf
app.register_blueprint(turf.bp)
db.init_app(app)
app/extensions.py:
from turf.models import metadata
db = SQLAlchemy(metadata=metadata)
turf/__init__.py:
from .routes import bp
turf/models.py:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import MetaData
metadata = MetaData()
Base = declarative_base(metadata=metadata)
# All the turf models are declared in this file
class Boundary(Base):
# ...etc...
turf/routes.py:
from .models import *
from app.extensions import db
bp = Blueprint('Turf', __name__, url_prefix='/turf')
#bp.route('/')
def index():
return render_template('turf/index.html')
It turns out you can declare the MetaData object in the extensions.py file and then import that in the Blueprint. I was sure this would fail, because the metadata object is now being populated after the db object is created, but I've verified that the models are indeed available and work as expected. And no more circular dependency. I've actually broken this part out into its own file to allow Blueprint code to import as little as possible.
app/base.py:
from sqlalchemy import MetaData
from sqlalchemy.ext.declarative import declarative_base
metadata = MetaData()
Base = declarative_base(metadata=metadata)
app/extensions.py:
from flask_sqlalchemy import SQLAlchemy
from .base import metadata
db = SQLAlchemy(metadata=metadata)
turf/models.py:
from app.base import Base
# All the turf models are declared in this file
class Boundary(Base):
# ...etc...
This also answers another question I had with my original approach: how would it work if I had a second Blueprint which also had model objects which needed to be available from non-Flask code? Now, I have created a single Base object and can use that to implement new classes in different Blueprints as needed.
One minor annoyance with this approach, though. In the non-Flask DB population script, I was originally able to use from models import * to refer to the sibling module (file) containing the models. This let me call the script directly, à la cd turf; python populate_db.py --arg. That no longer works because the models.py file now references a different package, app.extensions. So instead I have to use this workaround:
turf/populate_db.py:
try:
from .models import *
except:
print("You have to run this from outside the 'turf' directory like so: $ python -m turf.populate_db [...]")
sys.exit(1)

Circular import in Python with SQLAlchemy

My project:
project_name
|- my_app
|- __init__.py
|- run.py
|- models.py
First example
run.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
db = SQLAlchemy(app)
from models import User
db.create_all()
if __name__ == '__main__':
app.run()
models.py
from run import db
class User(db.Model):
#...
__ init.py __ is empty
After running this example I get this error:
ImportError: cannot import name User
This error describe Circular import of app variable in models.py (as I understand).
Second example
run.py
from my_app import app
if __name__ == '__main__':
app.run()
models.py
from my_app import db
class User(db.Model):
#...
__ init.p __
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
db = SQLAlchemy(app)
from models import User
db.create_all()
And now all works correct.
And, at this point, I don't understand, why the same code give me different logic?
Where is the magic? Why Circular import in __ init.py __ does't throw an error?
Thank You!
I'm just going to talk about your first example, because you can actually reproduce both scenarios without changing the code:
Example 1: Run run.py directly to see the Import error
Example 2: Open a python repl, and run:
from run import app
app.run()
And your first example now works, for the same reason your second code example did: the imports were moved out of __main__
In example 1, run.py is the top level execution environment, and the code here runs in __main__. The relative import to models.py requires that all references in models.py be resolved (since the class you're importing from models.py may be dependent on other parts of models.py outside of the class itself). The first thing models.py does is go back to __main__ to perform a relative import of db, and since it needs to resolve all references, it does - and one of the references it tries to resolve is the original from models import user statement. Voila, ImportError.
The thing is, at this point run.py hasn't finished executing, and so far it's still trying to import the definition of User. But now models.py is trying to call into it as though it had finished loading, and it expects run.py to already know the definition of User, even though it's right in the middle of trying to figure that exact thing out!
This isn't a circular import - not really. models.py is just trying to import from a file that hasn't finished executing yet, and whose contents aren't available for use.
By moving __main__ elsewhere - into the repl, or by moving your code around so that run.py doesn't define anything - you're giving the code a chance to actually resolve all the dependencies before any code tries to use them. By using from run import app; app.run(), this is the only code that is not done executing. The imports themselves all get to complete their work, resolve their dependencies and make them available to __main__
tl;dr don't accidentally write code that imports from __main__ when __main__ doesn't yet know the things it's being asked for

Python clean imports for models - SQL Alchemy

I have a flask app with the following directory structure:
myapp/
application.py
__init__.py
models/
__init__.py
user.py
The models use Flask-SQLAlchemy, and therefore, they need to have access to the db object (a SQLAlchemy instance) from application.py
user.py:
import sys,os
sys.path.append('/path/to/application/package')
from testapp import db
class User(db.Model):
id = db.Column(db.Integer,primary_key=True)
username = db.Column(db.String(255),unique=True)
age = db.Column(db.Integer)
def __init__(self,username,age):
self.username = username
self.age = age
def __repr__(self):
return '<User %r>' % self.username
Because any of the models need access to the application's SQLAlchemy instance, the db property, I have to throw this whole package on the path and then import from the main application module. For sanity's sake, I would like to keep the models in separate files. Will I need to put the path code on top of every model? Is there a better way? I'd rather not have the full path input like that, as they may be deployed to different hosts with different directory structures. Ideally there would be some way to internally handle the path so when it is used as another user via mod_wsgi I don't have to manually change the code.
1st approach:
I've ended up with the following structure:
project_root — also holds some configs, .gitignore file, etc
start.py
flask_root
__init__.py
application.py
module_1
__init__.py
models.py
module_2
__init__.py
models.py
Topmost start.py just runs the app:
#! /usr/bin/env python
from flask_root import applicaiton
if __name__ == '__main__':
application.manager.run()
Python searches for packages in the directory you script started from, so now you don't need add them to sys.path (as for me, modification of sys.path looks ugly).
Now you have full-working flask_root python package, and you can import everything from it, from any place of your application:
from flask_root.application import db
2nd approach:
If you start your Flask application from it's directory,
./application.py runserver
the directory you've started from is not be accessible as python package, even if it has __init__.py in it.
Though, with your directory layout you can do the following trick:
models/__init__.py:
from application import db
...
models/user.py:
from . import db
...
The first approach is more clean and universal. The second possibly can be useful when you need to share same blueprints between multiple Flask projects.

Categories

Resources