QuerySet

class sheraf.queryset.QuerySet(iterable=None, model_class=None, predicate=None, primary_key=None, **kwargs)[source]

Bases: object

A QuerySet is a collection containing Model instances. Like in a regular set, objects are unique, but the main difference is that QuerySet keeps the insertion order.

Parameters
  • iterable (Iterable) – A collection of models. If iterable is None, then model must be set.

  • model_class – A model class to iterate over. If model is None, then iterable must be set. If both are set, model_class is ignored.

  • predicate (Predicate) – a callable takes an instance as parameter and return a Boolean (if True, the instance will be returned in the iteration). If None, everything is returned.

  • kwargs – A dictionnary containing the values expected from the model parameters. If kwargs is {“foo”: “bar”} then the queryset will only contains models which attribute foo is “bar”.

For the following examples, let us work with a simple Cowboy model. For the sake of simplicity we use a IntOrderedNamedAttributesModel so the first instance created will have id 0, the second will have id 1 and so on…

>>> class Cowboy(sheraf.IntOrderedNamedAttributesModel):
...     table = "queryset_people"
...     name = sheraf.SimpleAttribute()
...     age = sheraf.SimpleAttribute()
...
>>> with sheraf.connection(commit=True):
...     peter = Cowboy.create(name="Peter", age=30)
...     steven = Cowboy.create(name="Steven", age=30)
...     george = Cowboy.create(name="George Abitbol", age=50)

QuerySet are mostly created by doing requests on a Model with all(), filter() or order(), but can also be initialized with custom data.

>>> with sheraf.connection():
...    a = Cowboy.all() # returns a QuerySet with all Cowboy
...    b = QuerySet([peter, steven, george]) # This is an equivalent custom QuerySet
...    assert list(a) == list(b)

QuerySet behave like iterators, and can only be consumed once.

>>> with sheraf.connection():
...    everybody = Cowboy.all()
...    assert [peter, steven, george] == list(everybody)
...    assert [] == everybody

Note

QuerySet can be compared against anything iterable, but the comparison will consume the QuerySet.

QuerySet keeps the order of insertion.

>>> assert QuerySet([peter, steven]) != QuerySet([steven, peter])

QuerySet supports slicing. Slices returns another QuerySet.

>>> with sheraf.connection():
...     assert peter == Cowboy.all()[0]
...     assert QuerySet([peter, steven]) == Cowboy.all()[0:2]
...     assert QuerySet([peter, steven, george]) == Cowboy.all()[0:]
copy()[source]

Copies the QuerySet without consuming it.

>>> with sheraf.connection():
...     peter = Cowboy.create(name="Peter")
...     steven = Cowboy.create(name="Steven")
...     george = Cowboy.create(name="George")
...     qall = Cowboy.all()
...     qcopy = qall.copy()
...
...     assert [peter, steven, george] == qall
...     # now qall is consumed
...
...     assert [peter, steven, george] == qcopy
...     # now qcopy is consumed
delete()[source]

Delete the objects contained in the queryset.

Avoids problems when itering on deleted objects.

filter(predicate=None, **kwargs)[source]

Refine a copy of the current QuerySet with further tests.

Parameters
  • predicate (callable object) – filter instance by returning a truthy value. If None everything is selected.

  • kwargs (A dictionary which keys must be valid attributes of the model iterated.) – A dictionnary containing the values expected from the model parameters. If kwargs is {"foo": "bar"} then the queryset will only contains models which attribute foo is "bar".

Returns

A copy of the current QuerySet refined with further tests.

Return type

QuerySet

It is possible to chain filter() calls:

>>> with sheraf.connection():
...    assert Cowboy.filter(name="George Abitbol", age=50) == \
...           Cowboy.filter(name="George Abitbol").filter(age=50)
...
>>> with sheraf.connection():
...    assert Cowboy.filter(lambda person: "Abitbol" in person.name, age=50) == \
...           Cowboy.filter(lambda person: "Abitbol" in person.name).filter(age=50)

An attribute cannot be filtered twice:

>>> with sheraf.connection():
...    Cowboy.filter(age=30).filter(age=40)
Traceback (most recent call last):
    ...
sheraf.exceptions.InvalidFilterException: Some filter parameters appeared twice

Note

Filtering on indexed attributes is more performant than filtering on non-indexed attributes. See index().

get()[source]

If the QuerySet contains one, and only one item, this method returns the item. If the QuerySet contains several objects, it raises a QuerySetUnpackException. If the QuerySet is empty, it raises a EmptyQuerySetUnpackException.

>>> with sheraf.connection():
...     peter = Cowboy.create(name="Peter")
...     steven = Cowboy.create(name="Steven")
...     assert peter == Cowboy.filter(name="Peter").get()
...     Cowboy.all().get()
Traceback (most recent call last):
    ...
sheraf.exceptions.QuerySetUnpackException: Trying to unpack more than 1 value from a QuerySet
>>> with sheraf.connection():
...     Cowboy.filter(age=30).get()
Traceback (most recent call last):
    ...
sheraf.exceptions.EmptyQuerySetUnpackException: Trying to unpack an empty QuerySet
>>> with sheraf.connection():
...     Cowboy.filter(name="Unknown cowboy").get()
Traceback (most recent call last):
    ...
sheraf.exceptions.EmptyQuerySetUnpackException: Trying to unpack an empty QuerySet
order(*args, **kwargs)[source]

Copies the current QuerySet and adds more order to it.

Parameters
  • args (sheraf.ASC or sheraf.DESC) – There can be only one positionnal argument. Choose to iterate over ids in an ascending or a descending way.

  • kwargs (A dictionary which keys must be valid attributes of the model iterated, and the values must be sheraf.ASC or sheraf.DESC) – Further parameters will set an order on the matching model attributes.

Returns

A copy of the current QuerySet with refined order.

Return type

QuerySet

The default order is the ascending model ids.

>>> with sheraf.connection(commit=True):
...     peter = Cowboy.create(name="Peter", age=35)
...     steven = Cowboy.create(name="Steven", age=35)
...     george = Cowboy.create(name="George", age=50)
...
>>> with sheraf.connection():
...     assert [peter, steven, george] == Cowboy.all()
...     assert [peter, steven, george] == Cowboy.all().order(sheraf.ASC)
...     assert [george, steven, peter] == Cowboy.all().order(sheraf.DESC)
...
...     assert [george, peter, steven] == Cowboy.all().order(name=sheraf.ASC)
...     assert [steven, peter, george] == Cowboy.all().order(name=sheraf.DESC)

Several order parameters can be passed, either as arguments of the function, or by calling order() calls.

>>> with sheraf.connection():
...     assert [george, peter, steven] == Cowboy.all().order(age=sheraf.DESC, name=sheraf.ASC)

Note

Sorting on indexed attributes is more performant than sorting on other attributes. See index(). The less order() parameters are passed, the better performances will be.

search(**kwargs)[source]

Refine a copy of the current QuerySet with further tests.

This method is very similar to filter() except the values it takes are transformed with the same way values are transformed at indexation. TODO: pas très clair

For instance, if an attribute indexes its values with a lowercase search_func, the search() attributes will go through the same search_func. Hence it allows to pass uppercase filter values, while filter() does not allow this.

>>> class MyCustomModel(sheraf.Model):
...     table = "my_custom_model"
...     my_attribute = sheraf.SimpleAttribute().index(
...        index_keys_func=lambda string: {string.lower()}
...     )
...
>>> with sheraf.connection(commit=True):
...     m = MyCustomModel.create(my_attribute="FOO")
...
>>> with sheraf.connection():
...     assert [m] == MyCustomModel.search(my_attribute="foo")
...     assert [m] == MyCustomModel.filter(my_attribute="foo")
...
...     assert [m] == MyCustomModel.search(my_attribute="FOO")
...     assert [] == MyCustomModel.filter(my_attribute="FOO")