Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
josephmancuso committed Nov 23, 2024
1 parent 5da7c38 commit 723d409
Show file tree
Hide file tree
Showing 11 changed files with 311 additions and 3 deletions.
116 changes: 116 additions & 0 deletions playground.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
class Database:
"""
Simulate a database query. Replace this with actual database calls.
"""
@staticmethod
def get(table, conditions):
# Dummy data for demonstration
data = {
"companies": [{"company_id": 1, "name": "Acme Corp"}],
"users": [{"user_id": 1, "company_id": 1, "name": "John Doe"}],
}
return data['companies'][0]


class HasOne:
def __init__(self, parent, related_model, foreign_key, local_key):
self.parent = parent
self.related_model = related_model
self.foreign_key = foreign_key
self.local_key = local_key
self._related_instance = None

def get(self):
"""
Perform the database query to fetch the related model.
"""
if self._related_instance is None:
related_data = Database.get(
self.related_model.table_name(),
{self.foreign_key: getattr(self.parent, self.local_key)}
)
if related_data:
self._related_instance = self.related_model(**related_data)
return self._related_instance

def __call__(self):
"""
Allow the relationship instance to be callable.
"""
return self


class RelationshipProperty:
"""
A wrapper for dual behavior: as a property and as a callable returning the relationship instance.
"""
def __init__(self, relationship):
self.relationship = relationship

def __getattr__(self, name):
"""
Delegate attribute access to the related model instance.
"""
print("Delegating attribute access to the related model instance")
related_instance = self.relationship.get()
if related_instance:
return getattr(related_instance, name)
raise AttributeError(f"{self.__class__.__name__} has no attribute '{name}'")

def __call__(self):
"""
Make the relationship callable to return the relationship instance.
"""
return self.relationship

def __repr__(self):
return repr(self.relationship.get())




class Model:
"""
Base model class to share common functionality.
"""
@classmethod
def table_name(cls):
return cls.__name__.lower() + "s" # Example: User -> users

def has_one(self, related_model, foreign_key=None, local_key="id"):
return RelationshipProperty(HasOne(self, related_model, foreign_key, local_key))


class Company(Model):
def __init__(self, company_id, name):
self.company_id = company_id
self.name = name

def welcome(self):
return f"Welcome to {self.name}"

class User(Model):
def __init__(self, user_id, company_id, name):
self.user_id = user_id
self.company_id = company_id
self.name = name

@property
def company(self):
"""
Return a RelationshipProperty wrapper for dual behavior.
"""
return self.has_one(Company, "company_id", "company_id")


# Usage
user = User(user_id=1, company_id=1, name="John Doe")

# Access the related company instance as a property
related_company = user.company
print("property", related_company) # Output: Acme Corp
print("property", related_company.name, related_company.name=="Acme Corp") # Output: Acme Corp

# Call the relationship instance to get the related company
related_company_callable = user.company()
print("callable", isinstance(related_company_callable, HasOne)) # Output: True
47 changes: 47 additions & 0 deletions playground2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from src.masoniteorm.query import QueryBuilder
from src.masoniteorm.models import Model
from src.masoniteorm.models.relationships import HasOne


class CreditCard(Model):
__table__ = "company_credit_cards"
__primary_key__ = "credit_card_id"


class Company(Model):
__table__ = "tbl_companies"

@property
def cards(self):
"""
Return a RelationshipProperty wrapper for dual behavior.
"""
return self.has_many(CreditCard, "company_id", "company_id")

class User(Model):
__table__ = "tbl_users"
__primary_key__ = "user_id"

@property
def company(self):
"""
Return a RelationshipProperty wrapper for dual behavior.
"""
return self.has_one(Company, "company_id", "company_id")


# Usage
# user = User(user_id=1, company_id=1, name="John Doe")
user = User.find(667)

# Access the related company instance as a property
# related_company = user.company
# print("property name", related_company.company_name) # Output: Acme Corp
# print("property 2", related_company.company_name, related_company.company_name=="Acme Corp") # Output: Acme Corp

# # Call the relationship instance to get the related company
# related_company_callable = user.company().get()
company = user.company
print(company.cards().to_sql())
# print("callable", isinstance(user.company(), HasOne)) # Output: True

37 changes: 35 additions & 2 deletions src/masoniteorm/models/Model.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@
from ..observers import ObservesEvents
from ..query import QueryBuilder
from ..scopes import TimeStampsMixin
from .relationships.new import HasOne, HasMany

"""This is a magic class that will help using models like User.first() instead of having to instatiate a class like
User().first()
"""


class ModelMeta(type):
def __getattr__(self, attribute, *args, **kwargs):
"""This method is called between a Model and accessing a property. This is a quick and easy
Expand Down Expand Up @@ -1175,3 +1174,37 @@ def filter_guarded(cls, dictionary: Dict[str, Any]) -> Dict[str, Any]:
# If all fields are guarded, all data should be filtered
return {}
return {f: dictionary[f] for f in dictionary if f not in cls.__guarded__}


# move to relationships class
def has_one(self, related_model, foreign_key=None, local_key="id"):
return RelationshipProperty(HasOne(self, related_model, foreign_key, local_key))

def has_many(self, related_model, foreign_key=None, local_key="id"):
print("setting up has many")
return RelationshipProperty(HasMany(self, related_model, foreign_key, local_key))
class RelationshipProperty:
"""
A wrapper for dual behavior: as a property and as a callable returning the relationship instance.
"""
def __init__(self, relationship):
self.relationship = relationship

def __getattr__(self, name):
"""
Delegate attribute access to the related model instance.
"""
related_instance = self.relationship.get()
if related_instance:
return getattr(related_instance, name)
raise AttributeError(f"{self.__class__.__name__} has no attribute from relation '{name}'")

def __call__(self):
"""
Make the relationship callable to return the relationship instance.
"""
print("Calling relationship")
return self.relationship.apply_query()

# def __repr__(self):
# return repr(self.relationship)
2 changes: 1 addition & 1 deletion src/masoniteorm/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .Model import Model
from .Model import Model
27 changes: 27 additions & 0 deletions src/masoniteorm/models/relationships/HasOne.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class HasOne:
def __init__(self, parent, related_model, foreign_key, local_key):
self.parent = parent
self.related_model = related_model
self.foreign_key = foreign_key
self.local_key = local_key
self._related_instance = None

def get(self):
"""
Perform the database query to fetch the related model.
"""
print("getting")
if self._related_instance is None:
self._related_instance = self.related_model.where(self.foreign_key, getattr(self.parent, self.foreign_key)).first()

return self._related_instance

def __call__(self):
"""
Allow the relationship instance to be callable.
"""
print("calling")
return self

def where(self, key, value):
return self.related_model.where(key, value)
1 change: 1 addition & 0 deletions src/masoniteorm/models/relationships/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .HasOne import HasOne
Empty file.
40 changes: 40 additions & 0 deletions src/masoniteorm/models/relationships/new/HasMany.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
class HasMany:
def __init__(self, parent, related_model, foreign_key, local_key):
self.parent = parent
self.related_model = related_model
self.foreign_key = foreign_key
self.local_key = local_key
self._related_instance = None

def get(self):
"""
Perform the database query to fetch the related model.
"""
print("getting has many")
if self._related_instance is None:
self._related_instance = self.apply_query().get()

return self._related_instance

def __call__(self):
"""
Allow the relationship instance to be callable.
"""
print("calling")
return self.relationship.apply_query().get()

def where(self, key):
return self.related_model.where(key, value)

def apply_query(self):
"""Apply the query and return a dictionary to be hydrated
Arguments:
foreign {oject} -- The relationship object
owner {object} -- The current model oject.
Returns:
dict -- A dictionary of data which will be hydrated.
"""
print("applying query has many")
return self.related_model.where(self.foreign_key, getattr(self.parent, self.foreign_key))
41 changes: 41 additions & 0 deletions src/masoniteorm/models/relationships/new/HasOne.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
class HasOne:
def __init__(self, parent, related_model, foreign_key, local_key):
self.parent = parent
self.related_model = related_model
self.foreign_key = foreign_key
self.local_key = local_key
self._related_instance = None

def get(self):
"""
Perform the database query to fetch the related model.
"""
if self._related_instance is None:
self._related_instance = self.apply_query().first()

return self._related_instance

def __call__(self):
"""
Allow the relationship instance to be callable.
"""
return self

def __repr__(self):
return repr(self.related_model)

def where(self, key):
return self.related_model.where(key, value)

def apply_query(self):
"""Apply the query and return a dictionary to be hydrated
Arguments:
foreign {oject} -- The relationship object
owner {object} -- The current model oject.
Returns:
dict -- A dictionary of data which will be hydrated.
"""

return self.related_model.where(self.foreign_key, getattr(self.parent, self.foreign_key))
Empty file.
3 changes: 3 additions & 0 deletions src/masoniteorm/models/relationships/new/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .HasOne import HasOne
from .HasMany import HasMany
# from .BelongsTo import BelongsTo

0 comments on commit 723d409

Please sign in to comment.