Skip to content

Commit

Permalink
fix action for multiple items on show_cascade view
Browse files Browse the repository at this point in the history
  • Loading branch information
ThomasP0815 committed May 28, 2024
1 parent bbf2adb commit b74014e
Show file tree
Hide file tree
Showing 12 changed files with 28,028 additions and 3 deletions.
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 <LL@li.org>\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:password@mysqlserver.local/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"
Loading

0 comments on commit b74014e

Please sign in to comment.