diff --git a/lib/db.py b/lib/db.py new file mode 100644 index 000000000..7383d92fe --- /dev/null +++ b/lib/db.py @@ -0,0 +1,9 @@ +# db.py +# engine and sessionmaker are created here + +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +engine = create_engine('sqlite:///freebies.db') +Session = sessionmaker(bind=engine) +# session = Session() \ No newline at end of file diff --git a/lib/debug.py b/lib/debug.py index 4f922eb69..fe83ed094 100644 --- a/lib/debug.py +++ b/lib/debug.py @@ -1,9 +1,84 @@ #!/usr/bin/env python3 -from sqlalchemy import create_engine +from db import Session -from models import Company, Dev +from models import Company, Dev, Freebie -if __name__ == '__main__': - engine = create_engine('sqlite:///freebies.db') - import ipdb; ipdb.set_trace() +session = Session() +import ipdb; + +# TEST. Run these in ipdb, these # => indicate expected results. + +# Relationship Attributes and Methods + +# Freebie +# # Access the first freebie instance in the database +# freebie = session.query(Freebie).first() +# freebie +# # => Freebie(id=1, item_name=others, value=209, company_id=1, dev_id=8) +# Get the Dev who received the freebie +# freebie.dev # == returns the Dev instance for this Freebie. +# # => Dev(id=8, name=Jennifer Hernandez) +# freebie.dev.name +# # => 'Jennifer Hernandez' +# freebie.company # == returns the Company instance for this Freebie. +# # => Company(id=1, name=Carter-Brandt, founding-year=1925) + +# Company +# # Access the first company instance in the database +# company = session.query(Company).first() +# company +# # => Company(id=1, name=Carter-Brandt, founding-year=1925) +# company.freebies # == returns a collection of all the freebies for the Company. +# # => [Freebie(id=1, item_name=others, value=209, company_id=1, dev_id=8), Freebie(id=2, item_name=have, value=346, company_id=1, dev_id=12), Freebie(id=3, item_name=same, value=377, company_id=1, dev_id=2)] +# company.devs # == returns a collection of all the devs who collected freebies from the company. +# # => [Dev(id=8, name=Jennifer Hernandez), Dev(id=12, name=Corey Wu), Dev(id=2, name=Willie Goodman)] + +# Dev +# # Access the first dev instance in the database +# dev = session.query(Dev).first() +# dev +# # => Dev(id=1, name=Chelsea Long) +# dev.freebies # == returns a collection of all the freebies that the Dev has collected. +# # => [Freebie(id=15, item_name=light, value=151, company_id=5, dev_id=1), Freebie(id=28, item_name=apply, value=87, company_id=8, dev_id=1), Freebie(id=40, item_name=watch, value=189, company_id=11, dev_id=1)] +# dev.companies # == returns a collection of all the companies that the Dev has collected freebies from. +# # => [Company(id=5, name=George LLC, founding-year=1914), Company(id=8, name=Thomas, Anderson and Larson, founding-year=2003), Company(id=11, name=Hall Ltd, founding-year=1938)] + + +# Aggregate Methods +# Freebie +# print(freebie.print_details()) # using our freebie instance. If you get an error: check print(freebie.__dict__), run freebie = session.query(Freebie).first() first or quit and restart python debug.py +# # => Jennifer Hernandez owns a others from Carter-Brandt + +# Company +# Testing give_freebie(dev, item_name, value) method in Company class. +company = session.query(Company).first() +dev = session.query(Dev).first() +freebie = company.give_freebie(dev, item_name="T-shirt", value=70, session=session) +print(freebie.print_details()) +# # => Chelsea Long owns a T-shirt from Carter-Brandt +# company.oldest_company() +# # => Company(id=2, name=David-Boyd, founding-year=1886) + +# Dev +# dev = session.query(Dev).first() get first dev +# print(dev.received_one('gas')) +# # => False +# print(dev.received_one('light')) +# # => True + +# Testing give_away(dev, freebie) method in Dev class. +dev1 = session.query(Dev).filter(Dev.name == "Jennifer Hernandez").first() +dev2 = session.query(Dev).filter(Dev.name == "Anita Romero").first() # id 3 in devs table +freebie = session.query(Freebie).first() # freebie to give away. According to the freebies table, the first conform to dev_id 8, "Jennifer Hernandez" + +# Ensure the freebie belongs to dev1 before trying to give it away +if freebie.dev == dev1: + updated_freebie = dev1.give_away(dev2, freebie) + print(f"Freebie '{updated_freebie.item_name}' has been transferred to {dev2.name}.") +else: + print(f"Freebie '{freebie.item_name}' does not belong to {dev1.name}.") + +# session.commit() +session.close() # close +ipdb.set_trace() diff --git a/lib/freebies.db b/lib/freebies.db index 12beb1c96..e32700f64 100644 Binary files a/lib/freebies.db and b/lib/freebies.db differ diff --git a/lib/migrations/versions/6cf5c7fb78fa_leverage_freebie_as_an_association_table.py b/lib/migrations/versions/6cf5c7fb78fa_leverage_freebie_as_an_association_table.py new file mode 100644 index 000000000..8a0be5838 --- /dev/null +++ b/lib/migrations/versions/6cf5c7fb78fa_leverage_freebie_as_an_association_table.py @@ -0,0 +1,28 @@ +"""Leverage Freebie as an Association Table + +Revision ID: 6cf5c7fb78fa +Revises: 719ebb52e4c8 +Create Date: 2025-05-25 13:20:50.773113 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '6cf5c7fb78fa' +down_revision = '719ebb52e4c8' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/lib/migrations/versions/719ebb52e4c8_add_freebie_model.py b/lib/migrations/versions/719ebb52e4c8_add_freebie_model.py new file mode 100644 index 000000000..c0f7b21a3 --- /dev/null +++ b/lib/migrations/versions/719ebb52e4c8_add_freebie_model.py @@ -0,0 +1,37 @@ +"""Add Freebie Model + +Revision ID: 719ebb52e4c8 +Revises: 5f72c58bf48c +Create Date: 2025-05-25 10:19:38.293281 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '719ebb52e4c8' +down_revision = '5f72c58bf48c' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('freebies', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('item_name', sa.String(), nullable=True), + sa.Column('value', sa.Integer(), nullable=True), + sa.Column('company_id', sa.Integer(), nullable=True), + sa.Column('dev_id', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['companies.id'], name=op.f('fk_freebies_company_id_companies')), + sa.ForeignKeyConstraint(['dev_id'], ['devs.id'], name=op.f('fk_freebies_dev_id_devs')), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('freebies') + # ### end Alembic commands ### diff --git a/lib/models.py b/lib/models.py index 2681bee5a..cf89e40ce 100644 --- a/lib/models.py +++ b/lib/models.py @@ -2,6 +2,9 @@ from sqlalchemy.orm import relationship, backref from sqlalchemy.ext.declarative import declarative_base +from db import Session # import session +session = Session() # start session + convention = { "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", } @@ -16,8 +19,23 @@ class Company(Base): name = Column(String()) founding_year = Column(Integer()) + freebies = relationship('Freebie', backref=backref('company')) + devs = relationship('Dev', secondary='freebies', back_populates='companies', viewonly=True) # leverage Freebie as join/association table for a many-to-many relationship between Company and Dev + + def give_freebie(self, dev, item_name, value, session): # using session from debug.py to avoid session conflicts + feebie = Freebie(dev=dev, company=self, item_name=item_name, value=value) # set dev=dev and company=self. The Company and the Dev will be linked through the foreign keys company_id and dev_id. We can also look up the ids and use them directly + session.add(feebie) # Add freebie to the session + session.commit() # Commit changes + return feebie + + @classmethod + def oldest_company(cls): + return session.query(Company).order_by(Company.founding_year).first() + def __repr__(self): - return f'' + return f'Company(id={self.id}, ' + \ + f'name={self.name}, ' + \ + f'founding-year={self.founding_year})' class Dev(Base): __tablename__ = 'devs' @@ -25,5 +43,41 @@ class Dev(Base): id = Column(Integer(), primary_key=True) name= Column(String()) + freebies = relationship('Freebie', backref=backref('dev')) # one-to-many relationship + companies = relationship('Company', secondary='freebies', back_populates='devs', viewonly=True) # viewonly set to True to prevent 'Dev.freebies' from copying column devs.id to column freebies.dev_id, as it causes conflicts with the relationship(s)- the one-to-many relationship above + + def received_one(self, item_name): + return any(freebie.item_name == item_name for freebie in self.freebies) # loop all Freebie instances associated with the class(Dev) since it defines the relationship, acessible in freebies + + def give_away(self, dev, freebie): + if freebie.dev != self: # feebie should belong to the dev giving it away + raise ValueError(f"The freebie '{freebie.item_name}' does not belong to this dev.") + freebie.dev = dev + return freebie # Return the updated freebie + def __repr__(self): - return f'' + return f'Dev(id={self.id}, ' + \ + f'name={self.name})' + +class Freebie(Base): + __tablename__ = 'freebies' + + id = Column(Integer(), primary_key=True) + item_name = Column(String()) + value = Column(Integer()) + + company_id= Column(Integer(), ForeignKey('companies.id')) + dev_id= Column(Integer(), ForeignKey('devs.id')) + + def print_details(self): + return f"{self.dev.name} owns a {self.item_name} from {self.company.name}" + + def __repr__(self): + return f'Freebie(id={self.id}, ' + \ + f'item_name={self.item_name}, ' + \ + f'value={self.value}, ' + \ + f'company_id={self.company_id}, ' + \ + f'dev_id={self.dev_id})' + + +session.close() # close the session diff --git a/lib/seed.py b/lib/seed.py index b16becbbb..1a285ca10 100644 --- a/lib/seed.py +++ b/lib/seed.py @@ -1,3 +1,67 @@ #!/usr/bin/env python3 # Script goes here! + +from faker import Faker +import random, choice + +from db import Session # import session + +from models import Company, Dev, Freebie + + +session = Session() # start session + +session.query(Company).delete() +session.query(Dev).delete() +session.query(Freebie).delete() + +fake = Faker() + +companies = [] +for i in range(15): + company = Company( + name=fake.unique.company(), + founding_year=random.randint(1870, 2025) + ) + + # add and commit individually to get IDs back + session.add(company) + session.commit() + + companies.append(company) + +devs = [] +for i in range(15): + dev = Dev( + name=fake.name() + ) + + # add and commit individually to get IDs back + session.add(dev) + session.commit() + + devs.append(dev) + +freebies = [] +for company in companies: + for i in range(random.randint(1,5)): + dev = random.choice(devs) + if company not in dev.companies: + dev.companies.append(company) + session.add(dev) + session.commit() + + freebie = Freebie( + item_name=fake.unique.word(), + value=random.randint(40, 400), + company_id=company.id, + dev_id=dev.id + ) + + freebies.append(freebie) + +session.bulk_save_objects(freebies) +session.commit() + +session.close() # close session