+2  A: 

What about using Elixir?

Justin
Elexir is built upon SQLAlchemy, and he stated that SQLAlchemy was bloated.... Isn't software on top of bloatware always bloatware?
Alxandr
No, I don't think it's always bloatware. But it's not using native Python types. I'm talking about coding in our sleep :)Why can't they intelligently translate from native types? Maybe Elixir still has work to do!
Luke Stanley
@Alxandr: Probably, but in my experience half the time when people complain about something being bloated it just means they're too lazy to learn it, or they resent that it includes features they don't need, or they're just generally pissed off and feel like complaining ;-) Elixir does come about as close as reasonably possible to making the sample code in the question work.
David Zaslavsky
If you think this is important, please upvote! Don't just star, we need to search our collective memory banks.
Luke Stanley
@Luke (3 comments up): SQL data types are more restrictive than Python data types, so it's generally not possible to automatically decide which SQL type properly corresponds to any given Python object. Of course an ORM could guess, but it'd be wrong sometimes and that would lead to problems down the line.
David Zaslavsky
Yes, this is true, that's the challenge my friend. If we need a little dialog popping up while I'm prototyping in Cherry Py or web.py, asking for confirmation - no hard feelings?
Luke Stanley
Being a lazy programmer is smart, it's about conserving energy and waste when thinking about our code! Just because some coders have learnt something doesn't mean it should be useful for the next breakthrough. We are talking about transformative evolution here.
Luke Stanley
+1  A: 

Forget ORM! I like vanilla SQL. The python wrappers like psycopg2 for postgreSQL do automatic type conversion, offer pretty good protection against SQL injection, and are nice and simple.

sql = "SELECT * FROM table WHERE id=%s"
data = (5,)
cursor.execute(sql, data)
colinmarc
Why is there SQL syntax in your example code? Did you read the question?
Luke Stanley
http://initd.org/psycopg/docs/usage.html <-- I just looked at this. You are way off!
Luke Stanley
I think people turn to DB Models because they think using plain SQL is too difficult. I was trying to show how simple it is. Maybe I misunderstood the question?
colinmarc
How is that way off? I didn't include the code for importing psycopg, or for creating the connection or the cursor, but it wasn't supposed to be a complete example, just the important part.
colinmarc
Sorry colinmarc. Thanks. I appreciate the intention but unfortunately it doesn't match with the goal in the question. Hey, maybe Python just isn't there yet.
Luke Stanley
+3  A: 

I think you should try ZODB. It is object oriented database designed for storing python objects. Its API is quite close to example you have provided in your question, just take a look at tutorial.

andreypopp
Thanks Andrey it looks *closer*.
Luke Stanley
Wow I can't beleive the question downvoting here. I talked about it in #python on freenode and it was downvoted! Is this a reasonable question?
Luke Stanley
Does it have to use getter or setter functions like employee.setName(name) rather than something like employee('Mandy') or employee.(name='Mandy') ?
Luke Stanley
No, if subclass your model from Persistent, it will track attribute changes itself, if you need to use mutable values as model attributes — use PersistenMapping instead of dict and PersistentList instead of list. As of my expirience, ZODB works great for blogs, homepages, medium-sized portals, desktop applications that are not staticstical data processing applications and etc.
andreypopp
Thanks Andrey (I missed their Persistent example on my first look). Do you know if it's possible to use the Persistent class and ZODB without installing Zope?
Luke Stanley
What do you mean by Zope? It is only a collection of libraries and nothing more. When you issue `easy_install ZODB` no more than ZODB and some helper libraries will be installed — no web frameworks, no network servers, just ZODB and some libs.
andreypopp
I see, it looks like Persistent maybe a class by someone else and I can't find it beacuse just easy installing ZODB isn't sufficient.I also just discovered http://www.mongodb.org !
Luke Stanley
+6  A: 

What you request cannot be done in Python 2.whatever, for a very specific reason. You want to write:

class Task(model): 
    title = ''
    isDone = False

In Python 2.anything, whatever model may possibly be, this cannot ever allow you to predict any "ordering" for the two fields, because the semantics of a class statement are:

  1. execute the body, thus preparing a dict
  2. locate the metaclass and run special methods thereof

Whatever the metaclass may be, step 1 has destroyed any predictability of the fields' order.

Therefore, your desired use of positional parameters, in the snippet:

Task('Illustrate different syntax modes', True)

cannot associate the arguments' values with the model's various fields. (Trying to guess by type association -- hoping no two fields ever have the same type -- would be even more horribly unpythonic than your expressed desire to use db.tasklist and db['tasklist'] indifferently and interchangeably).

One of the backwards-incompatible changes in Python 3 was introduced specifically to deal with situations of this ilk. In Python 3, a custom metaclass can define a __prepare__ function which runs before "step 1" in the above simplified list, and this lets it have more control about the class's body. Specifically, quoting PEP 3115...:

__prepare__ returns a dictionary-like object which is used to store the class member definitions during evaluation of the class body. In other words, the class body is evaluated as a function block (just like it is now), except that the local variables dictionary is replaced by the dictionary returned from __prepare__. This dictionary object can be a regular dictionary or a custom mapping type.

...

An example would be a metaclass that uses information about the ordering of member declarations to create a C struct. The metaclass would provide a custom dictionary that simply keeps a record of the order of insertions.

You don't want to "create a C struct" as in this example, but the order of fields is crucial (to allow the use of positional parameters that you want) and so the custom metaclass (obtained through base model) would have a __prepare__ classmethod returning an ordered dictionary. This removes the specific issue, but, of course, only if you're willing to switch all of your code using this "magic ORM" to Python 3. Would you be?

Once that's settled, the issue is, what database operations do you want to perform, and how. Your example, of course, does not clarify this at all. Is the taskList attribute name special, or should any other attribute assigned to the db object be "autosaved" (by name and, what other characteristic[s]?) and "autoretrieved" upon use? Are there to be ways to remove entities, alter them, locate them (otherwise than by having once been listed in the same attribute of the db object)? How does your sample code know what DB service to use and how to authenticate to it (e.g. by userid and password) if it requires authentication?

The specific tasks you list would not be hard to implement (e.g. on top of Google App Engine's storage service, which does not require authentication nor specification of "what DB service to use"). model's metaclass would introspect the class's fields and generate a GAE Model for the class, the db object would use __setattr__ to set an atexit trigger for storing the final value of an attribute (as an entity in a different kind of Model of course), and __getattr__ to fetch that attribute's info back from storage. Of course without some extra database functionality this all would be pretty useless;-).

Edit: so I did a little prototype (Python 2.6, and based on sqlite) and put it up on http://www.aleax.it/lustdb.zip -- it's a 3K zipfile including 225-lines lustdb.py (too long to post here) and two small test files roughly equivalent to the OP's originals: test0.py is...:

from lustdb import *  

class Task(Model): 
    title = ''
    done = False

db.taskList = []    
db.taskList.append(Task(title='Beat old sql interfaces', done=False))
db.taskList.append(Task(title='Illustrate different syntax modes', done=True))

and test1.p1 is...:

from lustdb import *

print 'Done tasks:'
for task in db.taskList:
    if task.done:
        print task

Running test0.py (on a machine with a writable /tmp directory -- i.e., any Unix-y OS, or, on Windows, one on which a mkdir \tmp has been run at any previous time;-) has no output; after that, running test1.py outputs:

Done tasks:
Task(done=True, title=u'Illustrate different syntax modes')

Note that these are vastly less "crazily magical" than the OP's examples, in many ways, such as...:

1. no (expletive delete) redundancy whereby `db.taskList` is a synonym of `db['taskList']`, only the sensible former syntax (attribute-access) is supported
2. no mysterious (and totally crazy) way whereby a `done` attribute magically becomes `isDone` instead midway through the code
3. no mysterious (and utterly batty) way whereby a `print task` arbitrarily (or magically?) picks and prints just one of the attributes of the task
4. no weird gyrations and incantations to allow positional-attributes in lieu of named ones (this one the OP agreed to)

The prototype of course (as prototypes will;-) leaves a lot to be desired in many respects (clarity, documentation, unit tests, optimization, error checking and diagnosis, portability among different back-ends, and especially DB features beyond those implied in the question). The missing DB features are legion (for example, the OP's original examples give no way to identify a "primary key" for a model, or any other kinds of uniqueness constraints, so duplicates can abound; and it only gets worse from there;-). Nevertheless, for 225 lines (190 net of empty lines, comments and docstrings;-), it's not too bad in my biased opinion.

The proper way to continue playing with this project would of course be to initiate a new lustdb open source project on the hosting part of code.google.com (or any other good open source hosting site with issue tracker, wiki, code reviews support, online browsing, DVCS support, etc, etc) - I'd do it myself but I'm close to the limit in terms of number of open source projects I can initiate on code.google.com and don't want to "burn" the last one or two in this way;-).

BTW, the lustdb name for the module is a play of word with the OP's initials (first two letters each of first and last names), in the tradition of awk and friends -- I think it sounds nicely (and most other obvious names such as simpledb and dumbdb are taken;-).

Alex Martelli
Wow you wrote a lot ;D Thanks for your rather detailed consideration.The lib/s that do it should probably work with the dominant versions of Python.Hey, I wouldn't be too hung up on requiring ordered argument passing instead of JUST keyword based but I think actually it might be doable using AST or similar. Did you consider that? What do you think?
Luke Stanley
@Luke, you can surely write your own Python compiler based on re-parsing the class's source (requiring the source to be around, of course, not just bytecode -- that would require yet another different approach). I think it would be a ridiculously large amount of effort for a ridiculously small alleged "benefit" (where the supposed "benefit" is... a "darker-than-black magic" way to break the "there should be only one way" Zen of Python principle!), but, as long as you're wasting your own time rather than anybody else's, hey, it **is** yours to squander as you please, of course.
Alex Martelli
I really am grateful for the thoughts, I just don't want to feel it comes out of some kind of need to be defensive. It's just code! We shouldn't take it personally. It's important to have a sense of balance.I'm not trying to step on anyone's toes here - I just want people to code more with less. I'm sorry old SQL modules are on the way out and that many had to learn them first. Evolution happens.A nice AST tool I've been playing with for Py to JS compilation could do.But, I guess just using keywords: the fall-back plan is just easier and *clearer* so it's not worth the effort.
Luke Stanley
If I didn't care _personally_ about "code" (and other aspects of program development), why ever would I want to spend my free time trying to help people on this site?! Anyway, making attribute access and indexing equivalent (as you request above) is definitely _not_ any help to "code more with less" -- it's just a terrible idea, inducing duplication and inconsistency without any benefit whatsoever, and I really just can't figure out how anybody claiming to love Python could possibly want anything that contrary to the language and its "Zen", for example.
Alex Martelli
Anyway, net of the indexing/attribute equivalence horror, the rest of your desiderata (as minisculely clarified so far) is not bad (until you _do_ clarify as I above requested of course, in which case the intrinsic weakness of the design may likely start showing;-) -- getting late for me to show code to exemplify the design I already gave above, but I may give it a shot tomorrow.
Alex Martelli
ha I knew deep down, you liked it really. Hey I was just checking because some people get quite defensive when the status-quo is challenged by some random. Saying something is bloated is unfortunately going to annoy a percentage of people and positively engage others.Of course you care about code! I meant that some people get their ego attached to a particular kind of code, just because that's how they are used to it or such.BTW I wasn't sure what you meant by "making attribute access and indexing equivalent" but am fleshing out the answers to your questions.
Luke Stanley
Any attribute assigned to the db object would be "autosaved" (by name, and using the type specified, with classes being taken care of to have their own definitions / colloumns and references in the database as needed).Autoretrieved upon use, but optionally loaded fully into memory (for a task app for only 100 users, this makes sense for example).Deletion would probably be an expected feature. del should work, and probably = None assignment.For an early version altering could totally update the item, and a later version could update only the parts needed.
Luke Stanley
It would be nice to have searching options too, though they would have to be rather Pythonic and short methods if no built methods are really a good semantic match.For quick prototyping, no user/pass would be needed but if there is one, optionally it would be specified.Again, as for what format/adapter and to use, there would be a default option to a local SQLite, JSON, XML or other format with a defualt filename for autosaves and autoretreival.
Luke Stanley
I guess it should optionally save ONLY on request or on assignment. Maybe later multiple assignments right next to each other could be combined if SQL or similar is being used but that's not so easy.Re: lower level formats/adapters: GaE, JSON, SQL and XML are probably good choices but I suppose demonstrating that it CAN be done with SQL would be a nice challenge. Google App Engine is a good target too though. Depending on how much data is being used and by how many users would probably effect what would make the most sense, for obvious reasons.
Luke Stanley
@Luke, have a look at my answer's edit for the prototype I came up with.
Alex Martelli
ha cool Alex, btw I've removed the psudocode bugs #2 (isDone) and #3 (print task) - they just show how simple code can still have bugs sowe should KISS syntax wise!.re: #1 it's very useful to make attribute access easy, and it doesn't require "delete duplication". ha I think lustdb is a good name and I'd be happy to setup an account for it. I'll check out the actual code now. progress! :)
Luke Stanley
re #1 the use case is easily taking a variable from somewhere else in the code
Luke Stanley
I changed test1.py to: use 'if task.done' and removed the attribute error check. I'm thinking it might make sense to build off a higher level library than use sqlite directly because it could be less code and may come with more databases built in. I'll take a look and setup a Google Groups list?
Luke Stanley
http://groups.google.com/group/lustdb
Luke Stanley
@Luke, explain to me how `getattr(db, thevariable)` would be any worse for your alleged "use case" than the disgusting and 100% unPythonic duplication and TMOWTDItis of having `db[thevariable]` become a synonym for it. As for the group, I'm not going to look at it (what for, to keep debating approaches that are entirely alien to Python like the attribute/indexing duplication you keep defending and apparently craving for?!) -- if you open a code.google.com project and invite me as a developer, I might accept, and maybe review your attempts to rewrite my code on SQLAlchemy, (cont...)
Alex Martelli
(...cont) but if the project keeps throwing off totally batty and indefensible (in Python) ideas like `db['x']` and `db.x` being synonyms, I'll simply drop it and go back to sanity -- anybody who knows so little Python to fail to understand that `getattr` is the "only obvious way to do it" has no business using an experimental Python framework like lustdb, much less designing or coding one, IMHO.
Alex Martelli
ok well, getattr isn't bad and NORMALLY duplication of ways to do things like with Ruby is bad,but db['x'] is less code than getattr(db,'x') all the time, and if we can make people forget sometimes they aren't really using a simple dictionary, it could be quite a lustfull programming experience; more power without getting in the way.We can often do more to reduce cognitive burdon, even if it's 9 charecters different, over time it adds up. Every time a function is used it requires extra thought in STM which can empty the brain of another function or varible (more bugs).
Luke Stanley
@Luke, OK, our disagreement is just too deep -- good luck with the future of lustdb, and count me out. Anybody who thinks `getattr` or "9 charecters" (sic) are a "cognitive burdon" (sic) and doesn't see how any good Python user would be driven crazy trying to understand why the (expletive deleted) there are two apparently identical ways to do the same thing in this one weird place out of all Python, why this "apparent dictionary" does _not_ in fact believe like a dict in many crucial ways, etc, is just too many parsecs away from Python to do any design I might be at all (cont...)
Alex Martelli
Alex Martelli
Luke Stanley
+1  A: 

The more I think on't the more the Smalltalk model of operation seems more relevant. Indeed the OP may not have reached far enough by using the term "database" to describe a thing which should have no need for naming.

A running Python interpreter has a pile of objects that live in memory. Their inter-relationships can be arbitrarily complex, but namespaces and the "tags" that objects are bound to are very flexible. And as pickle can explicitly serialize arbitrary structures for persistence, it doesn't seem that much of a reach to consider each Python interpreter living in that object space. Why should that object space evaporate with the interpreter's close? Semantically, this could be viewed as an extension of the anydbm tied dictionaries. And since most every thing in Python is dictionary-like, the mechanism is almost already there.

I think this may be the generic model that Alex Martelli was proposing above, it might be nice to say something like:

class Book:
    def __init__(self, attributes):
        self.attributes = attributes
    def __getattr__(....): pass

$ python
>>> import book
>>> my_stuff.library = {'garp': 
    Book({'author': 'John Irving', 'title': 'The World According to Garp', 
      'isbn': '0-525-23770-4', 'location': 'kitchen table', 
      'bookmark': 'page 127'}),
    ...
    }
>>> exit

[sometime next week]

$ python
>>> import my_stuff
>>> print my_stuff.library['garp'].location
'kitchen table'
# or even
>>> for book in my_stuff.library where book.location.contains('kitchen'):
   print book.title

I don't know that you'd call the resultant language Python, but it seems like it is not that hard to implement and makes backing store equivalent to active store.

There is a natural tension between the inherent structure imposed - and sometimes desired - by RDBMs and the rather free-form navel-gazing put here, but NoSQLy databases are already approaching the content addressable memory model and probably better approximates how our minds keep track of things. Contrariwise, you wouldn't want to keep all the corporate purchase orders such a storage system, but perhaps you might.

msw
Yeah, I do think it's closer to how our minds should think about it, which is the main motivation. Our working memory limit is 7 plus or minus 2 concepts, so we should keep the complexity down. It's more like seeing code as potentially a work of art rather than utilitarian necessity. The fact is, most programmers only have a few really good hours of coding in them a day, at best. So people appreciate anything that makes it easier, it isn't just navel-gazing :)
Luke Stanley
Yeah, it's possible to get kwargs and do the whole works. I know because I've half attempted similar before, and succeeded. (With a persistent JSON store and Pythonic, OO XHTML generator/access/template/model that uses similar syntax.)There are Pythonic ways to do it, I'd consider my example more Pythonic than Django.
Luke Stanley
I don't view it as a complexity issue, I view it as removing implementation artifacts and the artificial dichotomy between working and backing store. Python is loaded with databases except they're called dictionaries and lists and namespaces and when you use them you just use them. They persist for the duration of an interpreter, why such a short time? They migrate from cache to core to swap willy-nilly but need to be explicitly shifted to files. SQL is a historic means to the end of backing store, it's just an implementation detail.
msw
Right, totally! I meant that - I think my example is implementable directly, except probably requiring keyword arguments. :)
Luke Stanley
A: 

How about you give an example of how "simple" you want your "dealing with database" to be, and I then tell you all the stuff that is needed for that "simplicity" to get working ?

(And of which it will still be YOU that will be required to give the information/config to the database interface engine, somewhere, somehow.)

To name but one example :

If your database management engine is some external machine with which you/your app interfaces over IP or some such, there is no way around the fact that the IP identity of where that database engine is running, will have to be provided by your app's database interface client, somewhere, somehow. Regardless of whether that gets explicitly exposed in the code or not.

Erwin Smout
Erwin it would depend entirely on the usage. Imagine Elixir or Google App Engine Models being accessed like in the example in the question with Python types being turned into whatever is typically needed.Perhaps you can see some of the comments with here: http://stackoverflow.com/questions/2978138/django-models-sqlalchemy-are-bloated-any-truly-pythonic-db-models-out-there/2979999#2979999Batteries included autosaving, autoloading, and a sensible default file being used straight after import or with a very clear and easily memorable function to specify a server format, protocol, login etc.
Luke Stanley
I added some more clarification to the example code, hope that helps. By the way, I have a fairly good idea of what is required.A model class for mapping Python Object types to GAE types, or- SQLAlchemy / Elixir would be a good start. Some heuristic modes would probably be required here. Potentially this means, things like an overly large or overly small character range for strings for example unless the model was autochanged with a dialog with the developer as data required model changes. Followed by autoloading and autosaving with SQLite/JSON/XML chosen as a default local file format.
Luke Stanley
+1  A: 

I've been busy, here it is, released under LGPL: http://github.com/lukestanley/lustdb

It uses JSON as it's backend at the moment.

This is not the same codebase Alex Martelli did. I wanted to make the code more readable and reusable with different backends and such.

Elsewhere I have been working on object oriented HTML elements accessable in Python in similar ways, AND a library for making web.py more minimalist.

I'm thinking of ways of using all 3 elements together with automatic MVC prototype construction or smart mapping.

While old fashioned text based template web programming will be around for a while still because of legacy systems and because it doesn't require any particular library or implementation, I feel soon we'll have a lot more efficent ways of building robust, prototype friendly web apps.

Please see the mailing list for those interested.

Luke Stanley