Flask-Admin在其标准编辑视图中显示由关系定义的子对象。例如,如果User
对象有Address
子对象,则查看的编辑视图User
将Address
在适当的字段中显示子对象。然后,用户可以删除该对象,或添加另一个对象。
我希望用户能够单击或以其他方式进入子对象的编辑视图。在我描述的示例中,用户应该能够Address
直接从对象的编辑视图访问对象的编辑视图User
。
我发现的唯一相关内容是inline_models
,但这不是解决方案。该实现非常脆弱(例如,它不能处理长距离关系)。Flask-Admin知道子对象!我可以在视图中看到它们!我只希望他们成为他们自己的编辑视图的链接...
任何人都知道如何实现这一目标,或者可以链接到示例吗?
这是一个在编辑视图中放置指向另一个模型的编辑视图的链接的简单文件示例。它可能对您有帮助。
我已经使用了用户-地址关系,一个用户有一个地址,而地址可以有许多用户。
我已经使用Faker生成示例数据,因此您需要pip install faker
进入您的环境。
这个想法是使用Flask-Admin 表单规则,在这种情况下,我正在配置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 'Click me to get to Admin!' 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('{text}'.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 = 'Edit {text}'.format(url=url_for(self.endpoint, id=_id), text=str(_obj)) _hrefs.append(_link) return Markup('
'.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)