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

2024/10/7 12:20:09

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

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

Update: A 'lustdb' prototype has been written by Alex Martelli - if you know any somewhat lightweight, high-level database libraries with multiple backends we could wrap in syntax sugar honey, please weigh in!

from someAmazingDB import *  
#we imported a smart model class and db object which talk to database adapter/s
class Task (model): title = ''done = False #native types not a custom object we have to think about!db.taskList = []
#or
db.taskList = expandableTypeCollection(Task) #not sure what this syntax would bedb['taskList'].append(Task(title='Beat old sql interfaces',done=False))
db.taskList.append(Task('Illustrate different syntax modes',True)) # ok maybe we should just use kwargs#at this point it should be autosaved to a default db option#by default we should be able to reload the console and access the default db:>> from someAmazingDB import *
>> print 'Done tasks:'
>> for task in db.taskList:
>>     if task.done:
>>         print task.title
'Illustrate different syntax modes'

I'm a fan of Python, webPy and Cherry Py, and KISS in general.

We're talking automatic Python to SQL type translation or NoSQL. We don't have to totally be SQL compatible! Just a scalable subset or ignore it!

Re:model changes, it's ok to ask the developer when they try to change it or have a set of sensible defaults.

Here is the challenge: The above code should work with very little modification or thinking required. Why must we put up with compromise when we know better?

It's 2010, we should be able to code scalable, simple databases in our sleep.

If you think this is important, please upvote!

Answer

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 storethe 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 dictionaryis replaced by the dictionary returned from __prepare__. Thisdictionary object can be a regular dictionary or a custom mappingtype.

...

An example would be a metaclass thatuses information about theordering of member declarations to create a C struct. The metaclasswould provide a custom dictionary that simply keeps a record of theorder 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 = Falsedb.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;-).

https://en.xdnf.cn/q/70241.html

Related Q&A

Fabric Sudo No Password Solution

This question is about best practices. Im running a deployment script with Fabric. My deployment user deploy needs sudo to restart services. So I am using the sudo function from fabric to run these com…

cartopy: higher resolution for great circle distance line

I am trying to plot a great circle distance between two points. I have found an in the cartopy docs (introductory_examples/01.great_circle.html):import matplotlib.pyplot as plt import cartopy.crs as cc…

Python Flask date update real-time

I am building a web app with Python Flask with JavaScript. I am a beginner of Javascript.The process I do now:In Flask Python code, 1. I get data by scrapping the web (numeric data that updates every m…

How do I force pip to install from the last commit of a branch in a repo?

I want pip to install from the latest commit on a master branch of my github repository. I tried many options mentioned here on StackOverflow, none helped. For instance, that does not work:pip install …

Emacs: pass arguments to inferior Python shell during buffer evaluation

recently I started using Emacs as a Python IDE, and it not quite intuitive... The problem I am struggling with right now is how to pass command line arguments to the inferior python shell when the buff…

How to edit a wheel package (.whl)?

I have a python wheel package, when extracted I find some python code, Id like to edit this code and re-generate the same .whl package again and test it to see the edits .. How do I do that?

Choosing order of bars in Bokeh bar chart

As part of trying to learn to use Bokeh I am trying to make a simple bar chart. I am passing the labels in a certain order (days of the week) and Bokeh seems to be sorting them alphabetically. How ca…

buildout - using different python version

i have set up buildout project (django to be specific) that has to run in old machine, it works fine in my local system with python 2.7. In production server it runs python 2.5 and i want to configure…

Receive an error from lingnutls/Hogweed when importing CV2

Ive never seen an error like this and dont know where to start. I installed opencv with conda install opencvand am running Ubuntu Linux 18.04 using a conda environment named fpn. How should I even appr…

Understanding django admin readonly_fields

I created some code to differentiate between two usergroups in Django admin, resulting in showing all fields readonly or only some of them, which are set directly in the ModelAdmin class.At first here …