From 79ba76b22313f95b3dc33eba7720540805fd0b96 Mon Sep 17 00:00:00 2001 From: Michelle Di Guglielmo Date: Tue, 3 Feb 2026 22:47:21 +1100 Subject: [PATCH 1/2] done with relationships and data seeding --- lib/debug.py | 14 +++- lib/freebies.db | Bin 20480 -> 32768 bytes .../0b2ba6f06d01_added_freebie_table.py | 39 ++++++++++ ...updated_freebie_table_and_relationship_.py | 28 +++++++ lib/models.py | 41 +++++++++- lib/seed.py | 73 +++++++++++++++++- 6 files changed, 191 insertions(+), 4 deletions(-) create mode 100644 lib/migrations/versions/0b2ba6f06d01_added_freebie_table.py create mode 100644 lib/migrations/versions/8806fa7f641d_updated_freebie_table_and_relationship_.py diff --git a/lib/debug.py b/lib/debug.py index 4f922eb69..054bd9ff9 100644 --- a/lib/debug.py +++ b/lib/debug.py @@ -1,9 +1,19 @@ #!/usr/bin/env python3 +from faker import Faker +import random + from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +from models import Company, Dev, Freebie -from models import Company, Dev +fake = Faker() if __name__ == '__main__': engine = create_engine('sqlite:///freebies.db') - import ipdb; ipdb.set_trace() + Session = sessionmaker(bind=engine) + session = Session() + + import ipdb + ipdb.set_trace() diff --git a/lib/freebies.db b/lib/freebies.db index 12beb1c963e832db481e7a7493e3029e691ac4dc..550ed95631bf18708f54ee3ba72dcaeef453389a 100644 GIT binary patch literal 32768 zcmeI5U2GiH6@X`V#xpxV_uiS^wVgN#9w)IAH*rcx_@@QOcH$&!$61@Clpptc=6ZMW z?#wc?YqJqj8`P?_JXE5psv=%0Awk7M-=Ki1BBZv^s#HikK~Y=ipF~Bgib{Q|dhXtJ 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 MfFvLZeEJCd2eV-UB>(^b delta 114 zcmZo@U}{*vI6+#Fm4Sf)gkgYrqK>gBD}!FVEHD2L1{OXk27X@toqY9tQkw+@mho 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/migrations/versions/8806fa7f641d_updated_freebie_table_and_relationship_.py b/lib/migrations/versions/8806fa7f641d_updated_freebie_table_and_relationship_.py new file mode 100644 index 000000000..fc1b08ecb --- /dev/null +++ b/lib/migrations/versions/8806fa7f641d_updated_freebie_table_and_relationship_.py @@ -0,0 +1,28 @@ +"""Updated Freebie table and relationship columns in associated tables + +Revision ID: 8806fa7f641d +Revises: 0b2ba6f06d01 +Create Date: 2026-02-03 22:34:28.219506 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '8806fa7f641d' +down_revision = '0b2ba6f06d01' +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/models.py b/lib/models.py index 2681bee5a..a6972a3c1 100644 --- a/lib/models.py +++ b/lib/models.py @@ -1,6 +1,7 @@ from sqlalchemy import ForeignKey, Column, Integer, String, MetaData from sqlalchemy.orm import relationship, backref from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.ext.associationproxy import association_proxy convention = { "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", @@ -9,6 +10,15 @@ Base = declarative_base(metadata=metadata) +# company_dev = Table( +# 'company_devs', +# Base.metadata, +# Column('company_id', ForeignKey('companies.id'), primary_key=True), +# Column('dev_id', ForeignKey('devs.id'), primary_key=True), +# extend_existing=True, +# ) + + class Company(Base): __tablename__ = 'companies' @@ -16,14 +26,43 @@ class Company(Base): name = Column(String()) founding_year = Column(Integer()) + # relationships + freebies = relationship('Freebie', back_populates='company') + devs = association_proxy('freebies', 'dev', + creator=lambda dev: Freebie(dev=dev)) + def __repr__(self): return f'' + class Dev(Base): __tablename__ = 'devs' id = Column(Integer(), primary_key=True) - name= Column(String()) + name = Column(String()) + + # relationships + freebies = relationship('Freebie', back_populates='dev') + companies = association_proxy('freebies', 'company', + creator=lambda company: Freebie(company=company)) def __repr__(self): return f'' + + +class Freebie(Base): + __tablename__ = 'freebies' + + id = Column(Integer(), primary_key=True) + item_name = Column(String()) + value = Column(Integer()) + + # relationships and association object + company_id = Column(Integer(), ForeignKey('companies.id')) + dev_id = Column(Integer(), ForeignKey('devs.id')) + + company = relationship('Company', back_populates='freebies') + dev = relationship('Dev', back_populates='freebies') + + def __repr__(self): + return f'' diff --git a/lib/seed.py b/lib/seed.py index b16becbbb..eb7b96bfa 100644 --- a/lib/seed.py +++ b/lib/seed.py @@ -1,3 +1,74 @@ #!/usr/bin/env python3 +from faker import Faker +import random +from random import choice as rc +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker -# Script goes here! +from models import Company, Dev, Freebie + +fake = Faker() + +engine = create_engine('sqlite:///freebies.db') +Session = sessionmaker(bind=engine) +session = Session() + + +def create_companies(): + companies = [ + Company( + name=fake.company(), + founding_year=fake.year(), + ) + for i in range(20)] + session.add_all(companies) + session.commit() + return companies + + +def create_devs(): + devs = [ + Dev( + name=fake.name() + ) + for i in range(40)] + session.add_all(devs) + session.commit() + return devs + + +def create_freebies(): + freebies = [ + Freebie( + item_name=fake.word(), + value=random.randint(0, 20) + ) + for i in range(100)] + session.add_all(freebies) + session.commit() + return freebies + + +def delete_records(): + session.query(Company).delete() + session.query(Dev).delete() + session.query(Freebie).delete() + session.commit() + + +def relate_one_to_many(companies, devs, freebies): + for freebie in freebies: + freebie.company = rc(companies) + freebie.dev = rc(devs) + + session.add_all(freebies) + session.commit() + return companies, devs, freebies + + +if __name__ == "__main__": + delete_records() + companies = create_companies() + devs = create_devs() + freebies = create_freebies() + companies, devs, freebies = relate_one_to_many(companies, devs, freebies) From 60f25f087fb4fe2feea4ecfa671fdac06937fdcb Mon Sep 17 00:00:00 2001 From: Michelle Di Guglielmo Date: Fri, 6 Feb 2026 22:28:54 +1100 Subject: [PATCH 2/2] done with freebie tracker coding challenge --- lib/__pycache__/models.cpython-38.pyc | Bin 0 -> 3278 bytes lib/debug.py | 7 ++-- lib/freebies.db | Bin 32768 -> 32768 bytes lib/models.py | 49 ++++++++++++++++++++------ 4 files changed, 40 insertions(+), 16 deletions(-) create mode 100644 lib/__pycache__/models.cpython-38.pyc diff --git a/lib/__pycache__/models.cpython-38.pyc b/lib/__pycache__/models.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..16c57a00b066a78a507efb52820c041fdc1dd8d7 GIT binary patch literal 3278 zcmb7GOOG2x5T2eHkH@oPuf2}|;brkQV%I=;vqXqQ0fg8~0um#k(b(O)JG=96dS;i{ za!wnL`2`3mIpz*Og1^vLPMo1Yw-gTC&6vpbB>gwuoRo7Ql{Y|s!8F+sE?Z@~h zaKF=J@v$-a6r=bBgd3bCM#=(~nt_>GftA{Uoz{X{>I4p>bu+1_Zs4Ykppklkr)5^M zl=^|Mc{^#Qt)KG=iBmw6lWww^!1JJ*fg${(=Y=sC)}mP--uW?zZ1a<1gb zVVbG>msu%xg;dV)qi<=C{Vm+&P$hymJt~{8||e z5)o!mDtZN`Lv303=BWh#BBT^NT-2nmVh?N%hk14+GO`RpH-!Tn4oudIn@L=XkNW-o zjuc`Li(-=xpkfc=j1TEl7M~g>pJEhyAi@Y3Hv*Hhz!J8o9V4>bx@!gwX7ytO(Qzv| zxQ6h!P4UF~lJI$*yS%|Y#9`^K6*Rfen|F+$#anzCZ$yZ9@NV-JL}yjiujJ_{%EqcO z)C|nL@qnqNk&M$wj>7|ltA4F=K-DXq@dFmLC;;Io9~~kpqEPPKNo8?yG-U8%{t$0r zT)`*^@1-#Xl93A=>sC-I=4=3FGj=4h$(PAE{BwN^M@kiU}*DS0R!{RsWmmG3{Kd$?ULOyr}llu3~<(cgBep>yVkQ+ z0}x0xmQ?L1N)Cm*fG=o|d$#si)n^D+R>Zk-aKvd`_G-#5M6#nbb02=* ztKB!?AKUIZ@+GXR+D-U1mL1~VTyi19!B{nS^23Z%hKxldRc$AhMY&)%g;;9iTY17o zQ6iHx6ko?eVS*TJa$#=n!z$gQxPisF9Vcj+FK+ib3YzqtFjQU`0&IsUB%$)d@aAEZ%vPFVRX3`s zq=S`&E@j4C613!7MBX6c5_yt{529=&QXzXZ-z0L0h(5JbLKf?Tr^7EOS}wEA7W0^A z`OIO?KTgeEaxJa%pD>>koo})6v5HRPq||s~=}f8dXQc*BL6@2qZ{zek%0lMK5AhE- zzS45l$<@Qf|5?u*C04!4|T7h zK#KA(&%`HCC@9zl#fh@AO$IKioPQRN26$}?U+Bs{{q>tf-X?P9PzXTU$60deV>t!% zvV4a&y-VahBJUHSgVBhU9}ss6*@w|8souyfBIn|i0zzolwP}XaIZAEUk+eRrK(c}(ixc-Ydg zLTiQNiC@(#YV0we)H$*hjLx(Q+1j78o$zccG4|QMiCRT9%c7cvw@s3tVI^JKstV?I z^B=OJ8+9j6cSq)F7agL8CY_a4i_ld>!%rv1Bf_tmT0P{rp~Ok?EgYFwxAkjPgWr5e z%(GdA=!GQMMfKxQCJT@A&*DvC0l5ib(vo;dOJa7;cysOHp z4@TFW%cOiROz9r-IYzMta#~K?tZn+tw@^$;W4rgH@^HtClHs06$NIk)l11kU#ah-u zg4U_#W-imppav~dZ3`|gEQ?#^=E5abE|J3XXA~B0wE99@bqu&Zzl+Lc+Gc--dy;ah zOLS1bUfq>-*RF6%5UqfHS}QbHwZg5ct6e4#7bNepHAaVE8s' @@ -46,6 +57,18 @@ class Dev(Base): companies = association_proxy('freebies', 'company', creator=lambda company: Freebie(company=company)) + # methods + def received_one(self, item_name): + for freebie in self.freebies: + if freebie.item_name == item_name: + return True + return False + + def give_away(self, dev, freebie): + if freebie.dev == self: + freebie.dev = dev + session.commit() + def __repr__(self): return f'' @@ -64,5 +87,9 @@ class Freebie(Base): company = relationship('Company', back_populates='freebies') dev = relationship('Dev', back_populates='freebies') + # i have not idea what i am doing + def print_details(self): + print(f"{self.dev.name} owns a {self.item_name} from {self.company.name}") + def __repr__(self): return f''