Peewee - How to Convert a Dict into a Model - python

Lets say I have
import peewee
class Foo(Model):
name = CharField()
I would like to do the following:
f = {id:1, name:"bar"}
foo = Foo.create_from_dict(f)
Is this native in Peewee? I was unable to spot anything in the source code.
I've wrote this function which works but would rather use the native function if it exists:
#clazz is a string for the name of the Model, i.e. 'Foo'
def model_from_dict(clazz, dictionary):
#convert the string into the actual model class
clazz = reduce(getattr, clazz.split("."), sys.modules[__name__])
model = clazz()
for key in dictionary.keys():
#set the attributes of the model
model.__dict__['_data'][key] = dictionary[key]
return model
I have a web page that displays all the foos and allows the user to edit them. I would like to be able to pass a JSON string to the controller, where I would convert it to a dict and then make Foos out of it, so I can update as necessary.

If you have a dict, you can simply:
class User(Model):
name = CharField()
email = CharField()
d = {'name': 'Charlie', 'email': 'foo#bar.com'}
User.create(**d)

You could use PickledKeyStore which allows you to save any value as a python dict and it works like Python's pickle library.

Related

How to dynamically modify database request in Django?

I've got a problem with my code. I need to get an instance of a model that looks like this:
class Example(models.Model):
# ...
foo = models.JSONField(default={})
and depending on the user's input (JSON key). For example:
Example.objects.filter(foo__userinput = bar)
How can I manage to don't make JSON key hardcoded?
You can try filter by dict
key = 'foo__%s' % userinput
qfilter = {key: bar}
Example.objects.filter(**qfilter)

use existing field as _id using elasticsearch dsl python DocType

I have class, where I try to set student_id as _id field in elasticsearch. I am referring persistent example from elasticsearch-dsl docs.
from elasticsearch_dsl import DocType, String
ELASTICSEARCH_INDEX = 'student_index'
class StudentDoc(DocType):
'''
Define mapping for Student type
'''
student_id = String(required=True)
name = String(null_value='')
class Meta:
# id = student_id
index = ELASTICSEARCH_INDEX
I tied by setting id in Meta but it not works.
I get solution as override save method and I achieve this
def save(self, **kwargs):
'''
Override to set metadata id
'''
self.meta.id = self.student_id
return super(StudentDoc, self).save(**kwargs)
I am creating this object as
>>> a = StudentDoc(student_id=1, tags=['test'])
>>> a.save()
Is there any direct way to set from Meta without override save method ?
There are a few ways to assign an id:
You can do it like this
a = StudentDoc(meta={'id':1}, student_id=1, tags=['test'])
a.save()
Like this:
a = StudentDoc(student_id=1, tags=['test'])
a.meta.id = 1
a.save()
Also note that before ES 1.5, one was able to specify a field to use as the document _id (in your case, it could have been student_id), but this has been deprecated in 1.5 and from then onwards you must explicitly provide an ID or let ES pick one for you.

Determine if a property is a backref in sqlalchemy

I have the following relationship set up in a model:
role_profiles = Table('roleprofile', Base.metadata,
Column('role_id', Integer, ForeignKey('role.id')),
Column('profile_id', Integer, ForeignKey('profile.id'))
)
class profile(Base):
__tablename__ = 'profile'
# Columns...
roles = relationship('role', secondary=role_profiles, backref='profiles')
class role(Base):
__tablename__ = 'role'
# Columns...
So as I now understand that it works is that the roles property on the profile object will contain a list of role classes (which it does).
What I want to do is to serialize for each property of the model class generically. It works fine for the top class profile and I determine that there is a list of roles that I should recurse into:
# I need a statement here to check if the field.value is a backref
#if field.value is backref:
# continue
if isinstance(field.value, list):
# Get the json for the list
value = serialize.serialize_to_json(field.value)
else:
# Get the json for the value
value = cls._serialize(field.value)
The problem is that the backref of the relationship adds a pointer back to the profile. The same profile is then serialized and it recurse the roles over and over again until stack overflow.
Is there a way to determine that the property is a backref added by the relationship?
Update
Maybe I should add that it works fine in this case if I remove the backref since I don't need it but I would like to keep it in.
Update
As a temporary fix I added a class property to my base class:
class BaseModelMixin(object):
"""Base mixin for models using stamped data"""
__backref__ = None
and add it like this:
class role(Base):
__tablename__ = 'role'
__backref__ = ('profiles', )
# Columns...
and use it like this in my recursion:
if self.__backref__ and property_name in self.__backref__:
continue
If there is a better way please let me know because this doesn't look optimal.
Not sure if this is the best practice, but this code works for me. It returns True if the attribute is a reference, False if a regular column type.
def is_relation(orm_object, attr_name):
return hasattr(getattr(orm_object.__class__, attr_name).property, 'mapper')
You can create a __relationships__ in your class BaseModelMixin as a #property, which has a list of all relationships name which are not as a backref name in a model.
class BaseModelMixin(object):
"""Base mixin for models using stamped data"""
#property
def __relationships__(self):
"""
Return a list of relationships name which are not as a backref
name in model
"""
back_ref_relationships = list()
items = self.__mapper__.relationships.items()
for (key, value) in items:
if isinstance(value.backref, tuple):
back_ref_relationships.append(key)
return back_ref_relationships
As you have two class profile and role, so
>>> p = profile()
>>> p.__relationships__
# ['roles']
>>> r = role()
>>> r.__relationships__
# []
have a look at inspect
e.g.
from sqlalchemy import inspect
mapper = inspect(MyModelClass)
# dir(mapper)
# mapper.relationships.keys()

Python Inspect - Lookup the data type for a property in a GAE db.model Class

class Employee(db.Model):
firstname = db.StringProperty()
lastname = db.StringProperty()
address1 = db.StringProperty()
timezone = db.FloatProperty() #might be -3.5 (can contain fractions)
class TestClassAttributes(webapp.RequestHandler):
"""
Enumerate attributes of a db.Model class
"""
def get(self):
for item in Employee.properties():
self.response.out.write("<br/>" + item)
#for subitem in item.__dict__:
# self.response.out.write("<br/> --" + subitem)
The above will give me a list of the property names for the variable "item".
My idea of item.__dict__ didn't work because item was a str.
How can I then display the data field type for each property, such as db.FloatProperty() for the property called timezone?
GAE = Google App Engine - but I'm sure the same answer would work for any class.
Thanks,
Neal Walters
Iterate using "for name, property in Employee.properties().items()". The property argument is the Property instance, which you can compare using instanceof.
For problems like these, the interactive Python shell is really handy. If you had used it to poke around at your Employee object, you might have discovered the answer to your question through trial and error.
Something like:
>>> from groups.models import Group
>>> Group.properties()
{'avatar': <google.appengine.ext.db.StringProperty object at 0x19f73b0>,
'created_at': <google.appengine.ext.db.DateTimeProperty object at 0x19f7330>,
'description': <google.appengine.ext.db.TextProperty object at 0x19f7210>,
'group_type': <google.appengine.ext.db.StringProperty object at 0x19f73d0>}
From that you know that the properties() method of a db.Model object returns a dict mapping the model's property names to the actual property objects they represent.
I add the same problem, and the first 2 answers did not help me 100%.
I was not able to get the type information, from the meta data of the class or the
instance property, which is bizarre. So I had to use a dictionary.
The method GetType() will return the type of the property as a string.
Here is my answer:
class RFolder(db.Model):
def GetPropertyTypeInstance(self, pname):
for name, property in self.properties().items():
if name==pname:
return property
return None
def GetType(self, pname):
t = self.GetPropertyTypeInstance(pname)
return RFolder.__DB_PROPERTY_INFO[type(t)]
__DB_PROPERTY_INFO = {
db.StringProperty :"String",
db.ByteStringProperty :"ByteString",
db.BooleanProperty :"Boolean",
db.IntegerProperty :"Integer",
db.FloatProperty :"Float",
db.DateTimeProperty :"DateTime",
db.DateProperty :"Date",
db.TimeProperty :"Time",
db.ListProperty :"List",
db.StringListProperty :"StringList",
db.ReferenceProperty :"Reference",
db.SelfReferenceProperty :"SelfReference",
db.UserProperty :"User",
db.BlobProperty :"Blob",
db.TextProperty :"Text",
db.CategoryProperty :"Category",
db.LinkProperty :"Link",
db.EmailProperty :"Email",
db.GeoPtProperty :"GeoPt",
db.IMProperty :"IM",
db.PhoneNumberProperty :"PhoneNumber",
db.PostalAddressProperty :"PostalAddress",
db.RatingProperty :"Rating"
}

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