Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: action for multiple items on show_cascade view #2219

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27,607 changes: 27,607 additions & 0 deletions examples/issue_2054/NAMES.DIC

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions examples/issue_2054/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Quick How to Example
--------------------

Simple contacts application.

Insert test data::

$ python testdata.py

Run it::

$ export FLASK_APP=app/__init__.py
$ flask fab create-admin
$ flask run

14 changes: 14 additions & 0 deletions examples/issue_2054/app/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import logging

from flask import Flask
from flask_appbuilder import AppBuilder, SQLA

logging.basicConfig(format="%(asctime)s:%(levelname)s:%(name)s:%(message)s")
logging.getLogger().setLevel(logging.DEBUG)

app = Flask(__name__)
app.config.from_object("config")
db = SQLA(app)
appbuilder = AppBuilder(app, db.session)

from . import models, views # noqa
47 changes: 47 additions & 0 deletions examples/issue_2054/app/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import datetime

from flask_appbuilder import Model
from sqlalchemy import Column, Date, ForeignKey, Integer, String
from sqlalchemy.orm import relationship

mindate = datetime.date(datetime.MINYEAR, 1, 1)


class ContactGroup(Model):
id = Column(Integer, primary_key=True)
name = Column(String(50), unique=True, nullable=False)

def __repr__(self):
return self.name


class Gender(Model):
id = Column(Integer, primary_key=True)
name = Column(String(50), unique=True, nullable=False)

def __repr__(self):
return self.name


class Contact(Model):
id = Column(Integer, primary_key=True)
name = Column(String(150), unique=True, nullable=False)
address = Column(String(564))
birthday = Column(Date, nullable=True)
personal_phone = Column(String(20))
personal_celphone = Column(String(20))
contact_group_id = Column(Integer, ForeignKey("contact_group.id"), nullable=False)
contact_group = relationship("ContactGroup")
gender_id = Column(Integer, ForeignKey("gender.id"), nullable=False)
gender = relationship("Gender")

def __repr__(self):
return self.name

def month_year(self):
date = self.birthday or mindate
return datetime.datetime(date.year, date.month, 1) or mindate

def year(self):
date = self.birthday or mindate
return datetime.datetime(date.year, 1, 1)
Binary file not shown.
27 changes: 27 additions & 0 deletions examples/issue_2054/app/translations/pt/LC_MESSAGES/messages.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Portuguese translations for PROJECT.
# Copyright (C) 2014 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2014.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2014-01-13 00:29+0000\n"
"PO-Revision-Date: 2014-01-13 00:18+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: pt <[email protected]>\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 1.3\n"

#: app/views.py:42
msgid "List Groups"
msgstr "Lista de Grupos"

#: app/views.py:43
msgid "List Contacts"
msgstr ""

148 changes: 148 additions & 0 deletions examples/issue_2054/app/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import calendar

from flask import redirect
from flask_appbuilder import ModelView
from flask_appbuilder.actions import action
from flask_appbuilder.charts.views import GroupByChartView
from flask_appbuilder.models.group import aggregate_count
from flask_appbuilder.models.sqla.interface import SQLAInterface

from . import appbuilder, db
from .models import Contact, ContactGroup, Gender


def fill_gender():
try:
db.session.add(Gender(name="Male"))
db.session.add(Gender(name="Female"))
db.session.commit()
except Exception:
db.session.rollback()


class ContactModelView(ModelView):
datamodel = SQLAInterface(Contact)

list_columns = ["name", "personal_celphone", "birthday", "contact_group.name"]

base_order = ("name", "asc")
show_fieldsets = [
("Summary", {"fields": ["name", "gender", "contact_group"]}),
(
"Personal Info",
{
"fields": [
"address",
"birthday",
"personal_phone",
"personal_celphone",
],
"expanded": False,
},
),
]

add_fieldsets = [
("Summary", {"fields": ["name", "gender", "contact_group"]}),
(
"Personal Info",
{
"fields": [
"address",
"birthday",
"personal_phone",
"personal_celphone",
],
"expanded": False,
},
),
]

edit_fieldsets = [
("Summary", {"fields": ["name", "gender", "contact_group"]}),
(
"Personal Info",
{
"fields": [
"address",
"birthday",
"personal_phone",
"personal_celphone",
],
"expanded": False,
},
),
]

@action("muldelete", "Delete", "Delete all Really?", "fa-rocket")
def muldelete(self, items):
if isinstance(items, list):
self.datamodel.delete_all(items)
self.update_redirect()
else:
self.datamodel.delete(items)
return redirect(self.get_redirect())


class GroupModelView(ModelView):
datamodel = SQLAInterface(ContactGroup)
related_views = [ContactModelView]
show_template = "appbuilder/general/model/show_cascade.html"

@action("muldelete", "Delete", "Delete all Really?", "fa-rocket")
def muldelete(self, items):
if isinstance(items, list):
self.datamodel.delete_all(items)
self.update_redirect()
else:
self.datamodel.delete(items)
return redirect(self.get_redirect())


def pretty_month_year(value):
return calendar.month_name[value.month] + " " + str(value.year)


def pretty_year(value):
return str(value.year)


class ContactTimeChartView(GroupByChartView):
datamodel = SQLAInterface(Contact)

chart_title = "Grouped Birth contacts"
chart_type = "AreaChart"
label_columns = ContactModelView.label_columns
definitions = [
{
"group": "month_year",
"formatter": pretty_month_year,
"series": [(aggregate_count, "group")],
},
{
"group": "year",
"formatter": pretty_year,
"series": [(aggregate_count, "group")],
},
]


db.create_all()
fill_gender()
appbuilder.add_view(
GroupModelView,
"List Groups",
icon="fa-folder-open-o",
category="Contacts",
category_icon="fa-envelope",
)
appbuilder.add_view(
ContactModelView, "List Contacts", icon="fa-envelope", category="Contacts"
)
appbuilder.add_separator("Contacts")
appbuilder.add_view(
ContactTimeChartView,
"Contacts Birth Chart",
icon="fa-dashboard",
category="Contacts",
)
3 changes: 3 additions & 0 deletions examples/issue_2054/babel/babel.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[python: **.py]
[jinja2: **/templates/**.html]
encoding = utf-8
84 changes: 84 additions & 0 deletions examples/issue_2054/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import os

from flask_appbuilder.const import AUTH_DB
from flask_appbuilder.exceptions import PasswordComplexityValidationError

basedir = os.path.abspath(os.path.dirname(__file__))

CSRF_ENABLED = True
SECRET_KEY = "\2\1thisismyscretkey\1\2\e\y\y\h"

OPENID_PROVIDERS = [
{"name": "Google", "url": "https://www.google.com/accounts/o8/id"},
{"name": "Yahoo", "url": "https://me.yahoo.com"},
{"name": "AOL", "url": "http://openid.aol.com/<username>"},
{"name": "Flickr", "url": "http://www.flickr.com/<username>"},
{"name": "MyOpenID", "url": "https://www.myopenid.com"},
]

SQLALCHEMY_DATABASE_URI = "sqlite:///" + os.path.join(basedir, "app.db")
# SQLALCHEMY_DATABASE_URI = 'mysql://username:[email protected]/quickhowto'
# SQLALCHEMY_DATABASE_URI = 'postgresql://scott:tiger@localhost:5432/myapp'
# SQLALCHEMY_ECHO = True
SQLALCHEMY_POOL_RECYCLE = 3

BABEL_DEFAULT_LOCALE = "en"
BABEL_DEFAULT_FOLDER = "translations"
LANGUAGES = {
"en": {"flag": "gb", "name": "English"},
"pt": {"flag": "pt", "name": "Portuguese"},
"pt_BR": {"flag": "br", "name": "Pt Brazil"},
"es": {"flag": "es", "name": "Spanish"},
"fr": {"flag": "fr", "name": "French"},
"de": {"flag": "de", "name": "German"},
"zh": {"flag": "cn", "name": "Chinese"},
"ru": {"flag": "ru", "name": "Russian"},
"pl": {"flag": "pl", "name": "Polish"},
"el": {"flag": "gr", "name": "Greek"},
"ja_JP": {"flag": "jp", "name": "Japanese"},
}

FAB_API_MAX_PAGE_SIZE = 100


def custom_password_validator(password: str) -> None:
"""
A simplistic example for a password validator
"""
if len(password) < 8:
raise PasswordComplexityValidationError("Must have at least 8 characters")


# FAB_PASSWORD_COMPLEXITY_VALIDATOR = custom_password_validator

FAB_PASSWORD_COMPLEXITY_ENABLED = True

# ------------------------------
# GLOBALS FOR GENERAL APP's
# ------------------------------
UPLOAD_FOLDER = basedir + "/app/static/uploads/"
IMG_UPLOAD_FOLDER = basedir + "/app/static/uploads/"
IMG_UPLOAD_URL = "/static/uploads/"
AUTH_TYPE = AUTH_DB
# AUTH_LDAP_SERVER = "ldap://dc.domain.net"
AUTH_ROLE_ADMIN = "Admin"
AUTH_ROLE_PUBLIC = "Public"
APP_NAME = "F.A.B. Example"
APP_THEME = "" # default
# APP_THEME = "cerulean.css" # COOL
# APP_THEME = "amelia.css"
# APP_THEME = "cosmo.css"
# APP_THEME = "cyborg.css" # COOL
# APP_THEME = "flatly.css"
# APP_THEME = "journal.css"
# APP_THEME = "readable.css"
# APP_THEME = "simplex.css"
# APP_THEME = "slate.css" # COOL
# APP_THEME = "spacelab.css" # NICE
# APP_THEME = "united.css"
# APP_THEME = "darkly.css"
# APP_THEME = "lumen.css"
# APP_THEME = "paper.css"
# APP_THEME = "sandstone.css"
# APP_THEME = "solar.css"
# APP_THEME = "superhero.css"