Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ def is_dirty(self) -> bool:
def get_dirty(self) -> dict:
return {key: value for key, value in self.get_attributes().items() if not self.original_is_equivalent(key)}

def delete_attribute(self, key: str):
"""Remove a transient attribute that was added during query processing."""
self._attributes.pop(key, None)
self._dirty_attributes.pop(key, None)

def get_attributes_for_insert(self) -> dict:
# _dirty_attributes already went through set_attribute (casts applied on assignment).
# _attributes is set raw via new_model_instance, so apply set casts here.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,42 @@


class QueryBuilder(EagerLoadMixin, SupportMixin):
operators = [
"=",
"<",
">",
"<=",
">=",
"<>",
"!=",
"<=>",
"like",
"like binary",
"not like",
"ilike",
"&",
"|",
"^",
"<<",
">>",
"&~",
"is",
"is not",
"rlike",
"not rlike",
"regexp",
"not regexp",
"~",
"~*",
"!~",
"!~*",
"similar to",
"not similar to",
"not ilike",
"~~*",
"!~~*",
]

def __init__(self, connection: "Connection", grammar, processor):
super().__init__()
self.connection = connection
Expand Down Expand Up @@ -63,6 +99,10 @@ def with_(self, *eagers) -> "QueryBuilder":
def get_table_name(self) -> str:
return self._table

def table(self, table: str) -> "QueryBuilder":
self._table = table
return self

def where_in(self, column: str, values) -> "QueryBuilder":
if hasattr(values, "_items"):
values = values._items
Expand Down Expand Up @@ -134,6 +174,10 @@ def run_scopes(self) -> "QueryBuilder":
scope(self)
return self

def without_global_scopes(self) -> "QueryBuilder":
self._global_scopes = {}
return self

def get_grammar(self):
return self.grammar(
columns=self._columns,
Expand Down Expand Up @@ -424,6 +468,20 @@ async def chunk_by_id_desc(self, count: int, column: str = None, alias: str = No
def new(self):
return self.connection.query()

def invalid_operator(self, operator):
"""Determine whether an operator is not supported by the builder or grammar."""
if not isinstance(operator, str):
return True

operator = operator.lower()

grammar_operators = []
get_operators = getattr(self.grammar(), "get_operators", None)
if callable(get_operators):
grammar_operators = get_operators()

return operator not in self.operators and operator not in grammar_operators

def where(self, column, *args):
"""Specifies a where expression.

Expand Down
17 changes: 13 additions & 4 deletions fastapi_startkit/src/fastapi_startkit/masoniteorm/models/model.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
from __future__ import annotations
import inflection

from typing import TYPE_CHECKING

import inflection

from fastapi_startkit.carbon import Carbon
from fastapi_startkit.masoniteorm.collection import Collection
from fastapi_startkit.masoniteorm.models.fields import CreatedAtField, UpdatedAtField
from fastapi_startkit.masoniteorm.models.registry import Registry
from fastapi_startkit.masoniteorm.observers import ObservesEvents
from fastapi_startkit.masoniteorm.connections.manager import DatabaseManager
from fastapi_startkit.masoniteorm.models.attribute import Attribute
from fastapi_startkit.masoniteorm.models.fields import CreatedAtField, UpdatedAtField
from fastapi_startkit.masoniteorm.models.registry import Registry
from fastapi_startkit.masoniteorm.models.relationship import Relationship
from fastapi_startkit.masoniteorm.observers import ObservesEvents

if TYPE_CHECKING:
from fastapi_startkit.masoniteorm.models.builder import QueryBuilder
Expand Down Expand Up @@ -251,6 +252,14 @@ async def all(cls):
async def count(cls, column: str = "*"):
return await cls.query().count(column)

def table(self, table: str):
self.__table__ = table
return self

def timestamps(self, timestamps: bool = True):
self.__timestamps__ = timestamps
return self

@classmethod
def chunk(cls, count: int):
return cls.query().chunk(count)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import pendulum
from inflection import singularize

from fastapi_startkit.masoniteorm.models import registry
from .BaseRelationship import BaseRelationship
from ..collection import Collection
from ..models.pivot import Pivot
from .BaseRelationship import BaseRelationship
from fastapi_startkit.masoniteorm.models import registry


class BelongsToMany(BaseRelationship):
"""Has Many Relationship Class."""

def __init__(
self,
fn=None,
fn: str,
local_foreign_key=None,
other_foreign_key=None,
local_owner_key=None,
Expand All @@ -23,8 +23,7 @@ def __init__(
attribute="pivot",
with_fields=[],
):
if isinstance(fn, str):
self.fn = self.fn = lambda x: registry.Registry.resolve(fn)
self.fn = lambda: registry.Registry.resolve(fn)

self.local_key = local_foreign_key
self.foreign_key = other_foreign_key
Expand Down Expand Up @@ -134,15 +133,15 @@ async def apply_query(self, query, owner):
pivot_data.update({field: getattr(model, field)})
model.delete_attribute(field)

model.__original_attributes__.update(
{
self._as: (
Pivot.on(query.connection)
.table(self._table)
.hydrate(pivot_data)
.activate_timestamps(self.with_timestamps)
)
}
model.set_attribute(
self._as,
(
Pivot()
.on(query.connection)
.table(self._table)
.set_raw_attributes(pivot_data, True)
.timestamps(self.with_timestamps)
),
)

return result
Expand All @@ -151,11 +150,6 @@ def table(self, table):
self._table = table
return self

def make_builder(self, eagers=None):
builder = self.get_builder().with_(eagers)

return builder

async def make_query(self, query, relation, eagers=None, callback=None):
"""Used during eager loading a relationship

Expand Down Expand Up @@ -239,7 +233,6 @@ async def make_query(self, query, relation, eagers=None, callback=None):

async def get_related(self, query, relation, eagers=None, callback=None):
final_result = await self.make_query(query, relation, eagers=eagers, callback=callback)
builder = self.make_builder(eagers)

for model in final_result:
pivot_data = {
Expand All @@ -266,15 +259,15 @@ async def get_related(self, query, relation, eagers=None, callback=None):
pivot_data.update({field: getattr(model, field)})
model.delete_attribute(field)

model.__original_attributes__.update(
{
self._as: (
Pivot.on(builder.connection_name)
.table(self._table)
.hydrate(pivot_data)
.activate_timestamps(self.with_timestamps)
)
}
model.set_attribute(
self._as,
(
Pivot()
.on(query.connection)
.table(self._table)
.set_raw_attributes(pivot_data, True)
.timestamps(self.with_timestamps)
),
)

return final_result
Expand Down Expand Up @@ -497,7 +490,7 @@ def attach(self, current_model, related_record):
}
)

return Pivot.on(current_model.get_builder().connection).table(self._table).without_global_scopes().create(data)
return current_model.get_builder().connection.query().table(self._table).insert(data)

def detach(self, current_model, related_record):
data = {
Expand All @@ -508,9 +501,10 @@ def detach(self, current_model, related_record):
self._table = self._table or self.get_pivot_table_name(current_model, related_record)

return (
Pivot.on(current_model.get_builder().connection)
.table(self._table)
current_model.get_builder()
.connection.query()
.without_global_scopes()
.table(self._table)
.where(data)
.delete()
)
Expand All @@ -531,12 +525,7 @@ def attach_related(self, current_model, related_record):
}
)

return (
Pivot.table(self._table)
.on(current_model.get_builder().connection_name)
.without_global_scopes()
.create(data)
)
return current_model.get_builder().connection.query().table(self._table).insert(data)

def detach_related(self, current_model, related_record):
data = {
Expand All @@ -554,10 +543,4 @@ def detach_related(self, current_model, related_record):
}
)

return (
Pivot.on(current_model.get_builder().connection_name)
.table(self._table)
.without_global_scopes()
.where(data)
.delete()
)
return current_model.get_builder().connection.query().table(self._table).where(data).delete()
Loading
Loading