【问题标题】:flask admin edit child objects on click烧瓶管理员单击时编辑子对象
【发布时间】:2017-03-22 21:22:21
【问题描述】:

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

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

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

有人知道如何完成此操作或可以链接到示例吗?

【问题讨论】:

    标签: python relationship flask-admin


    【解决方案1】:

    这是一个在编辑视图中放置指向另一个模型的编辑视图的链接的单个文件简单示例。它可能对你有帮助,也可能没有。

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

    我已使用 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=None):
            if not field_args
                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=None):
            if not field_args
                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)
    

    【讨论】:

    • 你能更新这个来描述如何处理孩子的集合吗?例如,如果用户可以有多个地址?
    • @melchoir55 - 我添加了一个MultiLink 表单规则,适用于关系。
    • 首先,谢谢你,正是我需要的。但是在用户视图的地址下拉列表中,你是如何让它显示实际地址的,我的只是说
    • @brickfungus - 您是否为您的 Address 类定义了 __str__ 方法?
    • @pjcunningham 是的,但我拼写它 string 像个白痴...谢谢!现在工作
    猜你喜欢
    • 1970-01-01
    • 2020-08-11
    • 2014-04-23
    • 1970-01-01
    • 2018-04-15
    • 1970-01-01
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    相关资源
    最近更新 更多