I have a script called helpers.py in my Django app in which I can't import models because it creates a circular reference.
As a workaround to this problem, I tried loading the modules dynamically in the script, like so:
from django.apps import apps
MyModel=apps.get_model("mymodule", "MyModel")
but this gets called before Django loads models and throws an error.
Is there a way around this problem?
Ideally I need to be able to create references to the model after Django has initialised the models, so that its available throughout the script, but I'm not sure if this is possible.
Are you sure you need models inside your helpers.py? If needed stuff is related to a model consider implementing this as a model method or a custom manager.
If you are sure you need models in your helpers.py, then look at the examples below.
As #Selcuk mentioned you can try a local import in a function of the helpers.py. This example works even if helpers.py is imported in the models.py:
# helpers.py
def foo():
from .models import MyModel
# do something with MyModel
On the top of your helpers.py which has a circular import of the models.py you can import the whole models module without specifying the names you want to import from it. So the next example works too:
# helpers.py
import myapp.models
def foo():
# do something with myapp.models.MyModel
But the next doesn't work, because we specify the name from the module (that may be not defined yet):
# helpers.py
from .models import MyModel
# ImportError is risen when you import a name from the module which has a circular import of this module
def foo():
# do something with MyModel
And this doesn't work too. It will import the module, but when you try to access the name MyModel you will get NameError:
# helpers.py
from .models import *
def foo():
# do something with MyModel
# NameError is risen when you do something with MyModel here
Related
i m doing some project using django frame work i am a beginner and just used
django signals but i m confused that why do we need to imporrt signals file in app.py inside the ready function
code below makes question more clear i m stuck in this so require help
signal.py
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import Profile
#receiver(post_save,sender=User)
def create_profile(sender,instance,created,**kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save,sender=User)
def save_profile(sender,instance,**kwargs):
instance.profile.save()
app.py
from django.apps import AppConfig
class UsersConfig(AppConfig):
name = 'users'
def ready(self):
import users.signals
#i have no idea what this function does
what is the need of ready function here and why is it importing signals here???
what if i import signals at the top without using ready function??
what is the need of ready function here and why is it importing signals here?
The ready() method [Django-doc] is called after the registry is fully loaded. You thus can then perform some operations you want to perform before the server starts handling requests. This is specified in the documentation:
Subclasses can override this method to perform initialization tasks such as registering signals. It is called as soon as the registry is fully populated.
The reason the signals are imported here is because Django will not import the signals if you do not import those explicitly. If the signals module is not imported, then the signals are not registered on the corresponding models, and hence if you for example make changes to your User model, the signals will not be triggered.
Usually one adds a #noqa comment to the import line, to prevent a linter tool like pylint to raise warnings about an import that you do not use.
from django.apps import AppConfig
class UsersConfig(AppConfig):
name = 'users'
def ready(self):
import users.signals # noqa
In myApp, I have multiple classes in models. I would like to import all of these classes in admin.py and register.
Is it possible without repetition such as
from django.contrib import admin
from .models import (classA,classB, classC)
Could I import all the items without explicitly referring as done above
Could I also register all at one time
Thanks
Jeff
You can try this :-
import inspect
import models
for name, obj in inspect.getmembers(models):
if inspect.isclass(obj):
admin.site.register(obj)
This will get all the class of models.py and register on admin under loop.
I have not try it but its work same for getting classes on python.
you can register all model by using this:
from django.db.models.base import ModelBase
from django.contrib import admin
import models
for model_name in dir(models):
model = getattr(models, model_name)
if isinstance(model, ModelBase):
admin.site.register(model)
If I want to register my models with the admin I have to do this like this:
#admin.py
admin.site.register(models.About)
But with multiple models you can't do something like this:
models = (models.Project, models.Client, models.About)
for m in models:
admin.site.register(m)
First of all: why not!? Secondly: imagine one has a lot of models which all should be accessible from the admin interface. How do you do that in a generic way?
admin.site.register has this definition in the library:
def register(self, model_or_iterable, admin_class=None, **options):
so models to be registered can be a single model or iterable object so just use this:
myModels = [models.Project, models.Client, models.About] # iterable list
admin.site.register(myModels)
I tested this in my site and works perfectly fine.
# File: admin.py
from django.contrib import admin
from .models import Project, Client, About
admin.register(Project, Client, About)(admin.ModelAdmin)
With respect to the recent release of Django 1.7, you can use the django.contrib.admin.register decorator to register multiple models that using the same admin class.
from django.contrib import admin
from .models import Project, Client, About
#admin.register(Project, Client, About)
class DefaultAdmin(admin.ModelAdmin):
pass
Update
Consider making a simple call instead of declaring a dummy class
Based on the snippet here, what I usually do is have the following code in my admin.py
from django.db.models import get_models, get_app
from django.contrib import admin
from django.contrib.admin.sites import AlreadyRegistered
def autoregister(*app_list):
for app_name in app_list:
app_models = get_app(app_name)
for model in get_models(app_models):
try:
admin.site.register(model)
except AlreadyRegistered:
pass
autoregister('myapp')
With Django 3.1.3
in admin.py
from .models import Course, Category
admin.site.register(Course)
admin.site.register(Category)
This is better way to regestering multiple models
from django.contrib import admin
from myapp.models import Course, Category
admin.site.register(Course)
admin.site.register(Category)
from django.contrib import admin
from .models import *
#admin.register(Project, Client, About)
class AppAdmin(admin.ModelAdmin):
pass
Here is a wrokaround that attempts to register a custom models to the admin UI if existing else the default one, for that to work, you must follow the convention of naming your custom admin classes as "MyModelAdmin" by appending "Admin" to the end.
for model_name, model in apps.get_app_config('myapp').models.items() :
if '_' not in model_name :
if globals().get(model.__name__+'Admin') :
admin.site.register(model, globals().get(model.__name__+'Admin'))
else :
admin.site.register(model)
-> if you have just one model:
from django.contrib import admin
from .models import MyModel1
class myModel1Admin(admin.ModelAdmin):
list_display = ("name", "address")
admin.site.register(MyModel1, myModel1Admin)
-> but if you have more than one model or ModelAdmin, you can register them separately:
e.g:
from django.contrib import admin
from .models import MyModel1, MyModel2
class myModel1Admin(admin.ModelAdmin):
list_display = ("name", "address")
class myModel2Admin(admin.ModelAdmin):
list_display = ("name", "photo")
admin.site.register(MyModel1, myModel1Admin)
admin.site.register(MyModel2, myModel2Admin)
I would really like to make a form instance a model attribute so that i have access to the form from the template through an object that I have handy.
When I try to import import any form into models.py, I get an ImportError on each of the import statements in the forms.py file which reference a model in models.py. I'm assuming this is due to circular imports.
I can't seem to find any information on importing forms into models. Is this possible? If so, how?
What you're doing doesn't sound right, but if you want to do it you can embed the form import in the models' instance method like so:
class TestModel(model.Models):
def get_my_form(self):
from my_app.forms import MyForm
return MyForm()
In Django, is there a place I can get a list of or look up the models that the ORM knows about?
Simple solution:
import django.apps
django.apps.apps.get_models()
By default apps.get_models() don't include
auto-created models for many-to-many relations without
an explicit intermediate table
models that have been swapped out.
If you want to include these as well,
django.apps.apps.get_models(include_auto_created=True, include_swapped=True)
Prior to Django 1.7, instead use:
from django.db import models
models.get_models(include_auto_created=True)
The include_auto_created parameter ensures that through tables implicitly created by ManyToManyFields will be retrieved as well.
List models using http://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/
from django.contrib.contenttypes.models import ContentType
for ct in ContentType.objects.all():
m = ct.model_class()
print "%s.%s\t%d" % (m.__module__, m.__name__, m._default_manager.count())
If you want a dictionary with all models you can use:
from django.apps import apps
models = {
model.__name__: model for model in apps.get_models()
}
If you want to play, and not use the good solution, you can play a bit with python introspection:
import settings
from django.db import models
for app in settings.INSTALLED_APPS:
models_name = app + ".models"
try:
models_module = __import__(models_name, fromlist=["models"])
attributes = dir(models_module)
for attr in attributes:
try:
attrib = models_module.__getattribute__(attr)
if issubclass(attrib, models.Model) and attrib.__module__== models_name:
print "%s.%s" % (models_name, attr)
except TypeError, e:
pass
except ImportError, e:
pass
Note: this is quite a rough piece of code; it will assume that all models are defined in "models.py" and that they inherit from django.db.models.Model.
If you use the contenttypes app, then it is straightforward: http://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/
If you register your models with the admin app, you can see all the attributes of these classes in the admin documentation.
Here's a simple way to find and delete any permissions that exist in the database but don't exist in the ORM model definitions:
from django.apps import apps
from django.contrib.auth.management import _get_all_permissions
from django.contrib.auth.models import Permission
from django.core.management.base import BaseCommand
class Command(BaseCommand):
def handle(self, *args, **options):
builtins = []
for klass in apps.get_models():
for perm in _get_all_permissions(klass._meta):
builtins.append(perm[0])
builtins = set(builtins)
permissions = set(Permission.objects.all().values_list('codename', flat=True))
to_remove = permissions - builtins
res = Permission.objects.filter(codename__in=to_remove).delete()
self.stdout.write('Deleted records: ' + str(res))