2016-03-24 2 views
2

Предположим, что у нас есть отношение между Владельцем и Экранами между собой (владелец может владеть несколькими экранами).Flask-Admin сортировать по одному во многих подсчитанных полях

Можно отобразить столбец, показывающий количество экранов, принадлежащих каждому владельцу, с помощью функции hybrid_property и call count() для отношения. Однако я не нашел способ сделать это значение, рассчитанное значение сортируется в веб-интерфейсе: Если добавить number_of_screens в column_sortable_list, я получил следующее сообщение об ошибке:

Traceback (most recent call last): 
    File "app.py", line 71, in <module> 
    admin.add_view(OwnerAdmin(Owner, db.session)) 
    File "C:\Python27\lib\site-packages\flask_admin\contrib\sqla\view.py", line 319, in __init__ 
    menu_icon_value=menu_icon_value) 
    File "C:\Python27\lib\site-packages\flask_admin\model\base.py", line 718, in __init__ 
    self._refresh_cache() 
    File "C:\Python27\lib\site-packages\flask_admin\model\base.py", line 795, in _refresh_cache 
    self._sortable_columns = self.get_sortable_columns() 
    File "C:\Python27\lib\site-packages\flask_admin\contrib\sqla\view.py", line 539, in get_sortable_columns 
    column, path = self._get_field_with_path(c) 
    File "C:\Python27\lib\site-packages\flask_admin\contrib\sqla\view.py", line 365, in _get_field_with_path 
    value = getattr(model, attribute) 
    File "C:\Python27\lib\site-packages\sqlalchemy\ext\hybrid.py", line 740, in __get__ 
    return self.expr(owner) 
    File "app.py", line 48, in number_of_screens 
    return self.screens.count() 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\attributes.py", line 193, in __getattr__ 
    key) 
AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with Owner.screens has an attribute 'count' 

Вот пример кода для иллюстрации проблемы :

from flask import Flask 
from flask_sqlalchemy import SQLAlchemy 
from sqlalchemy.ext.hybrid import hybrid_property 

import flask_admin as admin 
from flask_admin.contrib import sqla 
from flask_admin.contrib.sqla.filters import IntGreaterFilter 

# Create application 
app = Flask(__name__) 

# Create dummy secrey key so we can use sessions 
app.config['SECRET_KEY'] = '123456790' 

# Create in-memory database 
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///sample_db_2.sqlite' 
app.config['SQLALCHEMY_ECHO'] = True 
db = SQLAlchemy(app) 


# Flask views 
@app.route('/') 
def index(): 
    return '<a href="/admin/">Click me to get to Admin!</a>' 


class Screen(db.Model): 
    __tablename__ = 'screen' 
    id = db.Column(db.Integer, primary_key=True) 
    width = db.Column(db.Integer, nullable=False) 
    height = db.Column(db.Integer, nullable=False) 
    owner_id = db.Column(db.Integer, db.ForeignKey('owner.id')) 
    owner = db.relationship('Owner', 
     backref=db.backref('screens', lazy='dynamic')) 

    @hybrid_property 
    def number_of_pixels(self): 
     return self.width * self.height 


class Owner(db.Model): 
    __tablename__ = 'owner' 
    id = db.Column(db.Integer, primary_key=True) 
    name = db.Column(db.Unicode) 

    @hybrid_property 
    def number_of_screens(self): 
     return self.screens.count() 


class ScreenAdmin(sqla.ModelView): 
    ''' Flask-admin can not automatically find a hybrid_property yet. You will 
     need to manually define the column in list_view/filters/sorting/etc.''' 
    list_columns = ['id', 'width', 'height', 'number_of_pixels'] 
    column_sortable_list = ['id', 'width', 'height', 'number_of_pixels'] 

    # make sure the type of your filter matches your hybrid_property 
    column_filters = [IntGreaterFilter(Screen.number_of_pixels, 
             'Number of Pixels')] 

class OwnerAdmin(sqla.ModelView): 
    ''' Flask-admin can not automatically find a hybrid_property yet. You will 
     need to manually define the column in list_view/filters/sorting/etc.''' 
    list_columns = ['id', 'name', 'number_of_screens'] 
    column_sortable_list = ['id', 'name', 'number_of_screens'] 


# Create admin 
admin = admin.Admin(app, name='Example: SQLAlchemy2', template_mode='bootstrap3') 
admin.add_view(ScreenAdmin(Screen, db.session)) 
admin.add_view(OwnerAdmin(Owner, db.session)) 

if __name__ == '__main__': 

    # Create DB 
    db.create_all() 

    # Start app 
    app.run(debug=True) 

ответ

4

Вы должны добавить модификатор hybrid property expression для number_of_screens гибридной собственности. Это метод, который испускает SQL, необходимый для вычисления количества экранов для определенного владельца. например

class Owner(db.Model): 
    __tablename__ = 'owner' 
    id = db.Column(db.Integer, primary_key=True) 
    name = db.Column(db.Unicode) 

    @hybrid_property 
    def number_of_screens(self): 
     return len(self.screens) 

    @number_of_screens.expression 
    def number_of_screens(cls): 
     return db.select([db.func.count(Screen.id)]).where(Screen.owner_id == cls.id).label("number_of_screens") 
+0

Спасибо, это именно то, что я искал! – jbfuzier

+0

'TypeError: count() принимает ровно один аргумент (0 задано)' :( –

+0

Получил его для работы с 'return len (self.screens)' под '@ hybrid_property'. –