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()
Related
I have created a object which I am trying to save in Django admin in myapp>Hello. But the object does not get created under 'Hello' when I run the server. How can I fix it? I have also registered my models in admin.py.
models.py:
from django.db import models
from django.contrib.auth.models import User
class Foo(models.Model):
foo_id = models.CharField(max_length=10)
class Hello(models.Model):
user = models.ForeignKey(User, models.DO_NOTHING)
foo_id = models.ForeignKey('Foo', models.DO_NOTHING, db_column='foo_id')
foo_text = models.CharField(max_length=500, default="Hello!")
views.py
from django.shortcuts import render,HttpResponse,redirect
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from .models import User,Foo, Hello
from django.contrib.auth import settings
#login_required
def home(request):
return render(request, 'index.html')
#login_required
def sendhello() :
Foos=Foo.objects.all()
for foo in foos:
#Hello(user=user, foo_text='hello there', foo_id=foo).save()
xyz, obj=Hello.objects.get_or_create(user=user, foo_text='hello there', foo_id=foo)
if xyz is True:
obj.save()
#login_required
def helloxyz(request):
user = User.objects.get(id=request.session['id'])
hellos=Hello.objects.filter(user_id=user)
print(hellos)
hellos_list=[]
for hello in hellos:
print(hello.hello_text)
hellos_list.append(hello.hello_text)
hellos_list.reverse()
print(hellos_list)
return render(request,'index.html',{'hellos': hellos_list,})
First of all, your sendhello function is not a view, so it does not get called when you view your site. I will for now just assume you have a JS login dialogue or someting you get inputs from. SO you want to generate DB entries from these inputs that are present (?) in your DB. You could do this by simply calling your generating function from within a view class or function (I recommend to put such non-view helper functions in a seperate file and import them in your view) or outside of views with e.g. django cron.
The simplest would be to just create a new file utilis.py, import them in your views with import myapp.utils and then call your function at the top of your site view.
Why a sperate file? It is just for better readability, is cleaner when someone else looks at your code and swapping out helpers is a bit easier.
If you have regularly occuring tasks that should best be executed independant from the user loading a specific page, take a look at the django_crontab module, it is really handy. You need to be on a Linux system dough.
I hope that answered your question
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
So I want to customize the admin page using the AdminSite class as described in the docs.
from django.contrib.admin import AdminSite
from .models import MyModel
class MyAdminSite(AdminSite):
site_header = 'Monty Python administration'
admin_site = MyAdminSite(name='myadmin')
But instead of using admin_site.register(MyModel) I would like to use the register decorator like
from django.contrib.admin import ModelAdmin
#admin_site.register(MyModel)
class MyModelAdmin(ModelAdmin):
pass
However, this only throws the error TypeError: 'NoneType' object is not callable. Is there now register decorator for subclasses of AdminSite in Django yet (1.9) or am I missing something?
You can use the keyword argument site like this:
#admin.register(MyModel, site=MyAdminSite)
from django.contrib import admin
...
#admin.register(MyModel, site=MyAdminSite)
class MyModelAdmin(ModelAdmin):
pass
Docs on the register decorator:
https://docs.djangoproject.com/en/1.9/ref/contrib/admin/#the-register-decorator
I'm not sure if you have figured it out yet. But I see that you haven't subclassing admin.ModelAdmin in your admin class.
#admin_site.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
pass
class MyAdminSite(AdminSite):
...
admin_site = MyAdminSite()
#admin_site.register(MyModel)
def ...
TypeError: 'NoneType' object is not callable is caused by the fact that admin_site.register is calling method register of AdminSite.
You should use django.contrib.admin.register which is the correct decorator:
from django.contrib import admin
#admin.register(MyModel, site=admin_site)
class MyModelAdmin(ModelAdmin):
...
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 have an app named doors and my models.py for the app has 10 tables/class. Under my admin.py, how do I register every model in the file models.py?
For example, currently I have to hardcode it:
from django.contrib import admin
from doors.models import *
admin.site.register(Group)
admin.site.register(Item)
admin.site.register(ItemType)
admin.site.register(Location)
admin.site.register(Log)
admin.site.register(Order)
admin.site.register(Property)
admin.site.register(User)
admin.site.register(Vendor)
Is there a way I perhaps find every class in models.py and loop through and register each class? Or is there some kind of wildcard I can use with Django?
Seems get_models and get_app are no longer available in django 1.8.
The following can be used:
from django.contrib import admin
from django.apps import apps
app = apps.get_app_config('dashboard')
for model_name, model in app.models.items():
admin.site.register(model)
EXTENSION: If you would like to show all or select fields of the model as a grid instead of a single column unicode representation of the model objects you may use this:
app = apps.get_app_config('your_app_name')
for model_name, model in app.models.items():
model_admin = type(model_name + "Admin", (admin.ModelAdmin,), {})
model_admin.list_display = model.admin_list_display if hasattr(model, 'admin_list_display') else tuple([field.name for field in model._meta.fields])
model_admin.list_filter = model.admin_list_filter if hasattr(model, 'admin_list_filter') else model_admin.list_display
model_admin.list_display_links = model.admin_list_display_links if hasattr(model, 'admin_list_display_links') else ()
model_admin.list_editable = model.admin_list_editable if hasattr(model, 'admin_list_editable') else ()
model_admin.search_fields = model.admin_search_fields if hasattr(model, 'admin_search_fields') else ()
admin.site.register(model, model_admin)
What this does is, it extends ModelAdmin class on the fly and sets the list_display field which is required for showing model data in grid representation in the admin. If you list your desired fields in your model as admin_list_display it takes that one, or generates a tuple of all fields available in the model, otherwise.
Other optional fields can similarly be set, such as list_filter.
See django documentation for more info on list_display.
I figured it out with #arie's link (for django < 1.8):
from django.contrib import admin
from django.db.models import get_models, get_app
for model in get_models(get_app('doors')):
admin.site.register(model)
But I wonder if I can do this without get_app... Couldn't the code be smart enough to know the name of its own app?
From Django 1.7 on, you can use this code in your admin.py:
from django.apps import apps
from django.contrib import admin
from django.contrib.admin.sites import AlreadyRegistered
app_models = apps.get_app_config('my_app').get_models()
for model in app_models:
try:
admin.site.register(model)
except AlreadyRegistered:
pass
From Django 1.8, to fix the error message
RemovedInDjango19Warning: django.db.models.get_app is deprecated.
We can use this approach in 2 lines
from django.contrib import admin
from my_app.models import *
from django.apps import apps
for model in apps.get_app_config('my_app').models.values():
admin.site.register(model)
from django.apps import apps
from django.contrib.admin.sites import AlreadyRegistered
app_models = apps.get_app_config('app-name').get_models()
for model in app_models:
try:
admin.site.register(model)
except AlreadyRegistered:
pass
from django.contrib import admin
from .models import Projects, ProjectsUsers, Comments, ProjectsDescription
Models = (Projects, ProjectsUsers, Comments, ProjectsDescription)
admin.site.register(Models)
From Django3.0,you can try add the following code in admin.py
from . import models
class ListAdminMixin(object):
def __init__(self, model, admin_site):
self.list_display = [field.name for field in model._meta.fields if field.name != "id"]
super(ListAdminMixin, self).__init__(model, admin_site)
for m in [your_model_name]:
mod = getattr(models, m)
admin_class = type('AdminClass', (ListAdminMixin, admin.ModelAdmin), {})
try:
admin.site.register(mod, admin_class)
except admin.sites.AlreadyRegistered:
pass