Confusing broken imports with Python3 and Django - python

I finally made the jump from python2 to python3 (hooray). However, I'm now plagued with import errors that I cannot seem to sanely resolve.
I have the standard Django 1.8 layout like this:
foo_bar
| - foo_bar
| - settings.py
| - urls.py
| - app_one
| - app_two
| - manage.py
My problem comes in when I try to import something from another app. For instance, importing a model from app_one.models.py into app_two.views.py.
I receive server errors depending on what I do.
1. Absolute Import
# in app_two.views.py
from foo_bar.app_one.models import MyModel
This gives the error:
No module named foo_bar.app_one
If I toggle my project back to 2.7, this import of course works A-OK. Not to mention, PyCharm is able to resolve this path and do auto-complete. So.. it's very confusing.
2. Relative imports:
# in app_two.views.py
from ..app_one.models import MyModel
This gives the error:
attempted relative import beyond top-level package
Again, same thing, seems like it should work -- even PyCharm thinks it should work (and can again autocomplete from the path). However, whenever I run my server, it explodes when this view is loaded.
Kind of Resolving:
Now, I can get the imports to work, but in a way that seems to make zero sense. If I leave off the top level package and abandon the relative import notation, it will import without error.
# in app_two.views.py
from app_one.models import MyModel
But this makes even less sense to me than everything else! Why does this work??? This one, of course, breaks when I swap back to 2.x. Further, PyCharm (for what it's worth), shows this as an unresolvable import.

For importing for Foreign Key relationships in a model (in Django 1.8+), you do not need to import explicitly, but can use string quoted version:
# in app1, import Customer from app2
customer_fk = models.ForeignKey('app2.Customer')
Complete Example:
from django.db import models
# this app is 'cart'
class ShoppingCart(models.Model):
# Import Customer Plan from another app - account
plan = models.ForeignKey('account.CustomerPlan')
started = models.DateTimeField()
cart_id = models.BigIntegerField()

Related

ValueError: attempted relative import beyond top-level package when trying to import models from another app in django

I have two django applications in my project in my project named electron: the first api and the second elec_meter. The directories are organized as follows:
electron /
api/
views.py
...
elec_meter/
models.py
...
In the api / views.py file I want to import elec_meter / models.py.This is how I do it:
from ..elec_meter.models import *
But I get the following error message:
ValueError: attempted relative import beyond top-level package
or
from electron.elec_meter.models import *
In this case I receive this error message:
ModuleNotFoundError: No module named 'electron.elec_meter'
Here is a picture of my code
How can I solve this problem?
The Python root path is the electron directory, so you can not work with from electron.….
You can import the objects by importing it starting with the name of the app, not the project. This thus means that you import this with:
from elec_meter.models import Model1, Model2
while you can make wildcard imports, it is often considered an antipattern, since it is unclear what you import. This means that it can set references to point to objects exported by the elec_meter.models module, and thus override the original references.

How to avoid circular import within django's tests dir?

I'm trying to fix a circular import issue I'm having with Django tests.
My dir structure is:
app/tests:
test_user_level01.py
test_user_level02.py
Within each file I have classes to test get 200s and get 403s (among other classes). Users in level 2 should have all the get 200s that level 1 users have and users in level 1 should have all the get 403s that the level 2 users have. Thus I have a circular import.
Normally I would solve this with an absolute import but I can't figure out what that would be.
I've tried:
"""test_user_level01.py"""
from . import test_user_level02
"""test_user_level02.py"""
from . import test_user_level01
But this seems to give a circular import error (missing classes).
Thank you ahead of time for your help.
PS. The following doesn't work:
import app.tests.test_user_level01 as level01
OR:
import tests.test_user_level01 as level01
OR:
import .test_user_level01 as level01
Move common code, i.e. code which is used in both test_user_level01.py and test_user_level02.py, to a separate file, e.g. app/tests/common.py, and then import from there. Don't forget to make app/tests a package (i.e. create __init__.py file in the dir).
"""test_user_level01.py"""
from app.tests.common import some_common_class_name
"""test_user_level02.py"""
from app.tests.common import some_common_class_name

Django Testing - ImportError: Start directory is not importable

I was trying to do a bit of testing on a pieace of code but I get the ImportError: Start directory is not importable. Below is my code. I would really appreciate if someone could help. I am using Python 2.7.5 and also pycharm.
This is the command I execute in virenv
manage.py test /project/tests.py
from django.test import TestCase
# Create your tests here.
from project.models import Projects
from signup.models import SignUp
class ProjectTestCase(TestCase):
def SetUp(self):
super(ProjectTestCase,self).SetUp()
self.john = SignUp.objects.get(email='john#john.com')
self.project = Projects.objects.get(pk=1)
def test_project_permission(self):
self.assertFalse(self.john.has_perm('delete',self.project))
self.assertTrue(self.john.has_perm('view',self.project))
self.assertTrue(self.john.has_perm('change',self.project))
You should verify that you have an __init__.py in your project directory (it can be an empty file)
You're not calling test correctly. Assuming you have an app known as project, the way to call the tests in that app is manage.py test project
[Edit for additional comment which is really getting into separate question territory]
I would suggest adding this to settings.py (at the bottom)
import sys
if 'test' in sys.argv:
DATABASES['default'] = {'ENGINE': 'django.db.backends.sqlite3'}
SOUTH_TESTS_MIGRATE = False
This:
A. Will use sqlite (sort of, I'm not sure if it's actually sqlite or an in-memory db that works like sqlite) ... I've noticed issues if I use postgres and am trying to have my permissions not be excessively liberal AND I've had a test abort)
B. Disables south migrations (so it will just start with a clean database built against whatever models are currently saying)

Relative import creates different class object?

In a Django project, I have a directory structure that looks something like this:
project/
├╴module_name/
│ ├╴dbrouters.py
│ ...
...
In dbrouters.py, I define a class that starts out like this:
class CustomDBRouter(object):
current_connection = 'default'
...
The idea is to have a database router that sets the connection to use at the start of each request and then uses that connection for all subsequent database queries similarly to what is described in the Django docs for automatic database routing.
Everything works great, except that when I want to import CustomDBRouter in a script, I have to use the absolute path, or else something weird happens.
Let's say in one part of the application, CustomDBRouter.current_connection is changed:
import project.module_name.dbrouters.CustomDBRouter
...
CustomDBRouter.current_connection = 'alternate'
In another part of the application (assume that it is executed after the above code), I use a relative import instead:
import .dbrouters.CustomDBRouter
...
print CustomDBRouter.current_connection # Outputs 'default', not 'alternate'!
I'm confused as to why this is happening. Is Python creating a new class object for CustomDBRouter because I'm using a different import path?
Bonus points: Is there a better way to implement a 'global' class property?
It depends on how this script is being executed. When you're using relative imports, you have to make sure the name of the script the import is in has a __name__ attribute other than __main__. If it does, import .dbrouters.CustomDBRouter becomes import __main__.dbrouters.CustomDBRouter.
I found this here.
It turns out, the problem was being caused by a few lines in another file:
PROJECT_ROOT = '/path/to/project'
sys.path.insert(0, '%s' % PROJECT_ROOT)
sys.path.insert(1, '%s/module_name' % PROJECT_ROOT)
The files that were referencing .dbrouters were imported using the "shortcut" path (e.g., import views instead of import module_name.views).

Can't Import Module

First of all, this is very likely not a path issue.
I have a pydev project in eclipse. Here is the directory structure:
Genetic-Framework
| Genetic-Framework
| Genetic
| __init__.py
| GA.py
| crossover.py
| fitness.py
| individual.py
| mutation.py
| population.py
| selection.py
| settings.py
| visualization.py
In GA.py, I have the following line:
from Genetic import settings, selection, visualization as vis
And yes, Genetic is in sys.path. However, I get the following error:
File "/.../Genetic-Framework/Genetic-Framework/Genetic/GA.py", line 17, in <module>
from Genetic import settings, selection, visualization as vis
ImportError: cannot import name settings
However, when I remove settings from that line, everything else imports just fine.
Interestingly, among the first lines of settings.py is this:
from Genetic import fitness, selection, mutation, crossover, population, GA
And when I remove GA from that line, everything seems to import just fine.
Why am I getting this error? Is this some issue with circular imports? How can I fix this?
Yes, that's an issue with circular imports.
The problem
The problem is that when your GA.py is run, it first tries to import settings. This means that settings.py starts getting run, and it immediately tries to import GA.
However, GA is already in the process of loading, so GA.py doesn't get run a second time - instead, settings just loads the GA that is already in memory (which is currently mostly empty, because it's still executing its imports).
Thus, things in settings that try to use things out of GA fail, because the things they're looking for in GA haven't been defined yet (because processing of GA.py hasn't gotten past the imports yet).
This makes the evaluation of settings.py raise an exception, which manifests as a failure to be imported (because an exception raised during import makes the import fail).
The solution
a) Avoid the situation in the first place.
In general, you should try to avoid circular imports in the first place. They often mean that you have really odd dependency structure that will be hard to debug later.
One way to do this is to try to find things that are needed in both modules and break them out into a separate third module that can be shared between the other two - so instead of using A.x in B, and B.y in A, you instead use C.x and C.y in both A and B.
b) Don't actually try to use things from circular imports until everything's loaded.
Another thing you can do is to defer usage of something from another module until after all of the imports have finished. In other words, don't try to reference an imported module's contents from top-level code, but instead place it in a class initializer or a function that you can call later on, once all of the imports have finished.
For example, instead of this...
import Foo
class Baz:
top_level_variable = Foo.bar
you can do this:
import Foo
class Baz:
def __init__(self):
self.instance_variable = Foo.bar
Obviously, instance properties are slightly different from class properties, but the idea is to defer actually having to look things up from other modules until after all of the modules finish executing and thus have their contents available. Also note that from Foo import bar would fail here because that tries to access the contents of Foo at the time of the import, which is what needs to be avoided.

Categories

Resources