Code Monkey home page Code Monkey logo

remodel's Introduction

remodel

Build Status

Very simple yet powerful and extensible Object Document Mapper for RethinkDB, written in Python.

It is plain simple!

from remodel.models import Model

class User(Model):
    pass

That's really everything you need to do to set up a model!

Don't forget to turn on your RethinkDB server and to create your tables (check the examples below for a helper that does just that!).

Features

  • schemaless;
  • dict interface;
  • full support for relations;
  • indexes;
  • convention over configuration;
  • lazy-loading;
  • caching;
  • thoroughly tested;

Installation

pip install remodel

Examples

Basic CRUD operations

class Order(Model):
    pass

# Create
my_order = Order.create(customer='Andrei', shop='GitHub')
# Update
my_order['total'] = 100
my_order.save()
# Read
saved_order = Order.get(customer='Andrei')
# Delete
saved_order.delete()

Creating tables

from remodel.models import Model
from remodel.helpers import create_tables, create_indexes

class Party(Model):
    has_many = ('Guest',)

class Guest(Model):
    belongs_to = ('Party',)

# Creates all database tables defined by models
create_tables()
# Creates all table indexes based on model relations
create_indexes()

Configuring database connection

Setups are widely different, so here's how you need to configure remodel in order to connect to your RethinkDB database:

from remodel.connection import pool

pool.configure(host='localhost', port=28015, auth_key=None, user='admin', password='', db='test')

Relations

Has one / Belongs to

class User(Model):
    has_one = ('Profile',)

class Profile(Model):
    belongs_to = ('User',)

andrei = User.create(name='Andrei')
profile = Profile.create(user=andrei, network='GitHub', username='linkyndy')
print profile['user']['name'] # prints Andrei

Has many / Belongs to

class Country(Model):
    has_many = ('City',)

class City(Model):
    belongs_to = ('Country',)

romania = Country.create(name='Romania')
romania['cities'].add(City(name='Timisoara'), City(name='Bucharest'))
print romania['cities'].count() # prints 2

Has and belongs to many

class Post(Model):
    has_and_belongs_to_many = ('Tag',)

class Tag(Model):
    has_and_belongs_to_many = ('Post',)

my_post = Post.create(name='My first post')
personal_tag = Tag.create(name='personal')
public_tag = Tag.create(name='public')
my_post['tags'].add(personal_tag, public_tag)
print my_post['tags'].count() # prints 2

Has many through

class Recipe(Model):
    has_many = ('SpecificSpice',)

class Chef(Model):
    has_many = ('SpecificSpice',)

class SpecificSpice(Model):
    belongs_to = ('Recipe', 'Chef')

quattro_formaggi = Recipe.create(name='Pizza Quattro Formaggi')
andrei = Chef.create(name='Andrei')
andreis_special_quattro_formaggi = SpecificSpice.create(chef=andrei, recipe=quattro_formaggi, oregano=True, love=True)
print andreis_special_quatro_formaggi['love'] # prints True

Callbacks

from remodel.models import Model

class Shirt(Model):
    def after_init(self):
        self.wash()

    def wash(self):
        print 'Gotta wash a shirt after creating it...'

or

from remodel.models import Model, after_save

class Prize(Model):
    @after_save
    def brag(self):
        print 'I just won a prize!'

Custom table name

class Child(Model):
    table_name = 'kids'

print Child.table_name # prints 'kids'

Custom model queries

import rethinkdb as r

class Celebrity(Model):
    pass

Celebrity.create(name='george clooney')
Celebrity.create(name='kate winslet')
upper = Celebrity.map({'name': r.row['name'].upcase()}).run()
print list(upper) # prints [{u'name': u'GEORGE CLOONEY'}, {u'name': u'KATE WINSLET'}]

Custom instance methods

class Child(Model):
    def is_minor(self):
        if 'age' in self:
            return self['age'] < 18

jack = Child.create(name='Jack', age=15)
jack.is_minor() # prints True

Custom class methods

from remodel.object_handler import ObjectHandler, ObjectSet

class TripObjectHandler(ObjectHandler):
    def in_europe(self):
        return ObjectSet(self, self.query.filter({'continent': 'Europe'}))

class Trip(Model):
    object_handler = TripObjectHandler

Trip.create(continent='Europe', city='Paris')
Trip.create(continent='Asia', city='Shanghai')
Trip.create(continent='Europe', city='Dublin')
print len(Trip.in_europe()) # prints 2

Viewing object fields

class Train(Model):
    pass

train = Train.create(nr=12345, destination='Paris', has_restaurant=True, classes=[1, 2])
print train.fields.as_dict()
# prints {u'classes': [1, 2], u'nr': 12345, u'destination': u'Paris', u'has_restaurant': True, u'id': u'd9b8d57f-5d67-4ff7-acf8-cbf7fdd65581'}

Concepts

Relations

Remodel supports various types of relationships:

  • has one
  • belongs to
  • has many
  • has and belongs to many
  • has many through

Defining relations

Related models are passed as tuples in a model's definition. All other aspects, such as foreign keys, indexes, lazy relation loading and relation cache are magically handled for you.

If you need precise definition for your related models, you can pass a configuration tuple instead of the string name of your related model:

    class Artist(Model):
        has_many = (('Song', 'songs', 'id', 'song_id'), 'Concert')
        # Tuple definition: (<related model name>, <related objects accessor field>, <model key>, <related model key>)

One important thing to notice is that reverse relationships are not automatically ensured if only one end of the relationship is defined. This means that if Artist has_many Song, Song belongs_to Artist is not automatically enforced unless explicitly defined.

Using relations

Assigning has_one and belongs_to objects doesn't mean that they are persisted. You need to manually call save() on them; assuming Profile belongs_to User:

profile['user'] = User(...)
profile.save()

On the other side, assigning has_many and has_and_belongs_to_many objects automatically persist them, so there is no need for you to call save() on them; assuming Shop has_many Product:

shop.add(product1, produt2)
# No need to call save() on products!

Note that certain assignments of related objects can not be performed unless one (or both) of the objects is saved. You can not save a GiftSize with a Gift attached without saving the Gift object first (when having a GiftSize belongs_to Gift).

Documentation

Can be found at https://github.com/linkyndy/remodel/wiki.

Motivation

The main reason for Remodel's existence was the need of a light-weight ODM for RethinkDB, one that doesn't force you to ensure a document schema, one that provides a familiar interface and one that gracefully handles relations between models.

Status

Remodel is under active development and it is not yet production-ready.

How to contribute?

Any contribution is highly appreciated! See CONTRIBUTING.md for more details.

License

See LICENSE

remodel's People

Contributors

arwema avatar bmjjr avatar caj-larsson avatar derkan avatar gesias avatar jannkleen avatar linkyndy avatar thedrow avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

remodel's Issues

Default fields and values

Hi!

Great module! I'm not sure if this is a support request or a feature request, but how
can I add default fields and values to my models? For example:

class User(Model):
is_active = True
created_date = datetime.datetime.utcnow()

Question about custom class methods

I was reading through the readme file, and was curious about why we are creating custom class methods with the usage below

from remodel.object_handler import ObjectHandler, ObjectSet

class TripObjectHandler(ObjectHandler):
    def in_europe(self):
        return ObjectSet(self, self.query.filter({'continent': 'Europe'}))

class Trip(Model):
    object_handler = TripObjectHandler

Trip.create(continent='Europe', city='Paris')
Trip.create(continent='Asia', city='Shanghai')
Trip.create(continent='Europe', city='Dublin')
print len(Trip.in_europe()) # prints 2

It seems to me that it seems counter-intuitive compared to making it liks so:

class Trip(Model):
    @classmethod
    def in_europe(self):
        return self.query.filter({'continent': 'Europe'})

Trip.create(continent='Europe', city='Paris')
Trip.create(continent='Asia', city='Shanghai')
Trip.create(continent='Europe', city='Dublin')
print len(Trip.in_europe()) # prints 2

I am not saying you are doing it wrong, I am just curious why it wasn't done that way as it seems much more intuitive. I am sure theres something I don't know about yet as I am just starting to use remodel.

not comfortable with rethinkdb-python 2.4.1

In [1]: import remodel                                                                                                                                                                                            
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-1-2f932a5f00d5> in <module>
----> 1 import remodel

~/.pyenv/versions/3.6.8/lib/python3.6/site-packages/remodel/__init__.py in <module>
----> 1 import remodel.monkey

~/.pyenv/versions/3.6.8/lib/python3.6/site-packages/remodel/monkey.py in <module>
      4 
      5 
----> 6 run = r.ast.RqlQuery.run
      7 
      8 def remodel_run(self, c=None, **global_optargs):

AttributeError: module 'rethinkdb' has no attribute 'ast'

Model.create can't take id

I am trying to create an object with id. e.g. User.create(id=123, name='kenji'). However it raises exception. User.create(name='kenji') works, but then the id is auto generated uuid that is not desirable for my app. Why create can't take id?

ADVICE: Trying to understand relaitonships here

Can i get an example what i can do with this:

class Artist(Model):
        has_many = (('Song', 'songs', 'id', 'song_id'), 'Concert')
        # Tuple definition: (<related model name>, <related objects accessor field>, <model key>, <related model key>)

Does this mean if i add multiple songs

a = Artist()
a.add(song_1, song_2)

I can access all the songs by:
a["songs"]
???

And also,

if <related objects accessor field> is 'id' (id of artist?) shouldn't the <related model key> be something like 'artist_id'?

Is it possible to have required model fields

I was wondering if there was a possibility to have a simple way to have a required set of fields on a model. One method I was thinking of was:

class Post(Model):
    required_fields = {
        'title',
        'author',
        'content'
    }

    def __init__(self, **kwargs):
        for field in self.required_fields:
            if field not in kwargs:
                raise KeyError('Field is required: {}'.format(field))

        super().__init__(**kwargs)

But will this break things or should this work? Or is there a better way to do this.

update for latest python-rethinkdb

Hi, remodel appears to be broken after a recent python-rethinkdb version change. Using Python3, remodel, and python-rethinkdb2.4.2, I get the following exception when creating a very simple Model:

>>> from remodel.models import Model, before_save
>>> 
>>> class A(Model):
...     pass
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/branden/venvs/remodel/lib/python3.5/site-packages/remodel/models.py", line 49, in __new__
    setattr(new_class, 'objects', object_handler_cls(new_class))
  File "/home/branden/venvs/remodel/lib/python3.5/site-packages/remodel/object_handler.py", line 7, in __init__
    self.query = query or r.table(model_cls._table)
AttributeError: module 'rethinkdb' has no attribute 'table'

This issue on the rethinkdb repo might contain the solution: rethinkdb/rethinkdb#6722

Plurals automatically in table names?

Is remodel converting my "business" table into "businesses" with this piece of code?

import remodel.utils
import remodel.connection
from remodel.models import Model
from remodel.helpers import create_tables, create_indexes
remodel.connection.pool.configure(db="book")

class Business(Model):
has_one = ('BusinessTypes',)

class BusinessTypes(Model):
belongs_to = ('Business',)

create_tables()
create_indexes()

How can i avoid it?

Connection pool will not honor maximum connections if all connections in use

It seems to me that the connection pool will not honor the maximum connections limit, because every time a connection is put back into the queue, the count is decremented. So take the following scenario:

  • max_connections is 5
  • Queue is empty five calls in a row: _created_connections.current() is now 5
  • All five connections are put back into the pool: _created_connections.current() is now 0
  • All five connections are taken out of the queue: _created_connections.current() is now 0
  • Five new connections can be pulled from the pool.
    def get(self):
        try:
            return self.q.get_nowait()
        except Empty:
            if self._created_connections.current() < self.max_connections:
                conn = self.connection_class(**self.connection_kwargs).conn
                self._created_connections.incr()
                return conn
            raise

    def put(self, connection):
        self.q.put(connection)
        self._created_connections.decr()

Async support for tornado/asynio.

Rethinkdb work best with async frameworks as it have very cool async features. Currenly remodel is not using async driver.
Any plan to support async ?

Multiple relations to same table

I'm trying to related one table (Asset) to another (Files) multiple times, the reason for this is that one Asset can have many files, but also have one primary file related to it. I'm unsure if it's possible with this is possible, but so far I've tried various things mentioned in the documents but nothing seems to pick up my settings.

Heres my model layout:

class Asset(Model):
    has_one = (('File', 'files', 'id', 'default_icon_file_id'),)
    has_many = ('File',)

class File(Model):
    has_many = ('File',)
    belongs_to = ('File', 'Asset',)

I then try and set it using:

asset['default_icon_file_id'] = models.File.get('ID HERE')

or

asset['default_icon_file'] = models.File.get('ID HERE')

but nothing gets picked up. Any pointers would be appreciated. I'll try get around to creating a PR for the readme

Create a helpers module inside remodel

remodel.utils should contain just the utilities needed for remodel's core, not helpers used by users to leverage some operations. Therefore, create_tables and create_indexes should be moved from there to a helpers module (remodel.helpers).

Additionally, it would be nice to have an additional helper, drop_tables.

This issue should be considered as a follow-up to #13.

Expose RelatedSet and RelatedM2MSet query

As a complex query can be constructed on a Model (with Model.table), it should be possible to do this on RelatedSet and RelatedM2MSet also.

Some examples:

user['preferences'].q.order_by('name').run()
recipe['ingredients'].q.with_fields('name', 'color').limit(10).run()

Circular imports on create_indexes() and create_tables()

The following code:

from remodel.helpers import create_indexes, create_tables

# Creates all database tables defined by models
create_tables()
# Creates all table indexes based on model relations
create_indexes()

Placed on __init__.py creates a circular import.

tests/unit/test_file_event.py:1: in <module>
    from configuration_managers.events.file import FileEvent
src/configuration_managers/events/__init__.py:6: in <module>
    create_indexes()
.tox/py35-unit/lib/python3.5/site-packages/remodel/helpers.py:29: in create_indexes
    from .registry import model_registry, index_registry
.tox/py35-unit/lib/python3.5/site-packages/remodel/registry.py:4: in <module>
    import remodel.models
.tox/py35-unit/lib/python3.5/site-packages/remodel/models.py:7: in <module>
    from .field_handler import FieldHandlerBase, FieldHandler
.tox/py35-unit/lib/python3.5/site-packages/remodel/field_handler.py:5: in <module>
    from .registry import index_registry
E   ImportError: cannot import name 'index_registry'

Removing the call to any of the functions and the import doesn't resolve this issue. Only removing both.

Documents with lists may not be able to save on update

If a document has fields with lists, save may complain about being non-deterministic. I've made some changes to the field manager that would keep track of dirty fields and then only update those, which should have the same effect but be atomic. I'll make a pull request as soon as it's well tested.

KeyError: 'changes' when saving the same object consecutively

Recently, a bug occurs when trying to perform a consecutive save on an object that has the same fields:

>>> user = User(...)
>>> user.save()
>>> user.save()
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1836, in __call__
    return self.wsgi_app(environ, start_response)
  File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1820, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "/usr/local/lib/python2.7/site-packages/flask_restful/__init__.py", line 263, in error_router
    return original_handler(e)
  File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1403, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python2.7/site-packages/flask_restful/__init__.py", line 260, in error_router
    return self.handle_error(e)
  File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1817, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1477, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python2.7/site-packages/flask_restful/__init__.py", line 263, in error_router
    return original_handler(e)
  File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1381, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python2.7/site-packages/flask_restful/__init__.py", line 260, in error_router
    return self.handle_error(e)
  File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1475, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1461, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/usr/local/lib/python2.7/site-packages/flask_restful/__init__.py", line 431, in wrapper
    resp = resource(*args, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/flask/views.py", line 84, in view
    return self.dispatch_request(*args, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/flask_restful/__init__.py", line 521, in dispatch_request
    resp = meth(*args, **kwargs)
  File "/usr/src/app/instasong/resources/user.py", line 21, in put
    user.save()
  File "/usr/local/lib/python2.7/site-packages/remodel/models.py", line 89, in save
    self.fields.__dict__ = result['changes'][0]['new_val']
KeyError: 'changes'

This issue seems to happen since RethinkDB 2.0, when their API changed a bit. Instead of returning a changes array each time for an operation that receives return_values=True as an argument, now the changes array is returned only when actual changes have been made. Hence, this affects remodel when trying to perform save() consecutively on an object, without changing any of its fields.

There's an ongoing discussion at rethinkdb/rethinkdb#4068; I'll wait for a conclusion there before making changes to remodel. This shouldn't affect vital parts of your workflow with remodel since it's not common to just call save() multiple times on the same object.

Also, as it's current state, remodel doesn't thoroughly support RethinkDB 2.0. There might be other issues as well. If that would be the case, I'll make a new release addressing those issues and fully supporting RethinkDB 2.0.

As always, if you have a special case or need regarding this issue, please comment and we'll figure it out.

P.S.: We should make sure to include a test with the above scenario in order for remodel to be more future proof.

Custom table name

Is it possible to pass arbitrary table name instead of the defaults pluralized class name? Useful on existing tables auto-generated by horizon

Write better docs

Remodel should be thoroughly documented. Both in-code and on the web.

Docs should be written for main API methods and a nice interface should host them on the web (Flask docs are very nice).

Investigate how secondary indexes are created

In some cases, secondary indexes are created twice for a model. I suspect that indexes for belongs_to relations are created on the wrong table (other instead of model).

After fixing this, tests should be written on FieldHandler with regard to this issue.

Add durability to delete

When running

model.delete()

you should be able to provide durability, which is present in the python rethinkdb driver:

table.delete([durability="hard", return_changes=False]) → object
selection.delete([durability="hard", return_changes=False]) → object
singleSelection.delete([durability="hard", return_changes=False]) → object

(durability: possible values are hard and soft)

and when fetching results:

model.get_all()

anything with a soft delete is not included (and the same with any joined results)

Implement count() for Model, RelatedSet and RelatedM2MSet

Model, RelatedSet and RelatedM2MSet should implement a count() method to leverage counting documents for a particular model/set.

Currently, in order to see how many document a model counts, we need to:

len(list(Model.all()))

With count(), everything will become a lot simpler:

Model.count()

Model filtering should not use params that are empty strings ("") or None

So I have been messing around with remodel and I can say I like it but my biggest issue is filtering objects based on multiple POSSIBLE params.

For example i have a starship with name, category, id ....... , and want to filter by just the id, i use Starship.filter(id=.....) but for example if i want to filter by the other 2 params not knowing which was supplied i have to do a larger number of conditional statements (or at least i couldn't find another way) as if i try the following:
Starship.filter(category=category if category is not None else "", name=name if name is not None else "") it will obviously return nothing if one of them is an empty string.

Is there a way to filter by POSSIBLE params without using custom Model queries or am I stuck using those?

KeyError when creating tables

when running remodel.utils.create_tables(), I get the folllowing error:

Traceback (most recent call last):
  File "./manage.py", line 4, in <module>
    from mzk import app
  File "/Users/jamorton/dev/mzk2/mzk/__init__.py", line 11, in <module>
    remodel.utils.create_tables()
  File "/Users/jamorton/dev/mzk2/env/lib/python3.4/site-packages/remodel/utils.py", line 19, in create_tables
    if result['created'] != 1:
KeyError: 'created'

Using python 3.4, rethinkdb 1.16.0-1 (python package rethinkdb 1.16.0-2), and remodel 0.3.0.

I think this is because the key should actually be "tables_created", not "created", according to http://rethinkdb.com/api/javascript/table_create/

Implement model callbacks

Callbacks, hooks, signals, you name them, are present in almost all big ORMs/ODMs. They make the model definition cleaner by providing methods that should be executed at certain times during an object's lifespan (on creation, on deletion etc.).

One of the biggest advantages callbacks would bring to remodel is that you won't need to overwrite save() or delete() anymore to implement actions associated with these events (and also call super in them ;) ).

remodel will implement the following callbacks:

  • before_save, after_save;
  • before_delete, after_delete;
  • after_init.

You would define these callbacks either by defining an instance method with the callback's name:

class User(Model):
    def before_save(self):
        # Do some stuff

either by decorating an instance method with a callback:

class User(Model):
    @before_save
    def do_some_stuff(self):
        # Do some stuff

The second syntax allows the definition of multiple methods to be invoked for the same callback which would represent a great advantage for splitting a large callback method into several smaller ones.

Callbacks can raise an exception, in which case any further action is halted (e.g.: an exception raised in the before_save() callback won't execute save()).

get_or_create bug

With model Order taken from examples:

my_order = Order.get_or_create(customer='Andrei', shop='GitHub')
my_order
(<Order: 660c3f24-e897-460b-a4e3-40587cdbda41>, True)
my_order['customer']
Traceback (most recent call last):
File "", line 1, in
TypeError: tuple indices must be integers or slices, not str

In case of create returns a plain tuple instead of model object.

Incorrect error message in create_tables()

When performing table creation in create_tables() any RqlRuntimeError is transformed into a Table ... for model ... already created which isn't necessarily true in all cases.

If, for example, the database doesn't exist, a RqlRuntimeError is raised, resulting in an erroneous message.

Should fix the error message or should pass the original one.

Improve compatibility of __contains__ @line 149 in models.py

There a some incompatibility of checking several keys against fields names:

        Products = Code['pack']['value']['products'].all()
        products=[{'id':x['id'], 'name': x['name']} for x in Products if ('id', 'name') in x]

File "/home/proj3/venv-3.5/lib/python3.5/site-packages/remodel/models.py", line 149, in __ contains__
return hasattr(self.fields, item)
TypeError: hasattr(): attribute name must be string

Need to make check for iterables for keys, returning True if all of them in place.

Wrapping function calls __setattr_ validations

Hi,
Correct me if I'm wrong, in ObjectHandler._wrap method:

    def _wrap(self, doc):
        obj = self.model_cls()
        # Assign fields this way to skip validation
        obj.fields.__dict__ = doc
        return obj

Code for description Assign fields this way to skip validation doesn't do what it says. fields.__setattr__ is called anyway.

My proposal is to change code with this:

    def _wrap(self, doc):
        obj = self.model_cls()
        # Assign fields this way to skip validation
        obj.fields.__dict__.update(doc)
        return obj

By this way fetching data into model object is 1,8x faster.

Regards

Find substring within columns like SQLAlchemy's or_

I'm working on a project where I need to be able to search the database for a certain string in several different columns.

This was pretty simple with SQLAlchemy/SQLite (back before I started switching to RethinkDB):

results = Tickets.query.filter(or_(
            Tickets.issue.contains(search_term),
            Tickets.details.contains(search_term),
            Tickets.representative.contains(search_term),
            Tickets.who.contains(search_term)
))

However, I can't seem to find anything like this in remodel. Is this not a feature that exists, or is it just undocumented?

There's obviously the option of doing a raw RethinkDB call:

r.table('users').filter(lambda doc:
    doc['name'].match("(?i)^john$")
).run(conn)

but I have had no luck with that and I would prefer to do everything via remodel.

How does the RqlQuery.run() find the settings

I have gotten started with Remodel again after a bit of a hiatus. Started working on a api with the falcon framework and found that some parts of Remodel could really accommodate the coding.

I was wondering one thing, Its somewhat of an assumption. I have a theory so maybe you could just tell me if I´m completely wrong or not. In the remodel.Model classes there is for example a save() method. That save method is calling the RqlQuery.run() method which if not supplied a connection will look for one in cls.threadData or create one with settings from environment variables?

The connection pool is never used unless used with
with get_conn()
do stuff

Thanks a lot for all your efforts! It´s much appreciated

Default value for non-existent object keys

It should be a way to silently retrieve a non-existent object key, especially since remodel's interface wants to be dict-like (which has get(key[, default])).

In the end, we'll be able to do the following:

user = User(name='Andrei')

print user.get('name') # prints 'Andrei'
print user.get('address') # prints None
print user.get('address', 'Example Street') # prints 'Example Street'

"current instance isn't saved"-error

Here's the code-example. Simple enough stuff

def create_default_roles(
        roles = ['Administrator', 'Service Desk', 'Customer']):
    for role in roles:
        r = Role.get(name=role)
        if r is None:
            Role.create(name=role)

def create_default_user(username='admin',
                        name='Administrator',
                        email=current_app.config['ADMIN_EMAIL'],
                        password='admin',
                        role='Administrator'):
    aes = SimpleAES(current_app.config['SECRET_KEY'])
    if User.get(username=username) is None:
        User.create(name=name,
                    username=username,
                    email=email,
                    password=aes.encrypt(password),
                    role=Role.get(name=role))

remodel.connection.pool.configure(db=current_app.config['RDB_DB'])
remodel.utils.create_tables()
remodel.utils.create_indexes()

create_default_roles()
create_default_user()

And here's the error-message with relevant stack-trace

File "manage.py", line 35, in create_default_user
role=Role.get(name=role))
File "/Users/tsg/.virtualenv/cdn-api/lib/python2.7/site-packages/remodel/object_handler.py", line 16, in > create
obj = self.model_cls(**kwargs)
File "/Users/tsg/.virtualenv/cdn-api/lib/python2.7/site-packages/remodel/models.py", line 65, in init
setattr(self.fields, key, value)
File "/Users/tsg/.virtualenv/cdn-api/lib/python2.7/site-packages/remodel/field_handler.py", line 79, in setattr
super(FieldHandler, self).setattr(name, value)
File "/Users/tsg/.virtualenv/cdn-api/lib/python2.7/site-packages/remodel/related.py", line 52, in set
'saved' % value)
ValueError: Cannot assign "<Role: 353202a4-086a-4091-b58a-64fbf0013d8b>": current instance isn't saved

It might just be something trivial, but I can't see what. And the documentation is not very helpful on this

Active development?

I wanted to know if this project is still in active development as it looks very useful out of the box already

Self referencing table

Hey,

I'm trying to create a files table, which will hold records for primary files and it's child files (files assosacted with the primary files).

Basically, something simple like

----------------------------------------------------
|     ID     |     Filename     |     ParentID     |
----------------------------------------------------

I've defined my model like:

class File(Model):
    has_many = ('File',)

and then called it like:

child_file = models.File.create(processing=False, type=output_file['type'], file=file_record, original_filename=filename)

however, when trying to call this, I'm getting the following error:

TypeError: <File: 9f353838-f362-454d-8d96-126451eae882> is not JSON serializable

Saving an object removes all links to its related objects

I trying to repeat a demo tutorial with code:

from remodel.models import Model
from remodel.helpers import create_tables, create_indexes

class Products(Model):
    has_many=('Code',)
    has_one=('Value',)

class Code(Model):
    has_one=('Value',)

class Value(Model):
    belongs_to_many=('Product', 'Code',)

create_tables()
create_indexes()
code=Code.create(code='sample')
code['value'].add(Value.create(value=10))

And this code raises error:

Traceback (most recent call last):
  File "models-test.py", line 18, in <module>
    code['value'].add(Value.create(value=10))
AttributeError: 'NoneType' object has no attribute 'add'

Whats wrong with code?

Rename HABTM join_model

Currently, the join_model for a has_and_belongs_to_many relation is composed like:

_[FirstModelSingular][SecondModelPlural]

where the two models are ordered ascending by name (e.g.: for Post and Tag models, a _posttags table is created).

We should change the table name to add an extra underscore between the two models for extra readability. So, for Post and Tag models, create a _post_tags table.

Fix package included files in MANIFEST.in

Things have run in a fast pace with the latest release of remodel and MANIFEST.in has been forgotten in this process. There are some files which have changed since the latest revision of this file, which leads to the following warning when installing remodel:

Downloading/unpacking remodel
  Downloading remodel-0.3.0.tar.gz
  Running setup.py egg_info for package remodel

    warning: no files found matching 'AUTHORS'
    warning: no files found matching 'CONTRIBUTING.rst'

Nothing critical here, will include this fix in the next release of remodel (probably v0.4.0, not to pollute the release namespace with an additional release for such a minor issue).

Connection pool does not honor changing db

I've been trying to port my flask superframework stuff over to using remodel, and I've run into some problems.

First off, was just trying to figure out how to specify an auth_key.

Thankfully there was some 'old' documentation on the rethinkdb blog:
http://www.rethinkdb.com/blog/remodel/

It was the only place I found remodel.connection.pool.configure() documented.

Anyway, I'm using flask-classy, and each of my 'plugins' do their own imports.
However, everything wants the User object to be around for flask-login's methods to be happy.
https://github.com/kamilion/kaizen/blob/master/auth/authmodel.py#L23

I previously had each module in a different database, but in moving to remodel, I found I could only reliably talk to a single db, versus my previous bare reql.

https://github.com/kamilion/mcubed-web/blob/master/auth/authmodel.py#L95

Here's the change in configurations:
https://github.com/kamilion/kaizen/blob/master/app/config.py.example#L45
versus:
https://github.com/kamilion/mcubed-web/blob/master/app/config.py.example#L44

I'm trying to figure out what the best way to solve this problem is.

So far, my leaning is towards a deeper level change versus trying to bandaid the connection pool.

I could (in order of complexity of code):
alter the connection pool to be aware of each connection's db (ick, but my code doesn't change)
alter the Model to allow a 'default' db if not overridden, move r.db() from connection.pool -> Model
flush/empty the connection pool when a configuration change occurs (ick, sledgehammer)
alter the connection pool to provide a pool of connections for each db (ick, violates DRY)

Thoughts, feelings, flames, comments?

Cannot create_tables() multiple times

Running create_tables() on an existing database produces an "already exists" error. Somewhat understandable but as a schema develops it is bound to add new tables/models as time goes on. It seems to me that create_tables() should have an option to avoid creating already existing tables or at catching that error and carrying on.

Alternatively having a clear public interface for the application to do that work itself (exposing/documenting registry) would seem reasonable.

I haven't gotten that far, but I'm betting the same might be said for create_indexes...

Handling model hierarchies offline

Not sure how deep a change like this would go, but it would be handy to be able to use remodel Model objects without having them stored in a database. Currently this is not possible since as soon as you try to add a linked object, remodel needs a db key relationship. (Error: Cannot assign "<Test: not saved>": "Test" instance isn't saved)
I see no reason why vanilla Python object links wouldn't suffice until one of the objects in the hierarchy is actually saved to the DB.

Love the project, by the way!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.