Reputation: 65
For my Django application, I have a dictionary of field names (keys) mapped to help texts (values) (which I read in from a csv file). In models.py
, I want to retrieve the appropriate help text for each field name.
If I have a sample dictionary like the following, with an entry for each field:
test_dict = {
'host_name': 'The name of a host',
...
}
And my models.py
looks like this:
class Host_main(models.Model):
host_name = models.CharField(max_length=20, unique=True, help_text=test_dict['host_name'])
def __str__(self):
return self.host_name
Is there a way to call the variable name (host_name
) dynamically in each help_text
definition?
I do have the option to do something like the following:
from varname import nameof
host_name = models.CharField(max_length=20, unique=True, help_text=test_dict[nameof(host_name)])
But if possible, I'd like to reference the current variable name with something consistent to avoid typing out the field name a second time, like help_text=test_dict[nameof(**this**)]
in pseudocode.
Thanks!
Upvotes: 0
Views: 351
Reputation: 2676
Let's simplify your question a little bit, and let's remove the django part first. Let's say you have a class with name A
, then you can set an attribute on A
with
class A: pass
A.foo = 'bar'
Alternatively, you can also do
setattr(A,'bar','baz')
You can verify that those are indeed equivalent by checking
print(A.foo) # bar
print(A.bar) # baz
So if you are defining a normal class, you can pretty much just loop through your dict
and set things with setattr
calls, in which case you have the control of the variable name.
And when we bring Django into the question, things are a bit more complicated...There are some very strange and magical things happening when you initialize a Model
class. Basically it does a lookup on things already defined, and transforms all the defined fields through the pretty much public private _meta
API. This means that adding class attributes outside of definition time won't work, and you might have to use a bit of a hack to construct a class directly through its metaclass (in case you are not familiar with Python's metaclasses, a class is basically an instance of its metaclass).
The basic syntax for dynamically constructing a class is through the type
call with 3 arguments: type(name, bases, dict)
. In your example,
class Host_main(models.Model):
host_name = models.CharField(max_length=20, unique=True, help_text=test_dict['host_name'])
def __str__(self):
return self.host_name
is the equivalent of
def Host_main_str(self): # the name here doesn't really matter unless you care about the __name__ attribute of the function, which you can overwrite.
return self.host_name
Host_main = type('Host_main', (models.Model,), {
'__str__': Host_main_str,
'host_name': models.CharField(max_length=20, unique=True, help_text=test_dict['host_name'])
})
It's actually how django constructs all their QuerySet classes (there were some more crazy things django did with dynamic class constructions if I remember correctly but I couldn't find a good example)
So, you can do things like
attr_dict = {'__str__':'Host_main_str'}
for name, help_text in test_dict.values():
attr_dict[name] = models.CharField(max_length=20, unique=True, help_text=help_text)
Host_main = type('Host_main', (models.Model,), attr_dict)
if you really insist on loop through a dictionary.
Upvotes: 2