烧瓶管理员点击后编辑子对象

时间:2017-03-22 21:22:22

标签: python relationship flask-admin

Flask-Admin在标准编辑视图中显示由关系定义的子对象。例如,如果User个对象有Address个孩子,则查看User的修改视图会在相应字段中显示Address个孩子。然后,用户可以删除该对象,或添加另一个对象。

我希望用户能够点击,或者能够进入子对象的编辑视图。在我所描述的示例中,用户应该能够直接从Address对象的编辑视图访问User对象的编辑视图。

我发现的唯一相关内容是inline_models,但这不是解决方案。实现非常脆弱(例如,它无法处理长距离关系)。 Flask-Admin知道子对象!我可以在视图中看到它们!我只是希望他们成为他们自己的编辑视图的链接...

任何人都知道如何完成此操作或链接到示例?

1 个答案:

答案 0 :(得分:4)

以下是在编辑视图中将链接放置到另一个模型的编辑视图的单个文件简单示例。它可能对你有所帮助。

enter image description here

我使用了用户 - 地址关系,用户有地址,地址可以有很多用户。

我已使用Faker生成示例数据,因此您需要pip install faker进入您的环境。

我们的想法是使用Flask-Admin form rules,在这种情况下,我要配置form_edit_rules

我创建了两个自定义规则:

Link,继承BaseRule。构造函数有三个值;端点,要在Flask url_for方法中与端点一起传递的属性的名称,最后是要显示为链接的文本。在此示例中,端点为'address.edit_view',因为这是我们要链接到的视图。

MultiLink,与Link类似,接受它与关系一起使用。

这里是代码(有一点错误检查):

from random import randint
from flask import Flask, url_for
from flask_admin.contrib import sqla
from flask_admin import Admin
from flask_admin.form.rules import BaseRule
from faker import Faker
from flask_sqlalchemy import SQLAlchemy
from markupsafe import Markup
from sqlalchemy import func, select
from sqlalchemy.ext.hybrid import hybrid_property

fake = Faker()

# 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:///:memory:'
# 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 Address(db.Model):

    __tablename__ = 'addresses'

    id = db.Column(db.Integer, primary_key=True)
    number = db.Column(db.String(255))
    street = db.Column(db.String(255))
    city = db.Column(db.String(255))
    country = db.Column(db.String(255))

    @hybrid_property
    def user_count(self):
        return len(self.users)

    @user_count.expression
    def user_count(cls):
        return select([func.count(User.id)]).where(User.address_id == cls.id).label("user_count")

    def __unicode__(self):
        return ', '.join(filter(None, [self.number, self.street, self.city, self.country]))


class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    first_name = db.Column(db.String(255))
    last_name = db.Column(db.String(255))
    email = db.Column(db.String(254))

    address_id = db.Column(db.Integer, db.ForeignKey('addresses.id'), index=True)
    address = db.relationship(Address, backref=db.backref('users'))

    def __str__(self):
        return unicode(self).encode('utf-8')

    def __unicode__(self):
        return '{} {}'.format(self.first_name, self.last_name)


class Link(BaseRule):
    def __init__(self, endpoint, attribute, text):
        super(Link, self).__init__()
        self.endpoint = endpoint
        self.text = text
        self.attribute = attribute

    def __call__(self, form, form_opts=None, field_args={}):

        _id = getattr(form._obj, self.attribute, None)

        if _id:
            return Markup('<a href="{url}">{text}</a>'.format(url=url_for(self.endpoint, id=_id), text=self.text))


class MultiLink(BaseRule):
    def __init__(self, endpoint, relation, attribute):
        super(MultiLink, self).__init__()
        self.endpoint = endpoint
        self.relation = relation
        self.attribute = attribute

    def __call__(self, form, form_opts=None, field_args={}):

        _hrefs = []
        _objects = getattr(form._obj, self.relation)
        for _obj in _objects:
            _id = getattr(_obj, self.attribute, None)
            _link = '<a href="{url}">Edit {text}</a>'.format(url=url_for(self.endpoint, id=_id), text=str(_obj))
            _hrefs.append(_link)

        return Markup('<br>'.join(_hrefs))


class UserAdmin(sqla.ModelView):
    can_view_details = True

    form_edit_rules = (
        'first_name',
        'last_name',
        'email',
        'address',
        Link(endpoint='address.edit_view', attribute='address_id', text='Edit Address')
    )


class AddressAdmin(sqla.ModelView):
    can_view_details = True

    column_list = ['number', 'street', 'city', 'country', 'user_count', 'users']

    form_edit_rules = (
        'number',
        'street',
        'city',
        'country',
        'users',
        MultiLink(endpoint='user.edit_view', relation='users', attribute='id')
    )


admin = Admin(app, template_mode="bootstrap3")
admin.add_view(UserAdmin(User, db.session))
admin.add_view(AddressAdmin(Address, db.session))


def build_db():
    db.drop_all()
    db.create_all()

    for _ in range(0, 20):
        _users = []
        for _ in range(0, randint(1, 10)):
            _user = User(
                first_name=fake.first_name(),
                last_name=fake.last_name(),
                email=fake.safe_email(),
            )
            _users.append(_user)

        _address = Address(
            number=fake.random_digit_not_null(),
            street=fake.secondary_address(),
            city=fake.city(),
            country=fake.country(),
            users = _users
        )

        db.session.add(_address)

    db.session.commit()


@app.before_first_request
def first_request():
    build_db()


if __name__ == '__main__':
    app.run(port=5000, debug=True)