Reputation: 23
I'm diving head-first into sqlalchemy ORM and am trying to understand relationships between objects.
I've created 3 classes that represent log files (Log), records parsed from the log files (LogRecord) and users (User) that each log record belong to.
Here's how I defined the classes:
from sqlalchemy import Table, Column, Integer, String, ForeignKey, create_engine
from sqlalchemy.orm import relationship, backref, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
import random
engine = create_engine('sqlite://')
# Create the session class
Session = sessionmaker(bind=engine)
# Create the session
session = Session()
Base = declarative_base()
class Log(Base):
__tablename__ = 'logs'
id = Column(Integer, primary_key=True, unique=True)
log_name = Column(String, unique=True)
records = relationship("LogRecord", backref="logs")
def __repr__(self):
return '<Log(log_name={:s}, id={:d}>'.format(
self.log_name, self.id)
class LogRecord(Base):
__tablename__ = 'log_records'
id = Column(Integer, primary_key=True, unique=True)
record_name = Column(String)
log_id = Column(Integer, ForeignKey('logs.id'))
user_id = Column(Integer, ForeignKey('user.id'))
user = relationship("User", backref="log_records")
def __repr__(self):
return '<LogRecord(record_name={:s}, id={:d}>'.format(
self.record_name,
self.id)
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True, unique=True)
name = Column(String, unique=True)
def __repr__(self):
return '<User(name={:s}, id={:d}>'.format(self.name, self.id)
And then populated them with some phony data:
Base.metadata.create_all(engine)
users = ['John', 'Alex', 'Nikki']
# Add 10 log files to Log
for x in range(0,10):
log = Log(log_name='log{:d}.txt'.format(x))
session.add(log)
# Add users
for user in users:
session.add(User(name=user))
# Select all users
users = session.query(User).all()
# Add log records
for log in session.query(Log):
# Assign a random user to each log record
user = users[int(random.random() * len(users))]
for x in range(0,10):
# Assign a random user to each log record
user = users[int(random.random() * len(users))]
r = LogRecord(record_name='{:s}-{:d}'.format(log.log_name, int(random.random()*100)),log_id=log.id,user=user)
session.add(r)
Each log file can contain records from one or more users. I'd like to get a distinct list of log files which contain at least one record from a specified user. I tried:
In [8]: user = session.query(User).filter(User.name == 'John').one()
In [9]: user
Out[9]: <User(name=John, id=1>
and then tried to find the logs using:
In [10]: session.query(Log).filter(Log.records.user == user).all()
But got the following:
session.query(Log).filter(Log.records.user == user).distinct().all()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-13-d6d323e178f8> in <module>()
----> 1 session.query(Log).filter(Log.records.user == user).distinct().all()
/Users/kerfoot/code/venvs/sqlalchemy/lib/python2.7/sitepackages/sqlalchemy/orm/attributes.pyc in __getattr__(self, key)
192 type(self.comparator).__name__,
193 self,
--> 194 key)
195 )
196
AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with Log.records has an attribute 'user'
Not sure if there's something wrong with my class defs or the query itself.
Any help much appreciated!
Upvotes: 2
Views: 106
Reputation: 77082
You cannot make filters on the properties of the properties, or simply put: you are not able to have more than one dot in the expression: Log.records.user
does not work.
Option-1: You can do this the same way you would do it in the SQL:
session.query(Log).join(LogRecord, Log.records).filter(LogRecord.user == user).all()
and sqlalchemy
is smart enough to return you only the list of Log
instances without any duplication (even though SQL statement might result in repetition).
Option-2: Another way is to use any(...)
, which is logically cleaner:
session.query(Log).filter(Log.records.any(LogRecord.user == user)).all()
Transcript:
-> get allLog
instances,
-> which contains at least oneLogRecord
,
-> that belongs to givenuser
.
Upvotes: 1