Как получить количество отдельных записей столбца пользователем в Flask-SQLAlchemy? - PullRequest
0 голосов
/ 03 мая 2019

У меня есть база пользовательских действий. Для конкретного пользователя я хочу вернуть количество отдельных типов действий, которые они выполнили. Например:

Боб:
Занятие 1: тип А
Деятельность 2: Тип B
Занятие 3: Тип А

Результат должен быть равен 2 из-за двух разных действий (A и B).

Я попробовал следующую команду:

Activity.activity_type.query.filter(Activity.user_id==1).distinct(Activity.activity_type) 

Часть Activity.activity_type выдает ошибку. Я смотрю на StackOverflow и документацию, но не смог найти, как правильно использовать distinct в этой ситуации. Буду признателен за помощь в том, как заставить этот запрос работать.

1 Ответ

1 голос
/ 03 мая 2019

Пожалуйста, извините за мое первоначальное толкование вашей цели, оно было неверным, так как возвращалось количество попыток каждого действия, а не количество попыток отдельных действий.

Ниже приведено решение поставленной проблемы.Илья Эвериля в комментариях.

Однако, исключение, которое вы получаете, происходит до того, как ваш запрос будет выполнен (я думаю, так как я пересмотрел ваши модели из запроса, который вы предоставили), так чтоОсновная часть этого ответа сосредоточена на следующем:

AttributeError: Ни объект «InstrumentedAttribute», ни объект «Comparator», связанный с Activity.activity_type, не имеют атрибута «query»

Я создал эти модели для выполнения вашего запроса:

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(16))

class Activity(db.Model):
    activity_id = db.Column(db.Integer, primary_key=True)
    activity_type = db.Column(db.String(32))
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    user = db.relationship('User')

Затем написал небольшой скрипт для добавления некоторых данных:

if __name__ == '__main__':
    with app.app_context():
        db.drop_all()
        db.create_all()
        names = ['batman', 'a', 'list', 'of', 'names']
        users = [User(name=name) for name in names]
        db.session.add_all(users)
        activities = [
            'Self Doubt',
            'Dark Introspection',
            'Gotham Needs Me',
            'Training Montage',
            'Fight Crime',
            'Get the girl'
        ]
        db.session.add_all(
            [Activity(
                activity_type=random.choice(activities),
                user=random.choice(users)
            ) for _ in range(20)]
        )

        db.session.commit()

Выполнение Activity.activity_type.query.filter(Activity.user_id==1).distinct(Activity.activity_type) вызывает вышеупомянутую ошибку.

Проблема связана с этой частью вашего запроса Activity.activity_type.query.Он говорит, что он искал атрибут с именем 'query' на Activity.activity_type, но не смог его найти.Немного разбив сообщение об ошибке:

Мы знаем, что Activity.activity_type является экземпляром Column, когда мы определяем его в модели, но print(Activity.activity_type) показывает <class 'sqlalchemy.orm.attributes.InstrumentedAttribute'>, так что это первый объект, на который ссылаютсяв ошибке.Comparitor - это внутренняя конструкция sqlalchemy, которая также проверяется (вы можете найти ее с помощью Activity.activity_type.comparator, если вам интересно).Поэтому sqlalchemy искал и не мог найти атрибут с именем 'query' в столбце Activity.activity_type.

Свойство .query происходит из базы db.Model, от которой наследуются ваши модели ибудет доступен только для объектов в том же MRO , что и db.Model.Поскольку объекты столбца не наследуются от db.Model, вы не можете получить доступ к свойству запроса через них.Activity.query законно, Activity.activity_type.query нет.

Это вопрос, который Илья предоставил в комментарии к моему первоначальному ответу, и это правильное решение:

        batman_activity_count = db.session.query(
            db.func.count(
                Activity.activity_type.distinct()
            )
        ).filter(Activity.user_id==1).scalar()

        print(batman_activity_count)  # random integer b/w 0 and 6.
...