Flask中使用SqlAlchemy的较好方法(根据数据库已有表,产生符合Flask-SqlAlchemy要求的models的定义)

来源:互联网 发布:java poi 跨行合并 编辑:程序博客网 时间:2024/06/03 16:32

首先安装 Flask-SqlAlchem 和 SqlAutoCode

在配置文件settings中设置

 

Python代码  收藏代码
  1. SQLALCHEMY_DATABASE_URI = 'mysql://kw3:123456@localhost/kw3_article'  
  2. SQLALCHEMY_BINDS = {  
  3.     'game''mysql://kw3:123456@localhost/kw_game',  
  4. }  
  5. TABLE_PREFIX = 't_'  

 

 将最后面的代码放到scripts下,并运行它。  如存为generate_models.py文件,python  scripts/generate_models.py

在项目入口文件中

 

Python代码  收藏代码
  1. #-*- coding: utf-8 -*-  
  2. from flask import Flask  
  3. from models.base.db import db  
  4.   
  5. app = Flask(__name__)  
  6. db.init_app(app)  

 你就可以在view中使用了,具体参考  http://packages.python.org/Flask-SQLAlchemy/index.html

 

以下代码关于from sqlalchemy.ext.declarative import _deferred_relationship引入错误的问题(cannot import name _deferred_relationship):

原因:你的sqlalchemy版本高于0.7

解决:修改sqlautocode文件夹下的declarative.py文件,将from sqlalchemy.ext.declarative import _deferred_relationship改为

from sqlalchemy.ext.declarative.clsregistry import _deferred_relationship

 

自动产生models的脚本:

 

Python代码  收藏代码
  1. #-*- coding: utf-8 -*-  
  2. #使用SqlAutocode,根据数据库已有表,产生符合Flask-SqlAlchemy要求的models的定义  
  3.   
  4. import os.path  
  5. from flask import Flask  
  6. from sqlautocode import config  
  7. from sqlautocode.declarative import *  
  8. from sqlautocode.formatter import _repr_coltype_as  
  9. from flask.ext.sqlalchemy import SQLAlchemy  
  10.   
  11. singular = plural = lambda x: x  
  12. constants.COLUMN = 'db.' + constants.COLUMN  
  13. constants.HEADER_DECL = """from sqlalchemy import * 
  14. if __name__ == '__main__': 
  15.     from flask import Flask 
  16.     from flask.ext.sqlalchemy import SQLAlchemy 
  17.  
  18.     app = Flask(__name__) 
  19.     app.config['SQLALCHEMY_DATABASE_URI'] = '%s' 
  20.     db = SQLAlchemy(app) 
  21. else: 
  22.     from .db import db 
  23.  
  24.  
  25. """  
  26.   
  27.   
  28. def no_prefix_wrapper(f, prefix=None):  
  29.     def _name2label(name, schema=None):  
  30.         if schema:  
  31.             if name.startswith(schema+'.'):  
  32.                 name = '.'.join(name.split('.')[1:])  
  33.         if prefix and name.startswith(prefix):  
  34.             name = name[ len(prefix):]  
  35.         label = str(''.join([s.capitalize() for s in  
  36.                    re.findall(r'([A-Z][a-z0-9]+|[a-z0-9]+|[A-Z0-9]+)', name)]))  
  37.         return label  
  38.     return _name2label  
  39.   
  40.   
  41. def column_repr(self):  
  42.   
  43.     kwarg = []  
  44.     if self.key != self.name:  
  45.         kwarg.append( 'key')  
  46.   
  47.     if hasattr(self'primary_key'and self.primary_key:  
  48.         self.primary_key = True  
  49.         kwarg.append( 'primary_key')  
  50.   
  51.     if not self.nullable:  
  52.         kwarg.append( 'nullable')  
  53.     if self.onupdate:  
  54.         kwarg.append( 'onupdate')  
  55.     if self.default:  
  56.         kwarg.append( 'default')  
  57.     ks = ', '.join('%s=%r' % (k, getattr(self, k)) for k in kwarg)  
  58.     if self.server_default:  
  59.         ks += ', ' if ks else ''  
  60.         ks += ('default=%s' % self.server_default.arg.text)  
  61.   
  62.     name = self.name  
  63.   
  64.     if not hasattr(config, 'options'and self.config.options.generictypes:  
  65.         coltype = repr(self.type)  
  66.     elif type(self.type).__module__ == 'sqlalchemy.types':  
  67.         coltype = repr(self.type)  
  68.     else:  
  69.         # Try to 'cast' this column type to a cross-platform type  
  70.         # from sqlalchemy.types, dropping any database-specific type  
  71.         # arguments.  
  72.         for base in type(self.type).__mro__:  
  73.             if (base.__module__ == 'sqlalchemy.types' and  
  74.                 base.__name__ in sqlalchemy.__all__):  
  75.                 coltype = _repr_coltype_as(self.type, base)  
  76.                 break  
  77.         # FIXME: if a dialect has a non-standard type that does not  
  78.         # derive from an ANSI type, there's no choice but to ignore  
  79.         # generic-types and output the exact type. However, import  
  80.         # headers have already been output and lack the required  
  81.         # dialect import.  
  82.         else:  
  83.             coltype = repr(self.type)  
  84.   
  85.     data = {'name'self.name,  
  86.             'type': coltype,  
  87.             'constraints'', '.join(["ForeignKey('%s')"%cn.target_fullname for cn in self.foreign_keys]),  
  88.             'args': ks and ks or '',  
  89.             }  
  90.   
  91.     if data['constraints']:  
  92.         if data['constraints']: data['constraints'] = ', ' + data['constraints']  
  93.     if data['args']:  
  94.         if data['args']: data['args'] = ', ' + data['args']  
  95.   
  96.     return constants.COLUMN % data  
  97.   
  98.   
  99. class FlaskModelFactory(ModelFactory):  
  100.   
  101.     def __init__(self, name, conn):  
  102.         self.name = name  
  103.         argv = ['sqlautocode', conn, '-d''-g''-i']  
  104.         config.configure(argv)  
  105.         config.interactive, config.schema, config.example = NoneNoneFalse  
  106.         super(FlaskModelFactory, self).__init__(config)  
  107.   
  108.     def _table_repr(self, table):  
  109.         s = "db.Table(u'%s',\n"%(table.name)  
  110.         for column in table.c:  
  111.             s += "    %s,\n"%column_repr(column)  
  112.         if self.name != "default":  
  113.             s +="    info={'bind_key': '%s'}\n"%self.name  
  114.         #if table.schema:  
  115.         #    s +="    schema='%s'\n"%table.schema  
  116.         s+=")"  
  117.         return s  
  118.   
  119.     def create_model(self, table):  
  120.         #partially borrowed from Jorge Vargas' code  
  121.         #http://dpaste.org/V6YS/  
  122.         log.debug('Creating Model from table: %s'%table.name)  
  123.   
  124.         model_name = self.find_new_name(singular(name2label(table.name)), self.used_model_names)  
  125.         self.used_model_names.append(model_name)  
  126.         is_many_to_many_table = self.is_many_to_many_table(table)  
  127.         table_name = self.find_new_name(table.name, self.used_table_names)  
  128.         self.used_table_names.append(table_name)  
  129.   
  130.         mtl = self.model_table_lookup  
  131.   
  132.   
  133.         class Temporal(self.DeclarativeBase):  
  134.             __table__ = table  
  135.  
  136.             @classmethod  
  137.             def _relation_repr(cls, rel):  
  138.                 target = rel.argument  
  139.                 if target and inspect.isfunction(target):  
  140.                     target = target()  
  141.                 if isinstance(target, Mapper):  
  142.                     target = target.class_  
  143.                 target = target.__name__  
  144.                 primaryjoin=''  
  145.                 lookup = mtl()  
  146.                 if rel.primaryjoin is not None and hasattr(rel.primaryjoin, 'right'):  
  147.                     right_lookup = lookup.get(rel.primaryjoin.right.table.name, '%s.c'%rel.primaryjoin.right.table.name)  
  148.                     left_lookup = lookup.get(rel.primaryjoin.left.table.name, '%s.c'%rel.primaryjoin.left.table.name)  
  149.   
  150.                     primaryjoin = ", primaryjoin='%s.%s==%s.%s'"%(left_lookup,  
  151.                                                                   rel.primaryjoin.left.name,  
  152.                                                                   right_lookup,  
  153.                                                                   rel.primaryjoin.right.name)  
  154.                 elif hasattr(rel, '_as_string'):  
  155.                     primaryjoin=', primaryjoin="%s"'%rel._as_string  
  156.   
  157.                 secondary = ''  
  158.                 secondaryjoin = ''  
  159.                 if rel.secondary is not None:  
  160.                     secondary = ", secondary=%s"%rel.secondary.name  
  161.                     right_lookup = lookup.get(rel.secondaryjoin.right.table.name, '%s.c'%rel.secondaryjoin.right.table.name)  
  162.                     left_lookup = lookup.get(rel.secondaryjoin.left.table.name, '%s.c'%rel.secondaryjoin.left.table.name)  
  163.                     secondaryjoin = ", secondaryjoin='%s.%s==%s.%s'"%(left_lookup,  
  164.                                                                   rel.secondaryjoin.left.name,  
  165.                                                                   right_lookup,  
  166.                                                                   rel.secondaryjoin.right.name)  
  167.                 backref=''  
  168. #                if rel.backref:  
  169. #                    backref=", backref='%s'"%rel.backref.key  
  170.                 return "%s = relation('%s'%s%s%s%s)"%(rel.key, target, primaryjoin, secondary, secondaryjoin, backref)  
  171.  
  172.             @classmethod  
  173.             def __repr__(cls):  
  174.                 try:  
  175.                     mapper = None  
  176.                     try:  
  177.                         mapper = class_mapper(cls)  
  178.                     except exc.InvalidRequestError:  
  179.                         log.warn("A proper mapper could not be generated for the class %s, no relations will be created"%model_name)  
  180.                     s = ""  
  181.                     s += "class "+model_name+'(db.Model):\n'  
  182.                     if cls.__bind_key__ != "default":  
  183.                         s += "    __bind_key__ = '%s'\n"%cls.__bind_key__  
  184.                     if is_many_to_many_table:  
  185.                         s += "    __table__ = %s\n\n"%table_name  
  186.                     else:  
  187.                         s += "    __tablename__ = '%s'\n"%table_name  
  188.                         if hasattr(cls'__table_args__'):  
  189.                             #if cls.__table_args__[0]:  
  190.                                 #for fkc in cls.__table_args__[0]:  
  191.                                 #    fkc.__class__.__repr__ = foreignkeyconstraint_repr  
  192.                                 #    break  
  193.                             s+="    __table_args__ = %s\n"%cls.__table_args__  
  194.                         s+="\n"  
  195.                         for column in cls.__table__.c:  
  196.                             s += "    %s = %s\n"%(column.name, column_repr(column))  
  197.                     ess = s  
  198.                     # this is only required in SA 0.5  
  199.                     if mapper and RelationProperty:  
  200.                         for prop in mapper.iterate_properties:  
  201.                             if isinstance(prop, RelationshipProperty):  
  202.                                 s+='    %s\n'%cls._relation_repr(prop)  
  203.                     return s  
  204.   
  205.                 except Exception, e:  
  206.                     log.error("Could not generate class for: %s"%cls.__name__)  
  207.                     from traceback import format_exc  
  208.                     log.error(format_exc())  
  209.                     return ''  
  210.   
  211.   
  212.         #hack the class to have the right classname  
  213.         Temporal.__name__ = model_name  
  214.   
  215.         #set up some blank table args  
  216.         Temporal.__table_args__ = {}  
  217.   
  218.         #add in the schema  
  219.         Temporal.__bind_key__ = self.name  
  220.         #if self.config.schema:  
  221.         #    Temporal.__table_args__[1]['schema'] = table.schema  
  222.   
  223.         #trick sa's model registry to think the model is the correct name  
  224.         if model_name != 'Temporal':  
  225.             Temporal._decl_class_registry[model_name] = Temporal._decl_class_registry['Temporal']  
  226.             del Temporal._decl_class_registry['Temporal']  
  227.   
  228.         #add in single relations  
  229.         fks = self.get_single_foreign_keys_by_column(table)  
  230.         for column, fk in fks.iteritems():  
  231.             related_table = fk.column.table  
  232.             if related_table not in self.tables:  
  233.                 continue  
  234.   
  235.             log.info('    Adding <primary> foreign key for:%s'%related_table.name)  
  236.             backref_name = plural(table_name)  
  237.             rel = relation(singular(name2label(related_table.name, related_table.schema)),  
  238.                            primaryjoin=column==fk.column)#, backref=backref_name)  
  239.   
  240.             setattr(Temporal, related_table.name, _deferred_relationship(Temporal, rel))  
  241.   
  242.         #add in the relations for the composites  
  243.         for constraint in table.constraints:  
  244.             if isinstance(constraint, ForeignKeyConstraint):  
  245.                 if len(constraint.elements) >1:  
  246.                     related_table = constraint.elements[0].column.table  
  247.                     related_classname = singular(name2label(related_table.name, related_table.schema))  
  248.   
  249.                     primary_join = "and_(%s)"%', '.join(["%s.%s==%s.%s"%(model_name,  
  250.                                                                         k.parent.name,  
  251.                                                                         related_classname,  
  252.                                                                         k.column.name)  
  253.                                                       for k in constraint.elements])  
  254.                     rel = relation(related_classname,  
  255.                                     primaryjoin=primary_join  
  256.                                     #foreign_keys=[k.parent for k in constraint.elements]  
  257.                                )  
  258.   
  259.                     rel._as_string = primary_join  
  260.                     setattr(Temporal, related_table.name, rel) # _deferred_relationship(Temporal, rel))  
  261.   
  262.   
  263.         #add in many-to-many relations  
  264.         for join_table in self.get_related_many_to_many_tables(table.name):  
  265.   
  266.             if join_table not in self.tables:  
  267.                 continue  
  268.             primary_column = [c for c in join_table.columns if c.foreign_keys and list(c.foreign_keys)[0].column.table==table][0]  
  269.   
  270.             for column in join_table.columns:  
  271.                 if column.foreign_keys:  
  272.                     key = list(column.foreign_keys)[0]  
  273.                     if key.column.table is not table:  
  274.                         related_column = related_table = list(column.foreign_keys)[0].column  
  275.                         related_table = related_column.table  
  276.                         if related_table not in self.tables:  
  277.                             continue  
  278.                         log.info('    Adding <secondary> foreign key(%s) for:%s'%(key, related_table.name))  
  279.                         setattr(Temporal, plural(related_table.name), _deferred_relationship(Temporal,  
  280.                                  relation(singular(name2label(related_table.name,  
  281.                                                      related_table.schema)),  
  282.                                           secondary=join_table,  
  283.                                           primaryjoin=list(primary_column.foreign_keys)[0].column==primary_column,  
  284.                                           secondaryjoin=column==related_column  
  285.                                           )))  
  286.                         break;  
  287.   
  288.         return Temporal  
  289.   
  290.   
  291. def gen_models_dir(app, models_dir):  
  292.     #找到并建立models文件夹和__init__.py文件  
  293.     if not models_dir:  
  294.         app_root = app.config.get('APPLICATION_ROOT''')  
  295.         if not app_root:  
  296.             app_root = os.path.dirname( os.path.dirname( os.path.realpath(__file__) ) )  
  297.         models_dir = os.path.join(app_root, 'models')  
  298.     if not os.path.exists(models_dir):  
  299.         os.mkdir(models_dir)  
  300.     init_file = os.path.join(models_dir, '__init__.py')  
  301.     with open(init_file, 'wb') as fh:  
  302.         fh.write('#-*- coding: utf-8 -*-\n')  
  303.     return models_dir  
  304.   
  305.   
  306. def write_db_file(db_file):  
  307.     #建立数据库定义文件  
  308.     with open(db_file, 'wb') as fh:  
  309.         fh.write('#-*- coding: utf-8 -*-\n')  
  310.         fh.write('\n')  
  311.         fh.write('from flask.ext.sqlalchemy import SQLAlchemy\n')  
  312.         fh.write('\n\n')  
  313.         fh.write('db = SQLAlchemy()\n')  
  314.   
  315.   
  316. def write_schema_file(factory, schema_file, name='default'):  
  317.     #建立数据库定义文件  
  318.     with open(schema_file, 'wb') as fh:  
  319.         fh.write("#-*- coding: utf-8 -*-\n")  
  320.         fh.write('\n')  
  321.         fh.write( repr(factory) )  
  322.         fh.write('\n')  
  323.         fh.write("if __name__ == '__main__':\n")  
  324.         if name == 'default':  
  325.             fh.write("    db.create_all(bind=None)\n")  
  326.         else:  
  327.             fh.write("    db.create_all(bind=['%s'])\n" % name)  
  328.   
  329.   
  330. def generate_models(app, models_dir=None):  
  331.     db = SQLAlchemy(app)  
  332.     conns = {  
  333.         'default': app.config.get('SQLALCHEMY_DATABASE_URI'or {},  
  334.     }  
  335.     conns.update( app.config.get('SQLALCHEMY_BINDS'or {} )  
  336.   
  337.     models_dir = gen_models_dir(app, models_dir)  
  338.     db_file = os.path.join(models_dir, 'db.py')  
  339.     if not os.path.exists(db_file):  
  340.         write_db_file(db_file)  
  341.     for name, conn in conns.items():  
  342.         if not conn:  
  343.             continue  
  344.         schema_file = os.path.join(models_dir, '%s.py' % name)  
  345.         if not os.path.exists(schema_file):  
  346.             factory = FlaskModelFactory(name, conn)  
  347.             write_schema_file(factory, schema_file, name)  
  348.   
  349.   
  350. if __name__ == '__main__':  
  351.     import sys  
  352.     sys.path.append(  
  353.         os.path.dirname( os.path.dirname( os.path.realpath(__file__) ) )  
  354.     )  
  355.     app = Flask(__name__)  
  356.     app.config.from_object('settings')  
  357.     name2label = no_prefix_wrapper(name2label, app.config.get('TABLE_PREFIX'))  
  358.     generate_models(app)  
0 0
原创粉丝点击