The following set-up illustrates my problem:
from peewee import (Model,
SqliteDatabase)
from peewee import (CharField,
ForeignKeyField,
IntegerField)
from playhouse.shortcuts import model_to_dict
database = SqliteDatabase(':memory:')
class BaseModel(Model):
class Meta:
database = database
class Description(BaseModel):
i_am = CharField(
db_column='I_AM'
)
class Meta:
db_table = 'DESCRIPTION'
# This is not a table, just a convenient class to inherit from.
class ValueBase(BaseModel):
value = IntegerField(
db_column='VALUE'
)
description = ForeignKeyField(
db_column='I_AM_ID',
rel_model=Description,
to_field='id'
)
class SpecificValue(ValueBase):
class Meta:
db_table = 'SPECIFIC_VALUE'
if __name__ == '__main__':
models = [Description, SpecificValue]
database.create_tables(models)
description = Description(
i_am = 'prime'
)
description.save()
specific = SpecificValue(
value=7,
description=description
)
specific.save()
### This works
print model_to_dict(
specific
)
### This does NOT work
print model_to_dict(
specific,
backrefs=True
)
The problem is that when I use model_to_dict and ask for back references, then the function thinks specific is in a table valuebase and not in a table called SPECIFIC_VALUE. Does anyone know how to do this sort of inheritance and correctly set table names?
EDIT
The traceback is as follows:
Traceback (most recent call last):
File "/home/lafras/Projects/Cloud/eg.py", line 61, in <module>
backrefs=True
File "build/bdist.linux-x86_64/egg/playhouse/shortcuts.py", line 96, in model_to_dict
File "build/bdist.linux-x86_64/egg/playhouse/shortcuts.py", line 117, in model_to_dict
File "build/bdist.linux-x86_64/egg/peewee.py", line 2794, in __iter__
File "build/bdist.linux-x86_64/egg/peewee.py", line 2787, in execute
File "build/bdist.linux-x86_64/egg/peewee.py", line 2470, in _execute
File "build/bdist.linux-x86_64/egg/peewee.py", line 3199, in execute_sql
File "build/bdist.linux-x86_64/egg/peewee.py", line 3048, in __exit__
File "build/bdist.linux-x86_64/egg/peewee.py", line 3191, in execute_sql
peewee.OperationalError: no such table: valuebase
If SpecificValue inherits directly from BaseModel:
class SpecificValue(BaseModel):
value = IntegerField(
db_column='VALUE'
)
description = ForeignKeyField(
db_column='I_AM_ID',
rel_model=Description,
to_field='id'
)
class Meta:
db_table = 'SPECIFIC_VALUE'
then the output is as expected:
{'id': 1, 'value': 7, 'description': {'id': 1, 'i_am': 'prime'}}
Can you explain a bit more thoroughly what's not working correctly? I don't see any back-references that would be associated with the SpecificValue table, since there are not other models that have a foreign key to it.
EDIT:
OK, so what's happening here is that when you specify backrefs, peewee is trying to go from:
SpecificValue -> Description -> ValueBase (which has a foreign key to description).
The fix is to explicitly exclude the backref:
model_to_dict(specific, backrefs=True, exclude=[Description.valuebase_set])
Related
I've created a small sqlite3 database using peewee to try to understand foreign keys and get them working in a simple database.
from peewee import *
db = SqliteDatabase('database.db')
class Category(Model):
category = CharField()
class Meta:
database = db
class Subcat(Model):
description = BlobField()
sub_cat = CharField()
top_category = ForeignKeyField(Category, related_name='group')
class Meta:
database = db
db.connect()
db.create_tables([Category, Subcat], safe=True)
I have then created a controller.py file to handle all the database transactions.
(Updated)
from peewee import *
from database import *
class ModCat(Category):
def add_cat(self,category):
update = Category.create(category=category)
def get_cat(self):
categories = Category.select(Category.category).order_by(Category.category)
return categories
class ModSubCat(Subcat):
def save_sub_cat(self, sub, master, desc):
name = Category().select().where(Category.name==master)
update = Subcat.create(sub_cat=sub, top_category=name, description=desc)
Finally, a main.py that allows me to enter data into the database with a simple form created in wxFormBuilder.
from gui import *
from controller import *
class Menu(InDb):
def __init__(self, parent):
InDb.__init__(self, parent)
get_categories = ModCat()
list = get_categories.get_cat()
new_list = []
for thing in list:
new_list.append(thing.category)
print new_list
for f in new_list:
self.m_comboBox1.Append(f)
def click_save( self, event ):
new_cat = ModCat()
new_cat.add_cat(self.m_textCtrl3.GetValue())
self.GetParent() # This assigns parent frame to frame.
self.Close() # This then closes frame removing the main menu.
frame = Menu(None)
frame.Centre()
frame.Show()
def sub_save( self, event ):
sub = self.m_textCtrl5.GetValue()
master = self.m_comboBox1.GetValue()
desc = "Hi"
update = ModSubCat()
update.save_sub_cat(sub, master, desc)
#Start App by calling sub class of MainMenu
if __name__ == '__main__':
app = wx.App(0)
frame = Menu(None)
frame.Centre()
frame.Show()
app.MainLoop()
The database creates with no errors but when I run main.py it always returns this error.
Traceback (most recent call last):
File "C:/Users/********/PycharmProjects/RAMS/main.py", line 2, in <module>
from controller import *
File "C:\Users\********\PycharmProjects\RAMS\controller.py", line 13, in <module>
class ModSubCat(Subcat):
File "C:\Python27\lib\site-packages\peewee.py", line 4710, in __new__
field.add_to_class(cls, name)
File "C:\Python27\lib\site-packages\peewee.py", line 1437, in add_to_class
invalid('The related_name of %(field)s ("%(backref)s") '
File "C:\Python27\lib\site-packages\peewee.py", line 1431, in invalid
raise AttributeError(msg % context)
AttributeError: The related_name of modsubcat.top_category ("group") is already in use by another foreign key.
I've changed related_by value with no luck.
Is the fact that I have a Sub Class ModSubCat in controller.py causing a problem?
Ive removed the related_name='group'
from
top_category = ForeignKeyField(Category, related_name='group') and now all works.
What is the related_name for?
I just start a simple project called flask_wiki this days and I'm using some flask extensions as the follows:
Flask-SQLAlchemy
Flask-Restful
MarshMallow
Well, I just discovered that the MarshMallow project provides a class called 'ModelSchema', which reads all fields from my SQLAlchemy Model and provide a fully automated (de)serialializer.
In my case, I created a 'GUID' Field which is RDBM agnostic and inserted it on my Sqlalchemy model as follows:
from flask.ext.sqlalchemy import SQLAlchemy
from flask_wiki.backend.custom_fields import GUIDField
class Page(db.Model):
"""
Implements the Page Model.
"""
guid = db.Column(GUIDField, primary_key=True, default=uuid.uuid4)
name = db.Column(db.String, nullable=False)
raw_content = db.Column(db.Text)
rendered_content = db.Column(db.Text)
def __repr__(self):
return self.__str__()
def __str__(self):
return self.name
The GUIDField is implemented is this way:
from sqlalchemy.types import TypeDecorator, CHAR
import uuid
class GUIDField(TypeDecorator):
# Platform independent GUID Implementation that uses little endianess.
impl = CHAR
def load_dialect_impl(self, dialect):
return dialect.type_descriptor(CHAR(32))
def process_bind_param(self, value, dialect):
if value is None:
return value
else:
if isinstance(value, uuid.UUID):
return value.bytes_le
def process_result_value(self, value, dialect):
if value is None:
return value
else:
return uuid.UUID(bytes_le=value)
The code for create test objects (through mixer) is working; all the guids are generated and verified correctly.
With this in mind, a just created the following MarshMallow Serializer Field:
from marshmallow import fields
import uuid
class GUIDSerializationField(fields.Field):
def _serialize(self, value, attr, obj):
if value is None:
return value
else:
if isinstance(value, uuid.UUID):
return str(value)
else:
return None
Finally, I created the SerializerClass:
from flask_wiki.backend.backend import marsh
from flask_wiki.backend.custom_serialization_fields import GUIDSerializationField
from flask_wiki.backend.models import Page
from marshmallow import fields
class PageSchema(marsh.ModelSchema):
class Meta:
model = Page
guid = GUIDSerializationField()
page_schema = PageSchema()
pages_schema = PageSchema(many=True)
I tried to use this last code with and without inserting the guid field, but in all cases the following error occurs:
Traceback (most recent call last):
File "/home/arthas/dev/flask-wiki/flask_wiki/wiki.py", line 3, in <module>
from flask_wiki.backend import backend
File "/home/arthas/dev/flask-wiki/flask_wiki/backend/__init__.py", line 1, in <module>
import flask_wiki.backend.routes
File "/home/arthas/dev/flask-wiki/flask_wiki/backend/routes.py", line 2, in <module>
from flask_wiki.backend.views import PageView
File "/home/arthas/dev/flask-wiki/flask_wiki/backend/views.py", line 3, in <module>
from flask_wiki.backend.serializers import pages_schema, page_schema
File "/home/arthas/dev/flask-wiki/flask_wiki/backend/serializers.py", line 7, in <module>
class PageSchema(marsh.ModelSchema):
File "/home/arthas/env/wiki/lib/python3.5/site-packages/marshmallow/schema.py", line 116, in __new__
dict_cls=dict_cls
File "/home/arthas/env/wiki/lib/python3.5/site-packages/marshmallow_sqlalchemy/schema.py", line 53, in get_declared_fields
declared_fields = mcs.get_fields(converter, opts)
File "/home/arthas/env/wiki/lib/python3.5/site-packages/marshmallow_sqlalchemy/schema.py", line 77, in get_fields
return converter.fields_for_model(opts.model, fields=opts.fields, exclude=opts.exclude)
File "/home/arthas/env/wiki/lib/python3.5/site-packages/marshmallow_sqlalchemy/convert.py", line 75, in fields_for_model
field = self.property2field(prop)
File "/home/arthas/env/wiki/lib/python3.5/site-packages/marshmallow_sqlalchemy/convert.py", line 93, in property2field
field_class = self._get_field_class_for_property(prop)
File "/home/arthas/env/wiki/lib/python3.5/site-packages/marshmallow_sqlalchemy/convert.py", line 151, in _get_field_class_for_property
field_cls = self._get_field_class_for_column(column)
File "/home/arthas/env/wiki/lib/python3.5/site-packages/marshmallow_sqlalchemy/convert.py", line 121, in _get_field_class_for_column
return self._get_field_class_for_data_type(column.type)
File "/home/arthas/env/wiki/lib/python3.5/site-packages/marshmallow_sqlalchemy/convert.py", line 143, in _get_field_class_for_data_type
'Could not find field column of type {0}.'.format(types[0]))
marshmallow_sqlalchemy.exceptions.ModelConversionError: Could not find field column of type <class 'flask_wiki.backend.custom_fields.GUIDField'>.
So, I finally ask: how to use marshmallow to serialize a custom sqlalchemy field?
You need to create your own Converter.
Try something like this:
import uuid
from flask_wiki.backend.models import Page
from flask_wiki.backend.backend import marsh
from marshmallow_sqlalchemy.convert import ModelConverter
from flask_wiki.backend.custom_fields import GUIDField
from marshmallow import fields
#Here you are overwriting the list of types of the SQLAlchemy, adding your custom type
class GUIDConverter(ModelConverter):
SQLA_TYPE_MAPPING = dict(
list(ModelConverter.SQLA_TYPE_MAPPING.items()) +
[(GUIDField, fields.Str)]
)
class GUIDSerializationField(fields.Field):
def _serialize(self, value, attr, obj):
if value is None:
return value
else:
if isinstance(value, uuid.UUID):
return str(value)
else:
return None
class PageSchema(marsh.ModelSchema):
class Meta:
model = Page
model_converter = GUIDConverter #Tell to Marshmallow to use your custom converter for this model
guid = GUIDSerializationField(attribute="guid")
You can also see this link for help.
I hope it helped and sorry for my bad english
According to what I've read in the docs, you can specify a model_converter attribute in your ModelSchema Meta class. The ModelConverter class has a SQLA_TYPE_MAPPING attribute you can override in a subclass to add your custom GUID field to the types detected by the automatic schema generator.
That said, I've never used it so I don't know if this will work or not.
I'm using mongokit with flask, and everytime I try to use a collection I created, I receive the error No collection found
I defined my collections in a separated file models.py. It looks like this:
from mongokit import Connection, Document
import os
import sys
here = os.path.dirname(os.path.abspath(__file__))
path = os.path.abspath(os.path.join(here, 'settings'))
sys.path.append(path)
from settings import base as settings
connection = Connection()
#connection.register
class Contact(Document):
__database__ = settings.MONGO_DBNAME
__collection__ = "Contact"
structure = {
"name":unicode,
"mobile_number":unicode,
}
required_fields = ["name"]
#connection.register
class User(Document):
__database__ = settings.MONGO_DBNAME
__collection__ = 'User'
structure = {
"username":unicode,
"twitter_access_token":unicode,
"twitter_token_secret":unicode,
"contacts":[Contact]
}
required_fields = ["username"]
default_values = {
"twitter_access_token": "",
"twitter_token_secret": ""
}
But then I tried:
>>> from models import User
>>> u = User()
>>> u["username"] = "somename"
>>> u.save()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/fernandocezar/.virtualenvs/contacts/lib/python2.7/site-packages/mongokit/document.py", line 404, in save
self.validate(auto_migrate=False)
File "/Users/fernandocezar/.virtualenvs/contacts/lib/python2.7/site-packages/mongokit/document.py", line 230, in validate
(size_limit, size_limit_str) = self._get_size_limit()
File "/Users/fernandocezar/.virtualenvs/contacts/lib/python2.7/site-packages/mongokit/document.py", line 214, in _get_size_limit
server_version = tuple(self.connection.server_info()['version'].split("."))
File "/Users/fernandocezar/.virtualenvs/contacts/lib/python2.7/site-packages/mongokit/document.py", line 622, in __getattribute__
raise ConnectionError('No collection found')
mongokit.mongo_exceptions.ConnectionError: No collection found
I followed this tutorial, but not even the notation connection.<dbname>.<collection>() works. And yes, there is, indeed, such a collection.
What am I missing?
To quote the tutorial you linked:
To avoid repeating ourselves though, let’s specify the database and
collection name in the Document definition:
#connection.register
class BlogPost(Document):
__collection__ = 'blog_posts'
__database__ = 'blog'
structure = {...}
>>> bp = connection.BlogPost()
In the shell example, the model object is constructed through the connection object. In your case, you were simply doing user = User(). Try creating the user through the same connection instance that you used to register the model (e.g. user = connection.User()).
I have one model (company category) populated by a table - simple names etc. I then have a company model and I'd like to link these two together such that I have have categories in a populated drop down box.
class CompanyCategory(db.Model):
categoryname = db.StringProperty(required=True)
class Company(db.Model):
companyurl = db.StringProperty(required=False)
companycategory = db.ReferenceProperty(CompanyCategory, collection_name='category')
However when I do this I get the following error:
<class 'google.appengine.ext.db.DuplicatePropertyError'>: Class CompanyCategory already has property categoryname
Traceback (most recent call last):
File "/base/data/home/apps/XXX/1.358759458095086806/showcompanies.py", line 52, in <module>
class Company(db.Model):
File "/base/python_runtime/python_lib/versions/1/google/appengine/ext/db/__init__.py", line 514, in __init__
_initialize_properties(cls, name, bases, dct)
File "/base/python_runtime/python_lib/versions/1/google/appengine/ext/db/__init__.py", line 429, in _initialize_properties
attr.__property_config__(model_class, attr_name)
File "/base/python_runtime/python_lib/versions/1/google/appengine/ext/db/__init__.py", line 3656, in __property_config__
self.collection_name))
How do I make this work? I have looked at some examples, but I'm not able to make work in this case. How can I make the companycategory a list.
Here is what is in the table:
CompanyCategory Entities
‹ Prev 20 1-1 Next 20 ›
ID/Name categoryname
id=96001 ss
This is where the model definition is the other way around. I want each company to have have a category which is populated from a table. With the following definition I can get something that resembles what I am looking for:
class CompanyCategory(db.Model):
categoryname = db.StringProperty(required=False)
def __unicode__(self):
return u'%s' % (self.categoryname)
class Company(db.Model):
companyurl = db.StringProperty(required=False)
companyname = db.StringProperty(required=False)
companydesc = db.TextProperty(required=False)
companyaddress = db.PostalAddressProperty(required=False)
companypostcode = db.StringProperty(required=False)
companyemail = db.EmailProperty(required=False)
companycountry = db.StringProperty(required=False)
companyvalid = db.BooleanProperty()
companyentrytime = db.DateTimeProperty(auto_now_add=True)
companylatlong = db.GeoPtProperty()
companycategory = db.ReferenceProperty(CompanyCategory)
I now get a drop down box when I go to add a company - populated from the values in the table. However I'm not getting the value's in the drop down populated into the text value in the datastore upon submission. How should this be done?
Pasting your code into shell.appspot.com works just fine - so whatever the cause of your problem, your sample code is not representative of it. As Christopher Ramirez suggests, you're probably declaring companycategory twice, either in the same model, or in a parent class of the model.
item = Table('Item', metadata, autoload=True, autoload_with=engine, encoding = 'cp1257')
class Item(object):
pass
from sqlalchemy.orm import mapper
mapper(Item, item)
I get error:
line 43, in <module>
mapper(Item, item)
File "C:\Python27\lib\site-packages\sqlalchemy\orm\__init__.py", line 890, in mapper
return Mapper(class_, local_table, *args, **params)
File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 211, in __init__
self._configure_properties()
File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 578, in _configure_properties
setparent=True)
File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 618, in _configure_property
self._log("_configure_property(%s, %s)", key, prop.__class__.__name__)
File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 877, in _log
(self.non_primary and "|non-primary" or "") + ") " +
File "C:\Python27\lib\site-packages\sqlalchemy\util.py", line 1510, in __get__
obj.__dict__[self.__name__] = result = self.fget(obj)
File "C:\Python27\lib\site-packages\sqlalchemy\sql\expression.py", line 3544, in description
return self.name.encode('ascii', 'backslashreplace')
UnicodeDecodeError: 'ascii' codec can't decode byte 0xeb in position 7: ordinal not in range(128)
I am connecting to MSSQL. table autoload seems to work. I only get this error while trying to map.
Thank you all for help!
Mapping the table to a class creates mapped properties on the class. The properties have the same name of the columns, by default. Since python 2.x only allows ascii identifiers, that fails if you have non-ascii column names.
The only solution I can think of is to give the identifiers a different name when mapping the table to a class.
The example below does that. Note that I'm creating the table on the code for simplicity, so anyone can run the code without having existing table. But you could do the same with a reflected table.
#-*- coding:utf-8 -*-
import sqlalchemy as sa
import sqlalchemy.orm
engine = sa.create_engine('sqlite://', echo=True) # new memory-only database
metadata = sa.MetaData(bind=engine)
# create a table. This could be reflected from the database instead:
tb = sa.Table('foo', metadata,
sa.Column(u'id', sa.Integer, primary_key=True),
sa.Column(u'nomé', sa.Unicode(100)),
sa.Column(u'ãéìöû', sa.Unicode(100))
)
tb.create()
class Foo(object):
pass
# maps the table to the class, defining different property names
# for some columns:
sa.orm.mapper(Foo, tb, properties={
'nome': tb.c[u'nomé'],
'aeiou': tb.c[u'ãéìöû']
})
After that you can use Foo.nome to refer to the nomé column and Foo.aeiou to refer to the ãéìöû column.
I faced the same problem and finally managed to do it replacing table['column'].key after autoloading it, just make all your table classes inherit this one and then modify the column name replacement in mapTo method or override manually the desired names with a dictionary and columns_descriptor method. I don't know if this is not the right way to do it but after searching for hours is the best aproach I've got.
class SageProxy(object):
#classmethod
def ismapped(cls, table_name=None):
if mappings:
if table_name:
if mappings.has_key(table_name):
tmap=mappings[table_name]
if tmap.has_key('class'):
tclass=tmap['class']
if tclass is cls:
return True
else:
for m in mappings:
if cls is m['class']:
return True
return False
#classmethod
def mappingprops(cls):
#override this to pass properties to sqlalchemy mapper function
return None
#classmethod
def columns_descriptors(cls):
#override this to map columns to different class properties names
#return dictionary where key is the column name and value is the desired property name
return {}
#classmethod
def mapTo(cls, table_name, map_opts=None):
if not cls.ismapped(table_name):
tab_obj=Table(table_name,sage_md,autoload=True)
for c in tab_obj.c:
#clean field names
tab_obj.c[c.name].key=c.key.replace(u'%',u'Porcentaje').replace(u'ñ',u'ny').replace(u'Ñ',u'NY').replace(u'-',u'_')
for k,v in cls.columns_descriptors():
if tab_obj.c[k]:
tab_obj.c[k].key=v
mapper(cls, tab_obj, properties=cls.mappingprops())
mappings[table_name]={'table':tab_obj,'class':cls}
return cls
I expect it will be usefull
I found that I could do this with a simple addition to my reflected class:
metadata = MetaData(bind=engine, reflect=True)
sm = sessionmaker(bind=engine)
class tblOrders(Base):
__table__ = metadata.tables['tblOrders']
meter = __table__.c['Meter#']
meter is now mapped to the underlying Meter# column, which allows this code to work:
currOrder = tblOrders()
currOrder.meter = '5'
Without the mapping, python sees it as a broken statement becase Meter followed by a comment does not exist in the object.