Package's namespace polluted by Django? - python

Django 1.8
For no obvious reasons, global variable defined in package's module is replaced between its initial assignment and deferred function call.
Minimal Django project is created with django-admin startproject. New empty application added with django-admin startapp simplelib. New app simplelib added to INSTALLED_APPS of project's settings.py.
Bellow is the only added code:
# content of myproject.simplelib.__init__.py
from django.db import models
from django.db.models.signals import class_prepared
def myhandler(sender, **kwargs):
print 'models from myhandler: {}'.format(models)
def direct_call():
print 'models from direct_call: {}'.format(models)
class_prepared.connect(myhandler)
print 'models from top namespace: {}'.format(models)
direct_call()
When project is run with manage.py runserver, following output is produced:
models from top namespace: <module 'django.db.models' from '/home/<snip>/Projects/Python/django-projects/lib/python2.7/site-packages/django/db/models/__init__.pyc'>
models from direct_all: <module 'django.db.models' from '/home/<snip>/Projects/Python/django-projects/lib/python2.7/site-packages/django/db/models/__init__.pyc'>
models from myhandler: <module 'simplelib.models' from '/home/<snip>/Projects/Python/django-projects/myproject/simplelib/models.pyc'>
^^^^^^^^^^^^^^^^
See, when signal handler function is invoked, modules global variable is changed.
There is no other project's code. It has to be altered by Django itself.
Note: above described effect applies only when simplelib is placed at the start of INSTALLED_APPS tuple. When added at the end, models still points to django.db.models, as expected.
Any idea what's going here ?

This is normal Python behaviour.
When you import a submodule, that submodule is set as an attribute on the parent module. In this case, when simplelib.models is imported, the models submodule is set on the parent module simplelib. The parent module namespace is the same as that module's __init__.py global namespace. This will overwrite the old value.
If you put simplelib as the first entry in INSTALLED_APPS, its models submodule will be the first models module imported by Django. This will replace the simplelib.models attribute before any model fires the class_prepared signal. On the other hand, if you put simplelib at the end of INSTALLED_APPS, Django will load simpellib.models as the last models module. Any models that are imported before that will fire the class_prepared signal before simplelib.models is imported, and before the models attribute is replaced with the submodule.

Related

In Django, why i cannot import a model in an python file created by me in inside the same app?

I created in Django, in my app an python file for forms and I want to import a model from .models (from the same app).
The problem is when I import the model, it returned an error.
The model is(models.py):
class Article(models.Model):
title = models.CharField(max_length=100)
location=models.CharField(max_length=120,null=True,blank=True)
category=models.CharField(max_length=100,null=True,blank=False)
slug=models.SlugField(null=True, blank=True, unique=True)
boddy=models.TextField()
timestamp = datetime.now()
update = models.TimeField(auto_now=True)
The problem is in util.py (the python file that i created) :
from .models import Article
The error is:
ImportError: cannot import name 'Article' from partially initialized module 'hello.models' (most likely due to a circular import) (D:\Python\Django\Projects\myproject\hello\models.py)
The python files are in the same app
It seems you're trying to do what is called a 'Circular Import'; you can't do that.
Your App is separated in modules and packages. (.py files and folders with a __init__.py file inside), and when you import a module into another module, python will run that entire module and set it into memory.
If in models.py you run
from ./.. import utils
... and in utils.py
from .models import Article
You will end up importing a module into another then into another recursively, resulting in a Memory Overflow.
To prevent that from happening, Django comes with a few checks that prevent that circular import.
A good way to prevent that is to separate your classes into smaller modules, so you only import that module.
On the other hand, if you're to refer to Foreign Keys, you can use Django built-in Lazy Reference by using '<app_name>.<ClassName>' under quotes.
By doing so, you don't have to import the module, hence, there's no risk of circular import.

Django ImportError: Module does not define attribute/class

I am trying to add a custom/overridden AdminSite because I need a different template for the Admin. I did everything as the Docs said:
https://docs.djangoproject.com/en/3.0/ref/contrib/admin/#overriding-the-default-admin-site
project/admin.py
from django.contrib.admin import AdminSite
class NewAdminSite(AdminSite):
index_template = 'admin/index.html'
site_header = 'Administration'
admin_site = NewAdminSite(name='newadmin')
project/apps.py
class NewAdminConfig(AdminConfig):
default_site = 'project.admin.NewAdminSite'
I don't have any other apps, project is my root directory
ofc I added admin_site.urls instead of admin.site.urls to urls.py, created a custom AdminConfig in apps.py, and added that new AdminConfig to installed Apps instead of django.contrib.admin.
The Problem is that I now receive this:
AdminSiteClass = import_string(apps.get_app_config('admin').default_site)
File "C:\Users\User.virtualenvs\ssccms-fGQRLLK4\lib\site-packages\django\utils\module_loading.py", line 24, in import_string
) from err
ImportError: Module "project.admin" does not define a "NewAdminSite" attribute/class
my folder structure:
manage.py
project
admin
settings
urls
apps
static
templates
migrations
workdir
sqlite
media
node_modules
locale
pipfile.lock
pipfile
wsgi
hope this is enough for folder structure, it is the django-shop cookiecutter project example structure
EDIT:
yes, it was definitely a circular import error. My workaround now is simply using the given admin.site by writing:
admin.site.index_template = 'admin/newadminindextemplate.html'
it works! Which is actually all that I wanted. But the Documentation says the following
https://docs.djangoproject.com/en/3.0/ref/contrib/admin/#root-and-login-templates
If you wish to change the index, login or logout templates, you are better off creating your own AdminSite instance.
I tried that:
https://docs.djangoproject.com/en/3.0/ref/contrib/admin/#customizing-the-adminsite-class
instead of the overriding part I pasted in the first link in the question but now there is
django.urls.exceptions.NoReverseMatch: Reverse for 'cascade_texteditor_config' not found. 'cascade_texteditor_config' is not a valid view function or pattern name.
now my new question is:
is it necessary to create your own instance of the index template ? the Documentation says no explanation so are there any problems that I might run into at a later time by doing admin.site.index_template?
I've just hit a similar error. It's a bit confusing, in my case it was due to a circular import.
In my case the following helped:
convert imports from top of the file containing NewAdminSite class to local imports (inside of the function where they're needed
move the instantiation of admin_site variable to a separate file
Neither of those problems is actually visible in the code you've pasted, but may give you an idea what to look for.

Same imports across different files

Is it possible to include libraries/packages in only one location?
class Sample( db.Model ):
randomText = db.StringProperty( multiline = True )
--
from google.appengine.ext import db
from project.models import Sample
class SampleHandler( ):
def get( self ):
xamp = Sample.all( )
Since the handler already imports db from the google.appengine.ext library/package, and then imports the model i'd assume you don't have to include it again in the model itself. However, it looks like I have to, any way?
Anyone care to explain?
You need to import modules where they are used.
If your models module uses the google.appengine.ext.db module, you need to import it there, not in your handler module.
Importing things creates a reference to that 'thing' in your module namespace, so that the code there can find it when using it. db is the local name by which you get to use the object defined in google.appengine.ext.
If your handler uses the same object, it needs to import that still. If by importing models all names used by models suddenly where available in your handler module too, you'd end up with name conflicts and hard-to-debug errors all over the place.
Vice versa, if only importing google.appengine.ext.db in your handler module and not in your models module were to work, you'd need to import all the dependencies of given module together with the module itself. This quickly becomes unworkable, as you'd need to document all the things your models module requires just to be able to use it.

Python - Dynamic class import

I have the following folder structure:
- MyProject
- App1
- some_module1.py
- some_module2.py
- App2
- some_other_module1.py
- some_other_module2.py
Inside each of the modules (some_module1.py for example) there is a class that extends from a base class, in my case, Producer.
What I am trying to do is dynamically load in this class. To do that, I have a list of "installed apps" that looks like this:
INSTALLED_APPS = (
'App1',
'App2',
)
I am trying to write a function that will check each "app" package for a particular producer class and ensure it extends from the producer base class. Something like this:
module_class = 'some_module1.SomeClass'
# Loop through each package in the INSTALLED_APPS tuple:
for app in INSTALL_APPS:
try:
#is the module_class found in this app?
#App1.some_module1.SomeClass - Yes
#App2.some_module1.SomeClass - No
# is the class we found a subclass of Producer?
exception ImportError:
pass
I've tried experimenting with imp and importlib, but it doesn't seem to handle this kind of import. Is there anyway for me to be able to achieve this?
You may want to have a look at:
__import__() to import modules knowing their name as string;
dir() to get the names of all the objects of a module (attributes, functions, etc);
inspect.isclass(getattr(<module ref>, <object name>)) to identify classes among the objects of a module;
issubclass() to identify sub-classes from a given class, as explained here.
With these tools, you can identify all classes in a given module, that are inheriting a given class.
I'm using this mechanism to dynamically instantiate classes from given module, so that their updates are automatically taken into account at an upper level.

Django: tests.py adds project name to class

In my views.py I have a piece of code that checks if a model inherits from an other model.
def inherits_from_animal(some_animal):
return Animal in inspect.getmro(some_animal)
Now in my views.py this works but in my tests it's failing because the name of the project gets added to the class name.
So if I have a project named zoo and an app named animals it looks like:
views.py:
<class 'animals.models.Animal'>
tests.py:
<class 'zoo.animals.models.Animal'>
Could anybody explain why this is happening and how I could fix it?
Update:
Seems to only be happening to an abstract model.
The problem is manage.py. If you put django apps inside other packages you should use their fully qualified module name for imports and as a corrolary in INSTALLED_APPS.
It still works if you omit the parent package name, because manage.py temporarily hacks sys.path to import the module that contains the settings module. Then nose does its own import magic and is unable to reproduce the manage.py hack.
I'd recommend to use django-admin.py instead of manage.py as it does not do any import tricks.
There is no concept of a project in django. Just treat everything as plain python packages and control your PYTHONPATH.
the inspect module is doing probably something hinky.
Python has the builtin function issubclass() for just this occasion!
def inherits_from_animal(some_animal):
return issubclass(some_animal, Animal)
although i dont see why you couldnt just use issubclass wherever you are using inherits_from_animal now ;)

Categories

Resources