如何在 Flask/SQLAlchemy 中显示包含多对多查询结果的列

问题描述 投票:0回答:4

我正在尝试通过构建一个简单的 Wiki 来学习 Python/Flask/SQLAlchemy(很大程度上基于 Flask-Admin 示例),但我很难理解如何从多对多关系中获取新列来显示.

我已经成功创建了 Wiki,并为标签创建了一个多对多关系表,没有任何问题(据我所知,标签工作正常),但我想将标签显示为一列,但不能弄清楚逻辑。

目标:我想显示一列,其中显示多对多关联表引用的标签。

这是我想要实现的目标的图片:

A table with a column named Tag(s) with two rows.  Row #1 contains the text

这是我认为相关的代码:

wiki_tags_table = db.Table('wiki_tags', db.Model.metadata,
                           db.Column('wiki_id', db.Integer, db.ForeignKey('wiki.id')),
                           db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'))
                           )

class Wiki(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), unique=True)
    description = db.Column(db.Text)
    path = db.Column(db.Unicode(256))
    date_added = db.Column(db.DateTime)
    tags_id = db.Column(db.Integer, db.ForeignKey('tag.id'))
    tags = db.relationship('Tag', secondary=wiki_tags_table, backref=db.backref('wiki_tags_table', lazy='dynamic'))

    def __unicode__(self):
        return self.item

class WikiAdmin(sqla.ModelView):

    column_exclude_list = ['path']

    column_hide_backrefs = False

    form_overrides = {
        'path': form.FileUploadField
    }

    form_args = {
        'path': {
            'label': 'File',
            'base_path': file_path
        }
    }

    column_searchable_list = ('title', 'description', 'path')

    def __init__(self, session):
        super(WikiAdmin, self).__init__(Wiki, session)

class Tag(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode(64))

    def __unicode__(self):
        return self.name

我一直在参考这些文档(主要是尝试 backref 的变体),但还没有弄清楚:

python flask flask-sqlalchemy flask-admin
4个回答
1
投票

不确定这是否有帮助,因为我正在学习自己。但我有一个类似的问题,我想显示“外键表”中的一列,并这样做:

我的模型.py

  from app import db

class Member(db.Model):
    __tablename__ = 'members'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(64), index=True)
    phone = db.Column(db.String(10), index=True)
    email = db.Column(db.String(120), index=True, unique=True)
    grade = db.relationship('Grade', backref='member')
    attendance = db.relationship('Attendance', backref='member')

    def __repr__(self):
        return '<User %r>' % self.name


class Grade(db.Model):
    __tablename__ = 'grades'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    member_id = db.Column(db.Integer, db.ForeignKey('members.id'))
    grade = db.Column(db.String(10))
    grade_date = db.Column(db.Date)

    def __repr__(self):
        return '<Grading %r>' % self.id

    def __str__(self):
        return self.grade

    def __unicode__(self):
        return self.grade


class Attendance(db.Model):
    __tablename__ = 'attendance'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    id_member = db.Column(db.Integer, db.ForeignKey('members.id'))
    attend_date = db.Column(db.Date)

    def __repr__(self):
        return '<Attenance %r>' % self.id

我的观点.py

from app.models import Member, Grade, Attendance
from app import app, admin, db
from flask_admin import BaseView, expose
from flask_admin.contrib.fileadmin import FileAdmin
from flask_admin.contrib.sqla import ModelView
import os.path as op


class AdminView(ModelView):
    column_display_pk = True  # optional, but I like to see the IDs in the list
    column_hide_backrefs = False
    # column_list = ('id', 'name', 'parent')
    create_modal = True
    edit_modal = True


class MemberAdmin(ModelView):
    column_display_pk = True  # optional, but I like to see the IDs in the list
    column_hide_backrefs = False
    can_view_details = True
    create_modal = True
    edit_modal = True
    form_columns = ['name', 'phone', 'email', 'grade', 'attendance']
    column_details_list = ['name', 'phone', 'email', 'grade', 'attendance']
    column_searchable_list = ['name', 'email']
    column_list = ('id', 'name', 'phone','email','grade')


class GradeAdmin(ModelView):
    column_display_pk = True  # optional, but I like to see the IDs in the list
    column_hide_backrefs = False
    column_list = ('id', 'member', 'grade', 'grade_date')
    form_choices = {'grade': [('Beginner', 'Beginner'), ('Yellow', 'Yellow'), ('Orange', 'Orange'),
                              ('Green 1', 'Green 1'), ('Green 2', 'Green 2'), ('Blue 1', 'Blue 1'),
                              ('Blue 2', 'Blue 2'), ('Purple 1', 'Purple 1'), ('Purple 2', 'Purple 2'),
                              ('Brown 1', 'Brown 1'), ('Brown 2', 'Brown 2'), ('Red 1', 'Red 1')]}


admin.add_view(MemberAdmin(Member, db.session, endpoint='member', category='Admin'))
admin.add_view(GradeAdmin(Grade, db.session, endpoint='grades', category='Admin'))
admin.add_view(ModelView(Attendance, db.session, endpoint='attendance', category='Admin'))

因为我真的不知道我在做什么(还),我认为让我看到我的会员模型的列以及添加来自成绩模型的额外列的是类 MemberAdmin(ModelView) 中的这些行:

column_hide_backrefs = False
can_view_details = True
...
form_columns = ['name', 'phone', 'email', 'grade', 'attendance']

0
投票

关系属性将相关表加载为对象列表,因此您应该能够在视图中打印所有 wiki 项目和所有关联标签,如下所示:

for wiki_item in Wiki.query.all():
    print wiki_item.title
    for tag in wiki_item.tags:
        print tag.name 

如果你尝试这样做会发生什么?


0
投票

我现在需要实现相同的目标,对我有用的只是在 ViewModel 类中添加 column_list 字段,所以在这种情况下,那就是

class WikiAdmin(sqla.ModelView):

    ...
    column_list = ('title', 'description', 'dataadded', 'tags')

这是一个老问题,但这是我在尝试自己弄清楚如何做时偶然发现的第一个线程(并且可以说是唯一相关的),所以也许这个答案会节省某人的时间


0
投票

尝试像我所做的那样,使用用于多对多字段的特殊列格式化程序。这对我有用。


def many_to_many_formatter(view, context, model, name: str) -> str:
    """This function formats the many-to-many fields for display in the list view"""
    try:
        rel_field, col_name = name.split(".")
        list_of_tags: list = getattr(model, rel_field)
        list_of_results: list = [str(getattr(tag, col_name)) for tag in list_of_tags]
        new_str: str = ", ".join(list_of_results)
    except Exception:
        current_app.logger.exception("Error trying to format many-to-many relationship field in list view!")
        new_str = ""

    return new_str


class SpecialView(ModelView):
    """Flask-Admin view"""

    column_list = (
        "model",
        "description",
        # many-to-many field
        "parts_rel.part_num"
    )

    column_formatters = {
        "parts_rel.part_num": many_to_many_formatter,
    }
© www.soinside.com 2019 - 2024. All rights reserved.