Black Friday = Pricewatch Bekijk onze selectie van de beste Black Friday-deals en voorkom een miskoop.

[SQLalchemy] resultaat van join opslaan

Pagina: 1
Acties:

  • Boudewijn
  • Registratie: Februari 2004
  • Niet online

Boudewijn

omdat het kan

Topicstarter
Hoihoi

Ik ben bezig met een projectje in python met het pylons framework waar ik een sql join uit wil voeren dmv postgresql. Vrij simpel allemaal , maar het wil maar niet.
Daarom deze post.

Situatie omschrijving:

Ik heb een tabel Fiber (wat een fysiek stuk glasvezel is) en een tabel Line (glasvezel tussen 2 punten, kunnen er ook meerdere zijn ,maar dat is het issue niet).
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
fiberdb=# \d "Fiber";
                                             Table "public.Fiber"
         Column         |           Type           |                         Modifiers
------------------------+--------------------------+-----------------------------------------------------------
 FiberID                | integer                  | not null default nextval('"Fiber_FiberID_seq"'::regclass)
 LineName               | character varying(50)    |
 FiberUsed              | character(1)             |
Indexes:
    "PRI_Fiber" PRIMARY KEY, btree ("FiberID")
    "FKI_Fiber_Line" btree ("LineName")
    "fki_" btree ("LineName")
Foreign-key constraints:
    "FK_Fiber_Line" FOREIGN KEY ("LineName") REFERENCES "Line"("LineName") ON UPDATE CASCADE ON DELETE RESTRICT

Ik heb een hele zwik niet relevante velden weggelaten.


Lines hebben een LineName, en bestaan uit een of meerdere Fibers.
Dit alles ligt keurig in sqlalchemy.


Nu wil ik dus het FiberUsed veld uit de Fiber objecten hebben en die per Line uitlezen.


De huidige code werkt zonder die join tussen die 2 tables, en is dus okay. Beide tabellen worden ook op andere plekken gebruikt, dus de ORM is gewoon goed.
Dit werkt bijv prima:
Python:
1
2
3
4
5
6
        def _get_list_Lines_between_Sites(self, site1, site2, LineDiscarded):
                query = models.Session.query(models.Line)                    
                if site2 == None:                                            
                                return query.filter(and_(models.Line.LineDiscarded == LineDiscarded, or_(models.Line.SiteLocationCodeA == site1,models.Line.SiteLocationCodeB == site1)))
                else:
                        return query.filter(and_(models.Line.LineDiscarded == LineDiscarded, or_(and_(models.Line.SiteLocationCodeA == site1,models.Line.SiteLocationCodeB == site2),and_(models.Line.SiteLocationCodeA == site2,models.Line.SiteLocationCodeB == site1) )))

Behalve dan dat dit dus maar op 1 table werkt.
Ik wil nu de Fiber tabel aan de Line tabel hangen met LineName als key.

Nu heb ik een aantal dingen geprobeerd, na het lezen van de sql alchemy join documentatie (http://www.sqlalchemy.org....html#querying-with-joins). Echter wil ik omwille van de leesbaarheid van de code de uitkomst van de join opslaan als losse variabele, zoals dit dus:


code:
1
query = models.Session.query(models.Line).join(Fiber, Line,Fiber.LineName==Line.LineName).order_by(Line.LineName)

Of
code:
1
query = models.Session.query(models.Line).join(Fiber, Line, 'LineName').order_by(Line.LineName)


Allereerst vind ik het jammer dat dit niet kan. Ik heb onder de bovenstaande regels een if constructie, waar ik gewoon aan query wil refereren als zijnde een reeds-gejoind-object. Imo is het een beetje onzinnig om de join en de order_by in alle 3 (of mischien wel 10 ;)) de branches van die functie op te nemen. De aanzet hiervoor maak ik dus in de bovenste code snippet, daar wil ik de query-variabele assigment vervangen door een query-variabele met die join er al in.
offtopic:
Het klopt dat hier objecten geoutput worden ipv strings oid, maar daar dat lost mijn mako template op.



Kan iemand me vertellen hoe ik dit doe?

i3 + moederbord + geheugen kopen?


  • Boudewijn
  • Registratie: Februari 2004
  • Niet online

Boudewijn

omdat het kan

Topicstarter
Schopperdeschop.

i3 + moederbord + geheugen kopen?


  • Daspeed
  • Registratie: Maart 2001
  • Laatst online: 13:39
Euh ik kan alleen een beetje Python en heb verder geen ervaring met SQLAlchemy, dus ik zie niet meteen wat't probleem zou kunnen zijn. Maar misschien kun je even nader toelichten wat er precies niet lukt: parsed de code niet? krijg je een runtime error? of krijg je niet het verwachte resultaat?

  • Boudewijn
  • Registratie: Februari 2004
  • Niet online

Boudewijn

omdat het kan

Topicstarter
Runtime....
code:
1
&#8669; |ArgumentError: Object '<sqlalchemy.sql.expression._BinaryExpression object at 0xb91df4c>' is not a Selectable and does not implement `__selectable__()`|

resp.

code:
1
&#8669; |AttributeError: 'ColumnProperty' object has no attribute 'mapper'|

Hier lijkt de join geheel niet te zijn gebeurd... Bij de bovenste weet ik niet exact wat er fout gaat.


De clue zit volgens mij wel in het feit dat ik 2 tabellen join en die gejoinde tabel als variabele wil gebruiken.
Dit omdat ik geen zin heb om 15x die join code (namelijk in alle branches van de code eronder) neer te zetten.

[ Voor 24% gewijzigd door Boudewijn op 18-01-2009 14:59 ]

i3 + moederbord + geheugen kopen?


  • Daspeed
  • Registratie: Maart 2001
  • Laatst online: 13:39
als ik jouw documentatielink zo eens bekijk, zie ik geen derde argument in de join-functie genoemd, en dat lijkt me aardig corresponderen met de foutmelding (op een filter kun je niet selecteren). Is het wel de bedoeling dat dat op die manier doet?

  • Boudewijn
  • Registratie: Februari 2004
  • Niet online

Boudewijn

omdat het kan

Topicstarter
Ja ik heb het ook met een 3e argument geprobeerd.

Dat zie je trouwens ook in deze snippet uit de startpost:
code:
1
query = models.Session.query(models.Line).join(Fiber, Line, 'LineName').order_by(Line.LineName)


Ook Fiber.LineName == Line.LineName gedaan trouwens.

i3 + moederbord + geheugen kopen?


  • Daspeed
  • Registratie: Maart 2001
  • Laatst online: 13:39
ik bedoelde eigenlijk: haal het 3e argument eens weg, volgens mij hoort dat in de filter-functie thuis
join() knows how to join between User and Address because there’s only one foreign key between them. If there were no foreign keys, or several, join() would require a third argument indicating the ON clause of the join

[ Voor 56% gewijzigd door Daspeed op 18-01-2009 15:32 . Reden: kzie net dat zoiets wel mogelijk moet zijn, maar ben nog steeds wel benieuwd of't zonder wel werkt ]


  • Boudewijn
  • Registratie: Februari 2004
  • Niet online

Boudewijn

omdat het kan

Topicstarter
Ja ook zonder het 3e argument werkt het dus niet goed. Ik voer de code even opnieuw uit, en ook zonder de order_by call:


code:
1
                query = models.Session.query(models.Line).join(Fiber, Line, Line.LineName==Fiber.LineName)

Error in runtime:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Module sqlalchemy.orm.query:875 in __join          view
<<                  onclause = prop
               
                clause = orm_join(clause, right_entity, onclause, isouter=outerjoin)
                if alias_criterion:
                    self._filter_aliases = ORMAdapter(right_entity,
>>  clause = orm_join(clause, right_entity, onclause, isouter=outerjoin)
Module sqlalchemy.orm.util:364 in join         view
<<  def join(left, right, onclause=None, isouter=False):
        return _ORMJoin(left, right, onclause, isouter)
   
    def outerjoin(left, right, onclause=None):
>>  return _ORMJoin(left, right, onclause, isouter)
Module sqlalchemy.orm.util:355 in __init__         view
<<                  self._target_adapter = target_adapter
   
            expression.Join.__init__(self, left, right, onclause, isouter)
   
        def join(self, right, onclause=None, isouter=False):
>>  expression.Join.__init__(self, left, right, onclause, isouter)
Module sqlalchemy.sql.expression:2294 in __init__         view
<<      def __init__(self, left, right, onclause=None, isouter=False):
            self.left = _selectable(left)
            self.right = _selectable(right).self_group()
   
            if onclause is None:
>>  self.right = _selectable(right).self_group()
Module sqlalchemy.sql.expression:939 in _selectable         view
<<          return element
        else:
            raise exc.ArgumentError("Object '%s' is not a Selectable and does not implement `__selectable__()`" % repr(element))
   
    def is_column(col):
>>  raise exc.ArgumentError("Object '%s' is not a Selectable and does not implement `__selectable__()`" % repr(element))
ArgumentError: Object '<sqlalchemy.sql.expression._BinaryExpression object at 0xc32b0ec>' is not a Selectable and does not implement `__selectable__()`


De ORM lijkt dus over de join te struikelen, omdat eea niet Selectable is.

Zonder het 3e argument krijgen we een error die ik nog niet had gezien :D :
code:
1
ProgrammingError: (ProgrammingError) table name "Line" specified more than once 'SELECT "Line"."LineName" AS "Line_LineName", "Line"."ProviderName" AS "Line_ProviderName", "Line"."Length" AS "Line_Length", "Line"."Type" AS "Line_Type", "Line"."MeasurementReport" AS "Line_MeasurementReport", "Line"."Comment" AS "Line_Comment", "Line"."LineDiscarded" AS "Line_LineDiscarded", "Line"."LineNamesReplacingThisLine" AS "Line_LineNamesReplacingThisLine", "Line"."EndOfContractDate" AS "Line_EndOfContractDate", "Line"."LineNameSURFnet" AS "Line_LineNameSURFnet", "Line"."SiteLocationCodeA" AS "Line_SiteLocationCodeA", "Line"."SiteLocationCodeB" AS "Line_SiteLocationCodeB", "Line".last_updated AS "Line_last_updated", "Line".last_updated_by AS "Line_last_updated_by", "Line"."GeoObjectID" AS "Line_GeoObjectID", "Line"."importedFromWKT" AS "Line_importedFromWKT", "Line"."thirdpartyName" AS "Line_thirdpartyName", "Line"."GeoData" AS "Line_GeoData" \nFROM "Line" JOIN "Fiber" ON "Line"."LineName" = "Fiber"."LineName" JOIN "Line" ON "Line"."LineName" = "Fiber"."LineName" \nWHERE "Line"."LineDiscarded" = %(LineDiscarded_1)s AND ("Line"."SiteLocationCodeA" = %(SiteLocationCodeA_1)s AND "Line"."SiteLocationCodeB" = %(SiteLocationCodeB_1)s OR "Line"."SiteLocationCodeA" = %(SiteLocationCodeA_2)s AND "Line"."SiteLocationCodeB" = %(SiteLocationCodeB_2)s)' {'SiteLocationCodeB_1': 'Asd002A', 'LineDiscarded_1': '0', 'SiteLocationCodeB_2': 'Asd001A', 'SiteLocationCodeA_2': 'Asd002A', 'SiteLocationCodeA_1': 'Asd001A'}


ik zoek inderdaad op alle lines tussen 'Asd001A' en 'Asd002A', en dan wil ik alle Lines selecteren waarvan de bijbehorende Fiber InUse==true heeft (of juist False).

Ik ga zo eerst eens eventjes die error goed lezen.

i3 + moederbord + geheugen kopen?


  • Daspeed
  • Registratie: Maart 2001
  • Laatst online: 13:39
Misschien hoef je in de join niet opnieuw Line op te nemen aangezien je je query daar al op baseert
code:
1
session.query(User).join((Address, User.addresses))


Verder valt het me op dat je zowel models.Line als Line gebruikt, kweet niet of dat nog uitmaakt?

  • Boudewijn
  • Registratie: Februari 2004
  • Niet online

Boudewijn

omdat het kan

Topicstarter
Ja ik ben gisteren nog ongelofelijk suf aan het klooien geweest om het werkend te krijgen.
De originele (werkende code, maar dan zonder join) gebruikt ook models.Line en Line door elkaar.

Line moet er wel in vrees ik:
code:
1
&#8669;  NameError: global name 'LineName' is not defined
.

i3 + moederbord + geheugen kopen?


  • Daspeed
  • Registratie: Maart 2001
  • Laatst online: 13:39
code:
1
session.query(models.Line).select_from(join(models.Line, models.Fiber))

zoiets dan?

  • Boudewijn
  • Registratie: Februari 2004
  • Niet online

Boudewijn

omdat het kan

Topicstarter
Ook niet.
code:
1
&#8669;  TypeError: 'module' object is not callable


Ik kan nu wel in elke query die join uit gaan voeren, maar dat is mi ook best wel een bad practice :/.

i3 + moederbord + geheugen kopen?


  • Boudewijn
  • Registratie: Februari 2004
  • Niet online

Boudewijn

omdat het kan

Topicstarter
Iemand nog een idee van wat het type is van de variabele die onstaat uit die join-query?
Mischien dat ik er van daar uit verder mee kan prutsen.

i3 + moederbord + geheugen kopen?


  • Daspeed
  • Registratie: Maart 2001
  • Laatst online: 13:39
kun je niet het type opvragen van een succesvolle join?

Python:
1
2
3
4
5
class Pizza(object):
    pass

food = Pizza()
print type(food) # output: <class '__main__.Pizza'>
Pagina: 1