44import csv
55
66
7+ PRAZNO = sql .SQL ("" )
8+ PRESLEDEK = sql .SQL (" " )
9+ VEJICA = sql .SQL (", " )
10+
711TIPI = {
812 int : "INTEGER" ,
913 str : "TEXT" ,
@@ -29,82 +33,260 @@ def povezi(*largs, **kwargs):
2933
3034def stolpec (privzeto = None , glavni_kljuc = False , stevec = False , obvezen = False ,
3135 enolicen = False ):
36+ """
37+ Vrni polje, ki opisuje stolpec v tabeli.
38+ """
3239 return field (default = privzeto ,
3340 metadata = dict (glavni_kljuc = glavni_kljuc , stevec = stevec ,
3441 obvezen = obvezen , enolicen = enolicen ))
3542
3643
3744def tip (stolpec ):
45+ """
46+ Vrni tip stolpca v tabeli.
47+ """
3848 t = stolpec .type
3949 while issubclass (t , Entiteta ):
4050 t = t .glavni_kljuc ().type
4151 return TIPI [t ]
4252
4353
4454class Funkcija :
55+ """
56+ Razred za definicije funkcij v SQL.
57+ """
4558 def __init__ (self , niz ):
4659 self .niz = sql .SQL (niz )
4760
4861
4962class Entiteta :
63+ """
64+ Nadrazred za posamezne entitetne tipe.
65+ """
66+
67+ def __post_init__ (self ):
68+ """
69+ Inicializiraj polje z ID-jem iz baze.
70+ """
71+ self .__dbid = None
72+
73+ def __getitem__ (self , kljuc ):
74+ """
75+ Vrni vrednost v stolpcu v obliki, kot je zapisana v bazi.
76+ """
77+ vrednost = getattr (self , kljuc )
78+ if isinstance (vrednost , Entiteta ):
79+ return vrednost [vrednost .glavni_kljuc ().name ]
80+ else :
81+ return vrednost
82+
83+ def __nastavi_id (self ):
84+ """
85+ Nastavi ID na glavni ključ.
86+ """
87+ self .__dbid = self [self .glavni_kljuc ().name ]
88+
5089 @classmethod
5190 def ustvari_tabelo (cls , pobrisi = False , ce_ne_obstaja = False ):
91+ """
92+ Ustvari tabelo v bazi.
93+ """
5294 with conn .transaction ():
5395 if pobrisi :
5496 cls .izbrisi_tabelo (ce_ne_obstaja )
5597 with conn .cursor () as cur :
56- niz = sql .SQL ("""
98+ cur . execute ( sql .SQL ("""
5799 CREATE TABLE {ce_ne_obstaja} {tabela} ({stolpci});
58100 """ ).format (
59- ce_ne_obstaja = sql .SQL ("IF NOT EXISTS" if ce_ne_obstaja else "" ),
101+ ce_ne_obstaja = sql .SQL ("IF NOT EXISTS"
102+ if ce_ne_obstaja else "" ),
60103 tabela = sql .Identifier (cls .tabela ()),
61- stolpci = sql .SQL (", " ).join (
62- sql .SQL ("{ime} {tip} {lastnosti} {privzeto} {referenca}" ).format (
104+ stolpci = VEJICA .join (
105+ sql .SQL ("""
106+ {ime} {tip} {lastnosti} {privzeto} {referenca}
107+ """ ).format (
63108 ime = sql .Identifier (stolpec .name ),
64- tip = sql .SQL ("SERIAL" if stolpec .metadata ["stevec" ] else tip (stolpec )),
65- lastnosti = sql .SQL (" " ).join (
66- sql .SQL (niz ) for lastnost , niz in LASTNOSTI .items ()
109+ tip = sql .SQL ("SERIAL" if stolpec .metadata ["stevec" ]
110+ else tip (stolpec )),
111+ lastnosti = PRESLEDEK .join (
112+ sql .SQL (niz )
113+ for lastnost , niz in LASTNOSTI .items ()
67114 if stolpec .metadata [lastnost ]
68115 ),
69116 privzeto = sql .SQL ("DEFAULT {vrednost}" ).format (
70- vrednost = stolpec .default .niz if isinstance (stolpec .default , Funkcija )
117+ vrednost = stolpec .default .niz
118+ if isinstance (stolpec .default , Funkcija )
71119 else sql .Literal (stolpec .default )
72- ) if stolpec .default else sql . SQL ( "" ) ,
120+ ) if stolpec .default else PRAZNO ,
73121 referenca = sql .SQL ("REFERENCES {tab}({st})" ).format (
74122 tab = sql .Identifier (stolpec .type .tabela ()),
75123 st = sql .Identifier (stolpec .type .glavni_kljuc ().name )
76- ) if issubclass (stolpec .type , Entiteta ) else sql .SQL ("" )
124+ ) if issubclass (stolpec .type , Entiteta )
125+ else PRAZNO
77126 )
78127 for stolpec in fields (cls )
79- ))
80- cur .execute (niz )
128+ )))
81129
82130 @classmethod
83131 def izbrisi_tabelo (cls , ce_obstaja = False ):
132+ """
133+ Izbriši tabelo iz baze.
134+ """
84135 with conn .transaction ():
85136 with conn .cursor () as cur :
86- cur .execute (sql .SQL ("DROP TABLE {ce_obstaja} {tabela};" ).format (
137+ cur .execute (sql .SQL ("""
138+ DROP TABLE {ce_obstaja} {tabela};
139+ """ ).format (
87140 ce_obstaja = sql .SQL ("IF EXISTS" if ce_obstaja else "" ),
88141 tabela = sql .Identifier (cls .tabela ())
89142 ))
90143
91144 @classmethod
92145 def tabela (cls ):
146+ """
147+ Vrni ime tabele.
148+ """
93149 if not hasattr (cls , "TABELA" ):
94150 cls .TABELA = cls .__name__ .lower ()
95151 return cls .TABELA
96152
97153 @classmethod
98154 def glavni_kljuc (cls ):
155+ """
156+ Vrni definicijo glavnega ključa.
157+ """
99158 if not hasattr (cls , "GLAVNI_KLJUC" ):
100- cls .GLAVNI_KLJUC = next (stolpec for stolpec in fields (cls ) if stolpec .metadata ["glavni_kljuc" ])
159+ cls .GLAVNI_KLJUC = next (stolpec for stolpec in fields (cls )
160+ if stolpec .metadata ["glavni_kljuc" ])
101161 return cls .GLAVNI_KLJUC
102162
103163 @classmethod
104164 def uvozi_podatke (cls ):
105- with open (f"podatki/{ cls .tabela ()} .csv" ):
165+ """
166+ Uvozi podatke v tabelo.
167+ """
168+ with open (f"podatki/{ cls .tabela ()} .csv" ) as f :
106169 rd = csv .reader (f )
107170 stolpci = next (rd )
108- for vrstica in rd :
109- # vstavi podatke v bazo
110- pass
171+ with conn .transaction ():
172+ with conn .cursor () as cur :
173+ cur .executemany (sql .SQL ("""
174+ INSERT INTO {tabela} ({stolpci}) VALUES ({podatki});
175+ """ ).format (
176+ tabela = sql .Identifier (cls .tabela ()),
177+ stolpci = VEJICA .join (
178+ sql .Identifier (stolpec ) for stolpec in stolpci
179+ ),
180+ podatki = VEJICA .join (
181+ sql .Placeholder (stolpec ) for stolpec in stolpci
182+ )
183+ ), ({stolpec : (podatek if podatek else None )
184+ for stolpec , podatek in zip (stolpci , vrstica )}
185+ for vrstica in rd ))
186+
187+ @classmethod
188+ def z_id (cls , id ):
189+ """
190+ Vrni entiteto s podanim ID-jem.
191+ """
192+ with conn .cursor () as cur :
193+ cur .execute (sql .SQL ("""
194+ SELECT {stolpci} FROM {tabela}
195+ WHERE {glavni_kljuc} = {id};
196+ """ ).format (
197+ stolpci = VEJICA .join (sql .Identifier (stolpec .name )
198+ for stolpec in fields (cls )),
199+ tabela = sql .Identifier (cls .tabela ()),
200+ glavni_kljuc = sql .Identifier (cls .glavni_kljuc ().name ),
201+ id = sql .Literal (id )
202+ ))
203+ vrstica = cur .fetchone ()
204+ if not vrstica :
205+ raise ValueError (f'{ cls .__name__ } z ID-jem { id } ne obstaja!' )
206+ self = cls (* vrstica )
207+ self .__nastavi_id ()
208+ return self
209+
210+ def vstavi (self ):
211+ """
212+ Vstavi entiteto v tabelo.
213+ """
214+ stolpci = []
215+ podatki = []
216+ generirani = []
217+ for stolpec in fields (self ):
218+ vrednost = self [stolpec .name ]
219+ if vrednost is not None :
220+ stolpci .append (stolpec .name )
221+ if isinstance (vrednost , Funkcija ):
222+ podatki .append (vrednost .niz )
223+ generirani .append (stolpec .name )
224+ else :
225+ podatki .append (sql .Literal (vrednost ))
226+ elif stolpec .metadata ["stevec" ]:
227+ generirani .append (stolpec .name )
228+ with conn .transaction ():
229+ with conn .cursor () as cur :
230+ cur .execute (sql .SQL ("""
231+ INSERT INTO {tabela} ({stolpci}) VALUES ({podatki})
232+ {generirano};
233+ """ ).format (
234+ tabela = sql .Identifier (self .tabela ()),
235+ stolpci = VEJICA .join (sql .Identifier (stolpec )
236+ for stolpec in stolpci ),
237+ podatki = VEJICA .join (podatki ),
238+ generirano = sql .SQL ("RETURNING {generirani}" ).format (
239+ generirani = VEJICA .join (sql .Identifier (stolpec )
240+ for stolpec in generirani )
241+ ) if generirani else PRAZNO
242+ ))
243+ if generirani :
244+ for kljuc , vrednost in zip (generirani , cur .fetchone ()):
245+ setattr (self , kljuc , vrednost )
246+ self .__nastavi_id ()
247+
248+ def posodobi (self ):
249+ """
250+ Posodobi entiteto v bazi.
251+ """
252+ assert self .__dbid is not None , "Entiteta še ni v bazi!"
253+ with conn .transaction ():
254+ with conn .cursor () as cur :
255+ cur .execute (sql .SQL ("""
256+ UPDATE {tabela} SET {vrednosti}
257+ WHERE {glavni_kljuc} = {id};
258+ """ ).format (
259+ tabela = sql .Identifier (self .tabela ()),
260+ vrednosti = VEJICA .join (
261+ sql .SQL ("""
262+ {stolpec} = {vrednost}
263+ """ ).format (
264+ stolpec = sql .Identifier (stolpec .name ),
265+ vrednost = vrednost .niz
266+ if isinstance (vrednost , Funkcija )
267+ else sql .Literal (vrednost )
268+ )
269+ for stolpec in fields (self )
270+ for vrednost in [self [stolpec .name ]]
271+ ),
272+ glavni_kljuc = sql .Identifier (self .glavni_kljuc ().name ),
273+ id = sql .Literal (self .__dbid )
274+ ))
275+ self .__nastavi_id ()
276+
277+ def izbrisi (self ):
278+ """
279+ Izbriši entiteto iz baze.
280+ """
281+ assert self .__dbid is not None , "Entiteta še ni v bazi!"
282+ with conn .transaction ():
283+ with conn .cursor () as cur :
284+ cur .execute (sql .SQL ("""
285+ DELETE FROM {tabela}
286+ WHERE {glavni_kljuc} = {id};
287+ """ ).format (
288+ tabela = sql .Identifier (self .tabela ()),
289+ glavni_kljuc = sql .Identifier (self .glavni_kljuc ().name ),
290+ id = sql .Literal (self .__dbid )
291+ ))
292+ self .__dbid = None
0 commit comments