Is it possible to use namedtuple with SQLalchemy?

I'm trying to get namedtuple to work with SQLalchemy, but to no avail .. Web search is not very covered, and I am new to Python and SQLalchemy, so I'm not sure if I'm chasing windmills: (The basic idea is that I have named element i.e.

Point=namedtuple('Point',['x','y']) 

which basically creates the Point class (tuple), if I'm right. At first, this works fine, and I can create objects such as:

 p=Point(3,4) 

But after I create the engine, etc., and the call box, I can not create any objects without receiving this error:

 Traceback (most recent call last): File "<pyshell#62>", line 1, in <module> f=Point(3,4) TypeError: __init__() takes exactly 1 argument (3 given) 

Any ideas why this is happening? Does anyone know how to get namedtuple to work with sqlalchemy? Of course, I can define my own Point class, but I'm obsessed with doing the namedtuple job now. I am using Python 2.7, SQLalchemy 0.6.6 (sqlite engine)

Example:

I am trying something like this:

 from sqlalchemy import * from sqlalchemy.orm import * from collections import namedtuple Point=namedtuple('Point',['x','y'],verbose=True) p=Point(3,4) db=create_engine('sqlite:///pointtest.db') metadata=MetaData() pointxy=Table('pointxy',metadata, Column('no',Integer,primary_key=True), Column('x',Integer), Column('y',Integer), sqlite_autoincrement=True) metadata.create_all(db) m=mapper(Point, pointxy) Session=sessionmaker(bind=db) session=Session() f=Point(3,4) 

The basic idea is that I want a named collection of things that can be easily stored in a database. So:

 class Bunch: __init__ = lambda self, **kw: setattr(self, '__dict__', kw) 

will not work with sqlalchemy (i think). I can create a Bunch class, but I won’t know in advance how many ints I want to keep in my collection. I will install it before creating my database. Hope I make sense.

+6
source share
2 answers

Namedtuples has a bunch of behaviors that make them unsuitable for comparing with sqlalchemy: most importantly, namedtuples cannot be changed after they are created. This means that you cannot use namedtuple to track the status of a row in the database after an insert operation. You would usually want to do something like this:

 class MyDataHolder(namedtuple('MyDataHolder', ('id', 'my_value')): pass mapper(MyDataHolder, MyDataMeta) ... newRow = MyDataHolder(None, 'AAA') ... session.add(newRow) 

When the session code executes SQL to add new data to the database, it will want to update newRow so that newRow.id matches the id assigned to the database for your row. But since newRow is an immutable tuple, the identifier cannot be changed to store the primary key that was returned from the database. This leads to the fact that namedtuples are largely unsuitable for mappers.

__Init__ problems occur because namedtuple is initialized to __new__ and then no change is expected. __init __ () is called after the object has been created and therefore has no effect. Thus, __init__ for namedtuple defines only one argument: self. I assume that Mapper assumes that __init __ () handles class initialization and is not aware of __new__ and immutable types. It looks like they are calling classname .__ init __ () with arguments that are passed at creation time. You can "fix" this by specifying your own initializer: __init __ (self, * args), but then you get into the weakref problem.

The weakref error occurs because namedtuples use __slots__ to store their values, rather than mutable __dict__s. I know that using __slots__ is a memory optimization, so you can effectively store many named elements. I assume that he expected that namedtuple will not change after its creation. This includes adding attributes, so using __slots__ is a good memory optimization. However, I do not claim to understand why the author of the namedtuple class does not support weak references. It would not be particularly difficult, but there is probably a really good reason why I am missing.

I ran into this question this morning and walked around it, specifying my own data mapping class that was initialized with the _fieldset attribute. This sets a (sub) set of fields that are interesting to me when displaying. Then I had some time and read the documentation on how named functions are implemented along with a bunch of other python internal components. I think I get most of why this doesn't work, but I'm sure there are python experts who are better at this than me.

- Chris

+3
source

The match operator seems to add _init_method. So, let's do the following after the mapper statement works again:

 del Point.__init__ 

I'm not sure that using a map for this type of thing is the right idea. For the mapper to work properly, most likely, you need a primary key ("no"), which your nametuple currently lacks in space.

+1
source

Source: https://habr.com/ru/post/898509/


All Articles