Set Django model field by choice text name - python

Consider the following example model:
class MyModel(models.Model):
TYPE_ONE = 1
TYPE_TWO = 2
TYPE_CHOICES = (
(TYPE_ONE, "One"),
(TYPE_TWO, "Two"),
)
type = models.SmallIntegerField(choices=TYPE_CHOICES)
# ... other fields
This works well internally, as I now have a 'constant' which I can reuse throughout my code to refer to each type.
However, when this model gets serialized and sent to the client as JSON through a custom API Controller implementation, it turns the type into it's textual representation. It might look like this:
{
'id': 1,
'type': 'One'
}
This is fine, however I'd like to be able to set the field value based on this text version (the consumer of my API wants to be able to pass friendly strings, not ints).
When constructing a model instance, how can I set type to One, and have it automatically convert it into the relevant int?
Something like:
m = MyModel()
m.type = "One"
m.save() # throws a ValueError
Thanks

You can use this workaround:
m.type = dict((key,value) for (value,key) in MyModel.TYPE_CHOICES)['One']

You should make one property for it. So you can use whenever you want.
class MyModel(object):
TYPE_CHOICES = ((1, "One"), (2, "Two"))
type = models.SmallIntegerField(choices=TYPE_CHOICES)
def getx(self):
return self.type
def setx(self, val):
ch = filter(lambda y: y if y[1] == val else None, self.TYPE_CHOICES)
self.type = ch[0][0]
type_val = property(getx, setx)
then save like this.
m = MyModel()
m.type_val = "One"
m.save()
it will save into type.
Hopefully, it will work for you. :)

Related

get def unicode string information in django

Hi I have a model that has a return statement like this.
def __unicode__(self):
return u'Car from: %s' % self.car_from
If I call the model and print it, it shows.
mymodel = CarFromData.objects.filter(user = self.user )
<QuerySet [<CarFromData: Car from: CarRoom 1>]>
I would like to access the self.car_from in my view. So I can store the
CarRoom1 inside a variable.
I tried this way
x = mymodel.model.__name__
but that does not give me that string information.
car_from is nothing but an attribute to a CarFromData Model. So, its value can get in following way:
mymodel = CarFromData.objects.filter(user=self.user)
myobject = mymodel.first()
x = None
if myobject:
x = myobject.car_from

Odoo: Get Fields name on other Model and make it as Selection

I want to get the field names in a model to be an option in a selection field in another model. Is it possible?
class ExportEmplWizard(models.TransientModel):
_name = 'hr.empl.exp.wizard'
empl_ids = fields.Many2many('hr.employee', string="Karyawan")
hr_field = fields.Selection(hr_field_choice, string="Pilih Kolom")
def empl_to_exp(self):
fields = self.env['hr.employee'].fields_get()
hr_field_choices = []
for key, val in fields.items():
choice = (key, val['string'])
hr_field_choices.append(choice)
I'm trying get fields name on other model using:
sel.self.env['hr.employee'].fields_get()
The problem is, i don't know how to make it as selection options (multiple selection actually).
Thank you for the help.
As per docstring for fields.Selection()
:param selection: specifies the possible values for this field.
It is given as either a list of pairs (value, string), or a
model method, or a method name.
Basically, the selection argument should work like the compute argument. And code of fields.Selection.get_values() confirms this.
So you should try something like this :
hr_field = fields.Selection(selection='empl_to_exp', string="Pilih Kolom")
def empl_to_exp(self):
fields = self.env['hr.employee'].fields_get()
return [(k, v['string']) for k, v in fields.items()]
You may have/want to use getattr(v, 'string', 'DEFAULT_VALUE') instead of simply v['string']. Fields should always have a string though.
def get_selection_name(env, model, field, value):
return dict(env[model].fields_get(field, 'selection').get(field, {}).get('selection',{})).get(value)
# usage
get_selection_name(request.env, 'sale.order', 'general_status', 'draft') # 'Draft'
get_selection_name(self.env, 'sale.order', 'general_status', 'draft') # 'Draft'
I use this!

Django get all base class objects that their derived class has a mixin

I have models that look something like this:
class SomeModel(model.Model):
pass
class Base(models.Model):
field1 = ...
class Mixin1(models.Model):
some_model = models.ForeignKey('SomeModel', on_delete=models.CASCADE)
class Meta:
abstract = True
class Derive1(Base, Mixin1):
field2 = ...
class Derive2(Base, Mixin1):
field3 = ...
class Derive3(Base):
field4 = ...
Now I want to be able to get all objects that derive from Base and Mixin1, without explicitly listing all classes that use Mixin1.
Something like:
Base.objects.filter(some_model=X)
Why this doesn't work I understand, but how to actually make it work I do not know.
Solution: Thanks to valentjedi's answer I got to a working solution. I am posting it here in case it will help someone one day.
On top of wanting all the results together, I also wanted them sorted by a date field in Base and the ability to filter them freely. So I created in Mixin1 the function:
#staticmethod
def get_mixin1_objects(some_model, q_filter=None):
mixin_models = set(Mixin1.__subclasses__()) & set(Base.__subclasses__())
results = []
for mdl in mixin_models:
apply_filter = Q(some_model=some_model)
if q_filter is not None:
apply_filter &= q_filter
results.append(list(mdl.objects.filter(apply_filter).order_by('date').all()))
# Merge sort all the events
return list(heapq.merge(*results))
While adding to Base the method:
def __gt__(self, other):
return self.date > other.date
Thus I can apply filters however I like, and can get order_by a constant field results (which suffices my current needs).
Example of usage:
lst = Mixinq.get_mixin1_objects(some_model_instance, q_filter=~Q(some_field_in_base=some_value))
I might not understand your question clearly, but you can use __subclasses__ magic method for something like this:
>>> set(Mixin1.__subclasses__()) & set(Base.__subclasses__())
{Derive2, Derive1}
Then you can filter those models and do whatever you want to them.

How to initialize django-form data type field from a given choices?

First of all: I am not able to find out the proper Title of this question.
Anyhow the question is:
I have to fill a form at template and the fields of this form are user dependent. For example you passes integer (integer is not a datatype) as a parameter to the method and it should returns like this:
fileds = forms.IntegerField()
If you pass bool then it should like this:
fields = forms.BooleanField()
So that i can use them to create my form. I tried with this code but it returns into the form of string.
Some.py file:
choices = (('bool','BooleanField()'),
('integer','IntegerField()'))
def choose_field():
option = 'bool' # Here it is hardcoded but in my app it comes from database.
for x in choices:
if x[0]==option:
type = x[1]
a = 'forms'
field = [a,type]
field = ".".join(field)
return field
When i print the field it prints 'forms.BooleanField()'. I also use this return value but it didn't work. Amy solution to this problem?
The simpliest way is to create your form class and include fields for all possible choices to it. Then write a constructor in this class and hide the fields you don't want to appear. The constructor must take a parameter indicating which fields do we need. It can be useful to store this parameter in the form and use it in clean method to correct collected data accordingly to this parameter.
class Your_form(forms.ModelForm):
field_integer = forms.IntegerField()
field_boolean = forms.BooleanField()
def __init__(self, *args, **kwargs):
option = kwargs["option"]
if option == "integer":
field_boolean.widget = field_boolean.hidden_widget()
else:
field_integer.widget = field_integer.hidden_widget()
super(Your_form, self).__init__(*args, **kwargs)
In your controller:
option = 'bool'
form = Your_form(option=option)

How would you inherit from and override the django model classes to create a listOfStringsField?

I want to create a new type of field for django models that is basically a ListOfStrings. So in your model code you would have the following:
models.py:
from django.db import models
class ListOfStringsField(???):
???
class myDjangoModelClass():
myName = models.CharField(max_length=64)
myFriends = ListOfStringsField() #
other.py:
myclass = myDjangoModelClass()
myclass.myName = "bob"
myclass.myFriends = ["me", "myself", "and I"]
myclass.save()
id = myclass.id
loadedmyclass = myDjangoModelClass.objects.filter(id__exact=id)
myFriendsList = loadedclass.myFriends
# myFriendsList is a list and should equal ["me", "myself", "and I"]
How would you go about writing this field type, with the following stipulations?
We don't want to do create a field which just crams all the strings together and separates them with a token in one field like this. It is a good solution in some cases, but we want to keep the string data normalized so tools other than django can query the data.
The field should automatically create any secondary tables needed to store the string data.
The secondary table should ideally have only one copy of each unique string. This is optional, but would be nice to have.
Looking in the Django code it looks like I would want to do something similar to what ForeignKey is doing, but the documentation is sparse.
This leads to the following questions:
Can this be done?
Has it been done (and if so where)?
Is there any documentation on Django about how to extend and override their model classes, specifically their relationship classes? I have not seen a lot of documentation on that aspect of their code, but there is this.
This is comes from this question.
There's some very good documentation on creating custom fields here.
However, I think you're overthinking this. It sounds like you actually just want a standard foreign key, but with the additional ability to retrieve all the elements as a single list. So the easiest thing would be to just use a ForeignKey, and define a get_myfield_as_list method on the model:
class Friends(model.Model):
name = models.CharField(max_length=100)
my_items = models.ForeignKey(MyModel)
class MyModel(models.Model):
...
def get_my_friends_as_list(self):
return ', '.join(self.friends_set.values_list('name', flat=True))
Now calling get_my_friends_as_list() on an instance of MyModel will return you a list of strings, as required.
What you have described sounds to me really similar to the tags.
So, why not using django tagging?
It works like a charm, you can install it independently from your application and its API is quite easy to use.
I also think you're going about this the wrong way. Trying to make a Django field create an ancillary database table is almost certainly the wrong approach. It would be very difficult to do, and would likely confuse third party developers if you are trying to make your solution generally useful.
If you're trying to store a denormalized blob of data in a single column, I'd take an approach similar to the one you linked to, serializing the Python data structure and storing it in a TextField. If you want tools other than Django to be able to operate on the data then you can serialize to JSON (or some other format that has wide language support):
from django.db import models
from django.utils import simplejson
class JSONDataField(models.TextField):
__metaclass__ = models.SubfieldBase
def to_python(self, value):
if value is None:
return None
if not isinstance(value, basestring):
return value
return simplejson.loads(value)
def get_db_prep_save(self, value):
if value is None:
return None
return simplejson.dumps(value)
If you just want a django Manager-like descriptor that lets you operate on a list of strings associated with a model then you can manually create a join table and use a descriptor to manage the relationship. It's not exactly what you need, but this code should get you started.
Thanks for all those that answered. Even if I didn't use your answer directly the examples and links got me going in the right direction.
I am not sure if this is production ready, but it appears to be working in all my tests so far.
class ListValueDescriptor(object):
def __init__(self, lvd_parent, lvd_model_name, lvd_value_type, lvd_unique, **kwargs):
"""
This descriptor object acts like a django field, but it will accept
a list of values, instead a single value.
For example:
# define our model
class Person(models.Model):
name = models.CharField(max_length=120)
friends = ListValueDescriptor("Person", "Friend", "CharField", True, max_length=120)
# Later in the code we can do this
p = Person("John")
p.save() # we have to have an id
p.friends = ["Jerry", "Jimmy", "Jamail"]
...
p = Person.objects.get(name="John")
friends = p.friends
# and now friends is a list.
lvd_parent - The name of our parent class
lvd_model_name - The name of our new model
lvd_value_type - The value type of the value in our new model
This has to be the name of one of the valid django
model field types such as 'CharField', 'FloatField',
or a valid custom field name.
lvd_unique - Set this to true if you want the values in the list to
be unique in the table they are stored in. For
example if you are storing a list of strings and
the strings are always "foo", "bar", and "baz", your
data table would only have those three strings listed in
it in the database.
kwargs - These are passed to the value field.
"""
self.related_set_name = lvd_model_name.lower() + "_set"
self.model_name = lvd_model_name
self.parent = lvd_parent
self.unique = lvd_unique
# only set this to true if they have not already set it.
# this helps speed up the searchs when unique is true.
kwargs['db_index'] = kwargs.get('db_index', True)
filter = ["lvd_parent", "lvd_model_name", "lvd_value_type", "lvd_unique"]
evalStr = """class %s (models.Model):\n""" % (self.model_name)
evalStr += """ value = models.%s(""" % (lvd_value_type)
evalStr += self._params_from_kwargs(filter, **kwargs)
evalStr += ")\n"
if self.unique:
evalStr += """ parent = models.ManyToManyField('%s')\n""" % (self.parent)
else:
evalStr += """ parent = models.ForeignKey('%s')\n""" % (self.parent)
evalStr += "\n"
evalStr += """self.innerClass = %s\n""" % (self.model_name)
print evalStr
exec (evalStr) # build the inner class
def __get__(self, instance, owner):
value_set = instance.__getattribute__(self.related_set_name)
l = []
for x in value_set.all():
l.append(x.value)
return l
def __set__(self, instance, values):
value_set = instance.__getattribute__(self.related_set_name)
for x in values:
value_set.add(self._get_or_create_value(x))
def __delete__(self, instance):
pass # I should probably try and do something here.
def _get_or_create_value(self, x):
if self.unique:
# Try and find an existing value
try:
return self.innerClass.objects.get(value=x)
except django.core.exceptions.ObjectDoesNotExist:
pass
v = self.innerClass(value=x)
v.save() # we have to save to create the id.
return v
def _params_from_kwargs(self, filter, **kwargs):
"""Given a dictionary of arguments, build a string which
represents it as a parameter list, and filter out any
keywords in filter."""
params = ""
for key in kwargs:
if key not in filter:
value = kwargs[key]
params += "%s=%s, " % (key, value.__repr__())
return params[:-2] # chop off the last ', '
class Person(models.Model):
name = models.CharField(max_length=120)
friends = ListValueDescriptor("Person", "Friend", "CharField", True, max_length=120)
Ultimately I think this would still be better if it were pushed deeper into the django code and worked more like the ManyToManyField or the ForeignKey.
I think what you want is a custom model field.

Categories

Resources