How to make a str var callable - django

What i want is to make a string var callable. I just have a list with different models_names and i want to call their create methods like this way.
class Object_model_a:
#def ...
class Object_model_b:
#def ...
class Object_model_c:
#def ...
list = ('Object_model_a', 'Object_model_b', 'Object_model_c')
x = list[0]() # this must create a new instance of Object_model_a
This is possible to develop using php like this way:
$hi = 'Hello'
$Hello = 'Hi!!'
echo $$hi
>> Hi!!
Anyone knows if this is possible using django?? This will simplify a lot my code.
Thanks a lot!

You could use Django's get_model helper function, which takes the app name and model name and returns the model class.
from django.db.models import get_model
list = ('Object_model_a', 'Object_model_b', 'Object_model_c')
model = get_model('my_app_name', list[0]) # returns Object_model_a class
instance = model() # creates a model instance

If for whatever reason you have a string that represents a particular class, the correct way to use that to instantiate an object of that class is to place your classes in a dict, keyed by the strings, then just invoke the class.
classes = dict((c.__name__, c) for c in (Object_model_a, Object_model_b, Object_model_c))
instance = classes['Object_model_a']()
Obviously, you don't need to use the classname as the key.

Thanks for all your comments, this is the way i've solved this requirement.
def create_object_instance(model_name, id=None):
for model in get_models():
if str(model.__name__).lower() == str(model_name).lower():
if id:
try:
return model.objects.get(id=id)
except ObjectsDoesNotExist:
return None
else:
return model.objects.all()
return None

Related

Access a serializer via a variable in drf

In the create method in my serializer I'm trying to send a portion of validated data to another serializer as below:
new_serializer = NewSerializer(validated_data.get('customer'))
new_serializer.save()
But in the place of NewSerializer I want to be able to use a variable. I have a dictionary where each model is mapped to its serializer.
my_dict = {"Model1": "Serializer1", "Model2": "Serializer2"}
The model name is available to me and I will use that to find out the corresponding serializer from the dictionary. I have over fifty serializers and I may have to use any one so importing all of them will not be feasible. I want to do something like this:
the_serializer = mydict.get('Model1')
new_serializer = the_serializer(validated_data.get('customer'))
Is there a way to achieve this?
From your question, I interpreted that you want to instantiate a class where the classname is provided by a variable.
For doing so, you can use getattr, however, you need to know in which module the class to be instantiated is located.
For instance, if your serializer class is defined in a module foo.py, you can do something like:
import foo
from yourapp.serializers import DefaultSerializer
the_serializer = "NewSerializer"
SerializerClass = getattr(foo, the_serializer, DefaultSerializer)
args = ("args", "to", "your", "serializer")
serializer_object = SerializerClass(*args)
Or in case you need to handle erronous serializer names:
try:
SerializerClass = getattr(foo, the_serializer, DefaultSerializer)
args = ("args", "to", "your", "serializer")
serializer_object = SerializerClass(*args)
except AttributeError as ae:
print(f"{ae}")
# handle exception here
However, this approach of instantiating the class name doesn't appear appealing to me. There has to be some better way to do what you're trying to do.
I found a way to do this by using import_string in django. I had my app_name and model_name present which were used to import the serializer. All of my serializers are named in {model_name}Serializer format so this way worked for me.
serializer_name = import_string(f"{app_name}.serializers {model_name}Serializer")
ins = serializer_name(data = main_data)
if ins.is_valid():
ins.save()
Here's the django documentation link.
https://docs.djangoproject.com/en/4.0/ref/utils/?fbclid=IwAR22qaDgk0xT9nOY0lP4s3tVh2aKEleefFWQf_L6th5I0DQ56RUMpUmskpo#module-django.utils.module_loading

model methods returning empty strings

I am getting empty in return (from my model methods), I don't get where I am wrong in this, we can query model using self
class SocialLinks(models.Model):
alias_name = models.CharField(max_length=10,)
name = models.CharField(max_length=30)
url_link = models.URLField()
def get_fb_link(self):
try:
fb = self.objects.get(alias_name='fb')
return fb.url_link
except:
return ""
def get_linkdin_link(self):
try:
linkdin = self.objects.get(alias_name='linkedin')
return linkdin
except:
return ""
def get_insta_link(self):
try:
insta = self.objects.get(alias_name='insta')
return insta.url_link
except:
Your issue is that self corresponds to one instance of the model class, not the class itself.
So you can do
all_fb_links = SocialLinks.objects.filter(alias_name="fb")
and you will get all the records from the model that are facebook links, but you cannot do this referencing a single instance of the record using self.
You could write a class method, but what you actually want here is a model manager to define some specific queries so that you can then do
SocialLinks.get_all_fb_links()
Here's the docs on defining a custom manager: https://docs.djangoproject.com/en/3.2/topics/db/managers/
A method on the class like you are defining would be used to return something not stored on the table, but which could perhaps be derived from it. A simple example might be:
def link_type_and_url(self):
return f"{self.alias_name}:{url_link}"

Built-In callable as a default argument for a Django Field

I have a JSONField that I need to apply a default dictionary to. As per the documentation, I am avoiding passing the mutable dictionary to the default field. This is done by instead passing the copy method to the default argument like such:
default_dict = {'some_key': 'some value'}
class MyModel(models.Model):
my_field = models.JSONField(default=default_dict.copy)
When applying makemigrations, this is failing because of the following condition in django.db.migrations.serializer.FunctionTypeSerializer:
if self.value.__module__ is None:
raise ValueError("Cannot serialize function %r: No module" % self.value)
I can get around this by defining a callable that returns a copy, but I think this is adding unnecessary syntax and makes it harder to read:
class ADict(dict):
def __call__(self):
return self.copy()
default_dict = ADict({'some_key': 'some value'})
class MyModel(models.Model):
my_field = models.JSONField(default=default_dict)
Is there a way to pass a built-in objects method as the default value for a Django field?
You can't do this since it basically needs to be a named function, whereas default_dict.copy is an "anonymous" function.
You can however make a named function like:
default_dict = {'some_key': 'some value'}
def copy_default_dict():
return default_dict.copy()
class MyModel(models.Model):
my_field = models.JSONField(default=copy_default_dict)
or even simpler:
def copy_default_dict():
return {'some_key': 'some value'}
class MyModel(models.Model):
my_field = models.JSONField(default=copy_default_dict)

In django how can i create a model instance from a string value?

All,
I have strings that represent my model and fields, like this
modelNameStr = 'MyModel'
fieldNameStr = 'modelField'
My model looks like this;
class MyModel(models.Model):
modelField = ForeignKey( ForeignModel )
...
What i want to do is create an instance of MyModel using the string variables, something like
model_instance = modelNameStr.objects.filter(fieldNameStr=ForeignModelInstance)
How can i do this?
Gath
model_instance = ContentType.objects.get(app_label=u'someapp', model=modelNameStr).model_class()(**{fieldNameStr: ForeignModelInstance})
Phew! Try saying that five times fast! But make sure you use the appropriate value for app_label.
Retrieving the model class you can use the get_model function from Django. Though you have to use a string like my_app.MyModel where 'my_app' is your django app which includes the model. Filtering field values can be achieved via a dict. Here an example:
from django.db.models import get_model
modelNameStr = 'my_app.MyModel'
fieldNameStr = 'modelField'
ModelClass = get_model(*model_class.split('.'))
filters = {fieldNameStr: ForeignModelInstance}
model_instance = ModelClass.objects.filter(**filters)

Django, query filtering from model method

I have these models:
def Foo(Models.model):
size = models.IntegerField()
# other fields
def is_active(self):
if check_condition:
return True
else:
return False
def Bar(Models.model):
foo = models.ForeignKey("Foo")
# other fields
Now I want to query Bars that are having active Foo's as such:
Bar.objects.filter(foo.is_active())
I am getting error such as
SyntaxError at /
('non-keyword arg after keyword arg'
How can I achieve this?
You cannot query against model methods or properties. Either use the criteria within it in the query, or filter in Python using a list comprehension or genex.
You could also use a custom manager. Then you could run something like this:
Bar.objects.foo_active()
And all you have to do is:
class BarManager(models.Manager):
def foo_active(self):
# use your method to filter results
return you_custom_queryset
Check out the docs.
I had similar problem: I am using class-based view object_list and I had to filter by model's method. (storing the information in database wasn't an option because the property was based on time and I would have to create a cronjob and/or... no way)
My answer is ineffective and I don't know how it's gonna scale on larger data; but, it works:
q = Model.objects.filter(...)...
# here is the trick
q_ids = [o.id for o in q if o.method()]
q = q.filter(id__in=q_ids)
You can't filter on methods, however if the is_active method on Foo checks an attribute on Foo, you can use the double-underscore syntax like Bar.objects.filter(foo__is_active_attribute=True)