Django Models / SQLAlchemy are bloated! Any truly Pythonic DB models out there?

后端 未结 8 1844
广开言路
广开言路 2021-02-09 15:16

\"Make things as simple as possible, but no simpler.\"

Can we find the solution/s that fix the Python database world?

Update: A

8条回答
  •  无人及你
    2021-02-09 15:51

    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;-).

提交回复
热议问题