So I'm trying to load a model dynamically as such :
urls.py
url(r'^list_view/([\w-]+)$', Generic_list.as_view()),
Here's my Generic_list class in views.py :
class Generic_list(ListView):
import importlib
module_name = 'models'
class_name = ?
module = __import__(module_name, globals(), locals(), class_name)
model = getattr(module, class_name)
template_name = 'django_test/generic_list.html'
About security, later I'll only allow classes from a white list.
So, what can I put in place of the question mark in order to get the class name from the url?
If you want to have your model loaded dynamically, you could do something like this:
class Generic_list(ListView):
template_name = 'django_test/generic_list.html'
#property
def model(self):
return # code that returns the actual model
More information about property. Basically, it will understand this method as an attribute of the class. Instead of having to define this attribute at the class level (meaning that the code will be evaluated when the file is imported by django), you make it look like it is an attribute, but it's acting like a method, allowing you to add business logic.
You could try to use property decorator and get_model method:
from django.views.generic import ListView
from django.apps import apps
class GenericList(ListView):
#property
def model(self):
# Obtain model name here and pass it to `get_model` below.
return apps.get_model(app_label='app_label', model_name='model_name')
Also, consider reading PEP 0008 for naming conventions.
Related
I have a file called models with few classes and I need to iterate throw them in order to get each class to preform the same task on it.
my problem:
from django.contrib import admin
from . import models
# this is very repetetive
admin.site.register(models.Myclass)
admin.site.register(models.MySecondclass)
admin.site.register(models.MyThirdclass)
#.
#.
#...
my goal:
from django.contrib import admin
from . import models
for class in models:
admin.site.register(class)
If you can't understand django
# models.py
import example
class Component():
return example.Component
class Post():
return "Post"
class Comment():
return "Comment"
# the goal file
from . import models
# i need to get my models as list [Component,Post,Comment]
Using inspect.getmembers with passing inspect.isclass as its second argument for predicate will give classes. To further filter to prevent other classes imported from somewhere else, ensure __module__ is "models":
import inspect
classes = [obj
for _, obj in inspect.getmembers(models, predicate=inspect.isclass)
if obj.__module__ == "models"]
gives classes as
[models.Component, models.Post, models.Comment]
after which you can do your for:
for class_ in classes:
admin.site.register(class_)
(using class_ to not override class of Python).
I had been dynamic create a python meta class
I want to dump it to a python file
cls = type("ClsName", (object, ), attr)
code = xxxGetSource(cls)
writeToFile(code, "moduleName/ClsName.py")
I need this, because when django makemigrations, it need to found the metaclass for model, but my model metaclass was dynamic generate
class XXXModel(GenerateCls({ .... }),
models.Model):
pass
Although Django does have tools that enable doing this, it is not actually needed.
What you need to have is that upon importing models.py, your dynamically created classes must be available on the module namespace.
So, all you have to do is to call upon whatever code does create the classes in code at the models.py module level, and ensure they are visible there - it can be as simple as:
from django.db import models
def create_classes():
Meta = type("Meta", (), {"app_label": "test2"})
DynamicUser = type("DynamicUser", (models.Model,), {"name": models.TextField(255), "__module__": __file__, "Meta": Meta})
return DynamicUser
DynamicUser = create_classes()
if you don't know at coding time the name and quantity of classes, then you can update the dicionary returned by globals() with the classes and their names.
Let's suppose the create_classes function would return a list contaning classes - one could do this in models.py
def materialize_for_migration():
classes = create_classes
globals().update({cls.__name__: cls for cls in classes})
materialize_for_migration()
In Django you can create managers for your models. I do this by added a new file called managers.py and in my model objects = MyManager().
To stop circular imports I do self.model. However, if I need to reference a different model in my manager i.e.
from models import SecondModel
second= SecondModel(name=test).save()
self.model(second=second)
I get the following error: ImportError: cannot import name SecondModel
So is there a way in Django to lazy load a model?
The currently accepted answer is deprecated as of Django 1.7; from this answer, you can adapt your code like this.
from django.apps import apps
class SomeModelManager(...):
...
def some_function(self):
model = apps.get_model(app_label='your_app', model_name='YourModel')
You have a few options:
1. Import by name
Django has a utility function for importing by string name so you don't need to import yourself. There are several methods available for this (see this question: Django: Get model from string?)
from django.db.models.loading import get_model
class SomeModelManager(...):
...
def some_function(self):
model = get_model('your_app', 'YourModel')
object = model()
2. Imports at the bottom
Add the import at the bottom of the managers.py file and make sure to simply import the module and not the models themselves.
So...
models.py:
import managers
class SomeModel(models.Model):
...
objects = managers.SomeModelManager()
managers.py
class SomeModelManager(...):
...
def some_function(self):
object = models.SomeOtherModel()
import models
Let's say there is ndb.Model that looks like this:
class Foo(ndb.Model):
bar = ndb.StringProperty()
My question is, if my only input is the Foo.query() how can I get the model as an object that this query belongs to?
def query_to_model(query):
# some magic
return model
The Foo.query().kind return the model's name as a string, but I didn't manage to find a way to get it as an object.
The following works using eval, but only when the model is defined in the same file:
def query_to_model(query):
return eval(query.kind)
I want something more general than that.
After you have imported code with this model definition, the list ndb.Model._kind_map should contain it. Here is the magic:
def query_to_model(query):
return ndb.Model._kind_map[query.name]
I use this code to find the model class if you have the kind name:
model_module = KIND_MODULES(kind_name)
mod = __import__(model_module, globals(), locals(), [kind_name], -1)
model_class = getattr(mod, kind_name)
The KIND Modules dict holds the modules to import the models from:
KIND_MODULES = { 'Users' : 'models', 'Comments' : 'models', 'Cities' : 'examples.models' }
I want create a ModelForm class where model is a parameter passed from the view.(i want a dynamic form, so i can create all forms using the same class ObjectForm by just changing model value in Meta) :
class ObjectForm(ModelForm):
model_name = None
def __init__(self, *args, **kwargs):
model_name = kwargs.pop('model_name ')
super(ModelForm, self).__init__(*args, **kwargs)
class Meta:
model = models.get_model('core', model_name )
exclude = ("societe")
An error is occured and say that model_name is not a global field.
Please help me on this problem.
your problem is that the class (and the Meta class) are processed at compile time, not when you instantiate your ObjectForm. at compile time, the model name is unknown. creating classes dynamically is possible, but a bit more complicated. as luck has it, the django devs have done the hard work for you:
>>> from django.forms.models import modelform_factory
>>> modelform_factory(MyModel)
<class 'django.forms.models.MyModelForm'>
update
So you want something like
def my_view(request):
# ...
MyForm = modelform_factory(MyModel)
form = MyForm(request.POST) # or however you would use a 'regular' form
Well, your basic error is that you are accessing model_name as a local variable, rather than as a model instance. That's fairly basic Python.
But even once you've fixed this, it still wouldn't work. The Meta class is evaluated at define time, by the form metaclass, rather than at runtime. You need to call forms.models.modelform_factory - you can pass in your modelform subclass to the factory, if you want to define some standard validation and/or fields.
form_class = modelform_factory(MyModel, form=MyModelForm)