Search code examples
pythondatabasepython-3.xpeewee

How to get values from `ForeignKeyField` using peewee in join query?


I have models like this (peewee 2.10):

class LegalAct(Model):
    start_date = DateField()
    expiration_date = DateField()

class SomeModel(Model):
    name = CharField()
    legal_act = ForeignKeyField(LegalAct)

class OtherModel(Model):
    name = Charfield()
    some_model = ForeignKeyField(SomeModel) 
    starting_legal_act = ForeignKeyField(LegalAct)
    closing_legal_act = ForeignKeyField(LegalAct)

class ExtraModel(Model):
    value = IntegerField()
    other_model = ForeignKeyField(OtherModel) 
    starting_legal_act = ForeignKeyField(LegalAct)
    closing_legal_act = ForeignKeyField(LegalAct)

Given list of SomeModle.ids and a date (to filter LegalAct) I want to get following data:

SomeModel.name
OtherModel.name
OtherModel.starting_legal_act.start_date
ExtraModel.starting_legal_act.start_date
ExtraModel.value

The issue is that I'm can't figure out how to traverse to OtherModel.starting_legal_act.start_date and ExtraModel.starting_legal_act.start_date - I only able to get id of the corresponding models and fetch the data in consequence queries.

My current code is like this:

other_model_legal_act = get_other_legal_act(date)  # a query
extra_model_legal_act = get_extra_legal_act(date)  # a query

data = OtherModel.select(
    SomeModel.name
    OtherModel.name
    OtherModel.starting_legal_act.alias('date_1')  # I get `id`, but want date
    ExtraModel.starting_legal_act.alias('date_2')  # I get `id`, but want date
    ExtraModel.value
).join(SomeModel).switch(OtherModel).join(ExtraModel).where(
    (OtherModel.legal_act == other_model_legal_act) &
    (ExtraModel.legal_act == extra_model_legal_act) &
    (SomeModel.id.in_(id_list))

)

I need to replace these lines with code that will return actual dates instead of record ids:

OtherModel.starting_legal_act.alias('date_1')  # I get `id`, but want date
ExtraModel.starting_legal_act.alias('date_2')  # I get `id`, but want date

Solution

  • You want model aliases to reference the LegalAct multiple times:

    LegalAct1 = LegalAct.alias()
    LegalAct2 = LegalAct.alias()
    
    data = OtherModel.select(
        SomeModel.name
        OtherModel.name
        LegalAct1.start_date.alias('date_1'),
        LegalAct2.start_date.alias('date_2'),
        OtherModel.starting_legal_act.alias('date_1')  # I get `id`, but want date
        ExtraModel.starting_legal_act.alias('date_2')  # I get `id`, but want date
        ExtraModel.value
    )
    .join(LegalAct1, on=(OtherModel.starting_legal_act == LegalAct.id))
    .switch(OtherModel)
    # 
    

    etc.

    In the future...please try to make your models easier to reason about. "SomeModel" and "OtherModel" are absolutely useless.