Skip to content
This repository has been archived by the owner on May 14, 2024. It is now read-only.

Use of scope in relationship #350

Open
wants to merge 3 commits into
base: 0.9
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions orator/orm/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,10 @@ def __init__(self, _attributes=None, **attributes):
self._boot_if_not_booted()

self._exists = False
self._without_scope_name = None
self._original = {}


# Setting default attributes' values
self._attributes = dict((k, v) for k, v in self.__attributes__.items())
self._relations = {}
Expand Down Expand Up @@ -803,6 +805,9 @@ def belongs_to(

query = instance.new_query()

if self.without_scope_name:
query.without_global_scope(self.without_scope_name)

if not other_key:
other_key = instance.get_key_name()

Expand Down Expand Up @@ -1829,6 +1834,7 @@ def _should_set_timestamp(self, timestamp):
if isinstance(self.__timestamps__, bool):
return self.__timestamps__


return timestamp in self.__timestamps__

def set_created_at(self, value):
Expand Down Expand Up @@ -1904,6 +1910,8 @@ def new_query_without_scope(self, scope):
"""
builder = self.new_query()

self.set_without_scope_name(scope)

return builder.without_global_scope(scope)

def new_query_without_scopes(self):
Expand Down Expand Up @@ -2822,6 +2830,13 @@ def get_dirty(self):
def exists(self):
return self._exists

@property
def without_scope_name(self):
return self._without_scope_name

def set_without_scope_name(self, s):
self._without_scope_name = s

def set_exists(self, exists):
self._exists = exists

Expand Down Expand Up @@ -2962,6 +2977,7 @@ def __setattr__(self, key, value):
"_exists",
"_relations",
"_original",
"_without_scope_name"
] or key.startswith("__"):
return object.__setattr__(self, key, value)

Expand Down Expand Up @@ -2990,6 +3006,7 @@ def __getstate__(self):
"attributes": self._attributes,
"relations": self._relations,
"exists": self._exists,
"without_scope_name": self._without_scope_name,
}

def __setstate__(self, state):
Expand All @@ -2998,3 +3015,4 @@ def __setstate__(self, state):
self.set_raw_attributes(state["attributes"], True)
self.set_relations(state["relations"])
self.set_exists(state["exists"])
self.set_without_scope_name(state["without_scope_name"])
5 changes: 5 additions & 0 deletions orator/orm/relations/relation.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,11 @@ def new_query(self, related=None):
if self._extra_query:
query.merge(self._extra_query.get_query())

if self._extra_query:
scope_in_relation = getattr(self._extra_query.get_model(), "without_scope_name")
if scope_in_relation:
query = query.without_global_scope(scope_in_relation)

return query

def new_instance(self, model, **kwargs):
Expand Down
3 changes: 3 additions & 0 deletions orator/orm/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ def __get__(self, instance, owner):
self._conditions = self._related
self._related = self._related.get_model().__class__

# Pass the relation specific scope to the instance, so can be used with the query
instance.set_without_scope_name(self._conditions.get_model().without_scope_name)

relation = self._get(instance)

if self._conditions:
Expand Down
54 changes: 54 additions & 0 deletions tests/orm/relations/test_decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
morph_many,
belongs_to,
)
from orator import SoftDeletes
from orator.orm.model import ModelRegister
from orator.connections import SQLiteConnection
from orator.connectors.sqlite_connector import SQLiteConnector
Expand All @@ -28,6 +29,20 @@ def setUp(self):
with self.schema().create("test_users") as table:
table.increments("id")
table.string("email").unique()
table.datetime("deleted_at").nullable()
table.timestamps()

with self.schema().create("test_cars") as table:
table.increments("id")
table.string("company_name")
table.datetime("deleted_at").nullable()
table.timestamps()

with self.schema().create("test_car_models") as table:
table.increments("id")
table.string("model_name")
table.integer("car_id")
table.datetime("deleted_at").nullable()
table.timestamps()

with self.schema().create("test_friends") as table:
Expand All @@ -53,6 +68,7 @@ def tearDown(self):
self.schema().drop("test_friends")
self.schema().drop("test_posts")
self.schema().drop("test_photos")
self.schema().drop("test_cars")

def test_extra_queries_are_properly_set_on_relations(self):
self.create()
Expand Down Expand Up @@ -132,6 +148,18 @@ def test_extra_queries_are_properly_set_on_relations(self):
user.posts().order_by("user_id").distinct().to_sql(),
)

car = OratorTestCar.where("company_name", "Toyota").first()
self.assertEqual(2, len(car.car_models))

model = car.car_models.first()
self.assertEqual("Toyota", model.car.company_name)


# Soft Delete the car, after that model should be able to access the car
model.car.delete()
model = model.fresh()
self.assertEqual("Toyota", model.car.company_name)

def create(self):
user = OratorTestUser.create(id=1, email="[email protected]")
friend = OratorTestUser.create(id=2, email="[email protected]")
Expand All @@ -147,6 +175,14 @@ def create(self):
post1.photos().create(name="Hero 1")
post1.photos().create(name="Hero 2")

car1 = OratorTestCar.create(company_name="Toyota")
car2 = OratorTestCar.create(company_name="Honda")
car1.car_models().create(model_name="Corolla")
car1.car_models().create(model_name="Prius")

# user.cars().create(company_name="Toyota", model="Prius")
# user.cars().create(company_name="Toyota", model="Corolla")

def connection(self):
return Model.get_connection_resolver().connection()

Expand Down Expand Up @@ -205,6 +241,24 @@ def imageable(self):
return


class OratorTestCar(Model, SoftDeletes):
__table__ = "test_cars"
__fillable__ = ["company_name"]

@has_many("car_id")
def car_models(self):
return OratorTestCarModels


class OratorTestCarModels(Model, SoftDeletes):
__table__ = "test_car_models"
__fillable__ = ["model_name"]

@belongs_to("car_id")
def car(self):
return OratorTestCar.with_trashed()


class DatabaseIntegrationConnectionResolver(object):

_connection = None
Expand Down