Skip subdirectory in python import - python

Ok, so I'm trying to change this:
app/
- lib.py
- models.py
- blah.py
Into this:
app/
- __init__.py
- lib.py
- models/
- __init__.py
- user.py
- account.py
- banana.py
- blah.py
And still be able to import my models using from app.models import User rather than having to change it to from app.models.user import User all over the place. Basically, I want everything to treat the package as a single module, but be able to navigate the code in separate files for development ease.
The reason I can't do something like add for file in __all__: from file import * into init.py is I have circular references between the model files. A fix I don't want is to import those models from within the functions that use them. But that's super ugly. Let me give you an example:
user.py
...
from app.models import Banana
...
banana.py
...
from app.models import User
...
I wrote a quick pre-processing script that grabs all the files, re-writes them to put imports at the top, and puts it into models.py, but that's hardly an improvement, since now my stack traces don't show the line number I actually need to change.
Any ideas? I always though init was probably magical but now that I dig into it, I can't find anything that lets me provide myself this really simple convenience.

It depends on what your circular references are for. If you have a class in user that inherits from Banana and a class in banana that inherits from User, you can't do this. You also can't do it if each class defines a decorator that gets used in the other or anything else that gets called during the actual import.
You can, however, if you are just mutually referencing helper functions, or if your User object has a method to create new instances of Banana and your Banana object has a method that creates new instances of User. As long as the mutual reference doesn't actually get used until something in the module is called from outside it, then in your model folder, in __init__.py, you can just do something like:
import user
import banana
#etc...
user.banana = banana
banana.user = user
#etc...
User = user.User
Banana = banana.Banana
Then for sake of clarity and not trying to figure out what's going on

Related

How to import parent class from a separate .py file for building inherited class inside a driver script for Abaqus python (version 2.7.15)?

I have the following file structure
src
|-main_file.py
|-utils
| |-util_debug.py
|-classes
| |-class_coupon_generic.py
| |-class_coupon_fatigue_type_1.py
| |-class_coupon_fatigue_type_2.py
The file class_coupon_generic.py contains a class named coupon_generic which will act as a parent class for different types of coupons in future development. The file class_coupon_fatigue_type_1.py contains a class named coupon_fatigue_type_1 which is inherited from the parent class coupon_generic. I am keeping all the class files in the folder classes. Inside the utils folder, I have a driver script for debugging util_debug.py. I want to import the classes coupon_generic and coupon_fatigue_type_1 (depending on which coupon I am working on) in this file to build the final coupon model.
How do I achieve that?
I tried to import the modules using imp.load_source and importlib.import_module; but I couldn't figure out a way to do that. I am getting some error like name 'coupon_generic' is not defined. If the class coupon_generic is defined inside the file class_coupon_fatigue_type_1.py, then the I can make the import work using imp.load_source, but that kind of defeats the whole purpose of having a parent class. In order to add any new method to the parent class later on, I have to go to separate scripts and modify them individually. What is the correct way of making such import work with classes having inheritance?
Also, I prefer a solution where I can make the import of class_coupon_fatigue_type_1.py from a string name because it will be varying for different coupon types and I can control that by just passing a string name of the file during debugging.

How to create a package of models in Django

Having a quite large models.py file (which contains several models), I'm trying to refactor, with one model per file.
I'm therefore trying to create a models package, with the following structure:
app/models/__init__.py
app/models/first_model.py
app/models/second_model.py
Unfortunately, I cannot get Django lazy reference mechanism to work well, i.e.:
first_model = models.ForeignKey('app.FirstModel')
returns the error that Django cannot find the model.
Any idea? Thanks!
It should work, make sure that in __init__.py you are importing all the models from first_model.py and second_model.py.
from .first_model import FirstModel
from .second_model import SecondModel
EDIT: If you want to retrieve the models as 'app_label.model_name', then you will have to import them in __init__.py, otherwise you can try the following:
Use https://docs.djangoproject.com/en/2.0/ref/applications/#django.apps.apps.get_model
Or you can use ContentTypes: https://docs.djangoproject.com/en/2.0/ref/contrib/contenttypes/#methods-on-contenttype-instances
The presence of __init__.py tells Python to consider it as a Package, but in order to let Django find your models for Migration and find it well, you should import all your models stuff in __init__.py
Keep the structure like it was:
app/models/__init__.py
app/models/first_model.py
app/models/second_model.py
__init__.py
from .first_models import *
from .second_models import *
In the Cookbook there is an example using only the name of the class (model) and not the app part:
first_model = models.ForeignKey('FirstModel')

Importing from views to models in Django

I have something like this in one of my apps's models.py :
class Account(AbstractBaseUser):
email = models.EmailField(unique=True)
I want to import a class from another app's views.py as below:
from anotherapp.views import MyClass
The problem is in the first lines of anotherapp.views file, I imported the Account class. So when I want to import MyClass into my models.py file, this error raises :
ImportError: cannot import name Account
That is circular import error you are encountering. While it is bad practice to import from views to models in Django, if you still want to, you can follow methods in this question to resolve it.
Here are few alternative ways that can be considered as good practice:
You can consider importing other low-level modules exist within anotherapp instead of MyClass (not depending on views.py)
You can use Django's signals to catch anotherapp's events project wide and act according to.
You can create a third file, say utils.py in anotherapp, move MyClass there and let anotherapp/views.py and your app's models.py import from anotherapp.utils

common import statement for similar classes

In my app, I have a model folder / package.
In that model folder, I generally have a whole bunch of import statements (most of them from SQLAlchemy, such as String, Integer, Session, etc.
I find myself writing the same import statements again and again for stuff like User, Blog, Article, Etc. and have become pretty sloppy at it.
Understanding that explicit is better than implicit and then going ahead and disregarding that, is there a way to do this once?
this probably isnt what you want but who knows
common_imports.py
from sqlalchemy import Model,String,...
in other scripts just do
from common_imports import *
[edit]
actually I came up with a really awful and hacky way to do what you want
a really hacky way to do it might be something like
\root_of_package
-main.py
\models
-__init__.py
-model1.py
-model2.py
then in models\__init__.py
from sqlalchemy import String,Integer,...
from glob import glob
Base = ...
for fname in glob("%s/*.py"%dirname(__file__)):
with open(fname) as f:
eval(f.read())
__all__ = locals()
then in models/model1.py
#just use all the imports/locals from __init__.py as if they already existed
#this will be hellish if you use an IDE that checks for errors for you
class Model1(Base):
id = Column(Integer,primary_key=True)
...
then in main.py
from models import Model1
I do this in one of my applications.
# approot/m.py
from models.model import String, Integer, Session, (etc)
# Otherwise blank!
Then in everything else:
import m
Then you can do m.String, m.Integer, m.Session and etc. This also means I can further encapsulate my code so that later when I write up user models I can just add to m.py
from models.user import User, Group, PermissionSet
and continue to access m.User, m.Group and etc.

Django: Refactoring models into sub-modules

After the models.py in one app grew quite large, I've tried to move some of the classes into subpackages.
The old structure was something like this:
# File: assets/models.py
class Asset(...):
# lots of irrelevant code
# File: widgets/models.py
from assets.models import Asset
class Video(...):
asset = models.ForeignKey(Asset)
This worked without a problem, so I'm not going into further details about the structure.
What I've tried to do now is move the Asset class into a submodule. The structure is now as follows:
# File: assets/models/__init__.py (of course I deleted the old models.py)
from .assets import Asset
# File: assets/models/assets.py
class Asset(...):
# lots of irrelevant code
# File: widgets/models.py
from assets.models.assets import Asset
class Video(...):
asset = models.ForeignKey(Asset)
Somehow this doesn't work, and I can't figure out what actually causes the trouble. The error I'm getting is this:
widgets.video: 'asset' has a relation with model , which has either not been installed or
is abstract
It appears that Django can't reliably detect which app a model belongs to if it's in a nested submodule (ie. not directly inside APPNAME.models).
This is a known problem and can be solved by adding the following lines (in this case to the Asset class), thus defining explicitly which app a model belongs to:
class Asset(models.Model):
...
class Meta:
app_label = 'assets'
...
References:
https://groups.google.com/forum/#!topic/django-users/MmaiKvbDlDc
https://code.djangoproject.com/ticket/14007
You should import from models as before:
from assets.models import Asset
This allows you to always import from models but organise the models separately within the models directory. It also means, conceptually, that Asset is still in models as your ForeignKey refers to a assets.models.Asset object, not assets.models.assets.Asset.

Categories