Rede¶
py4web comes with a Grid object providing grid and CRUD capabilities.
Características principais¶
Clique cabeças de coluna para classificar - clique novamente para DESC
Controle de paginação
Construído em Search (pode usar search_queries OU search_form)
Botões de ação - com ou sem texto
CRUD completa com Confirmação de exclusão
Pré e Pós Ação (adicionar seus próprios botões para cada linha)
Datas de grid em formato local
Formatação padrão por tipo de utilizador mais substituições
Exemplo básico¶
Neste exemplo simples, vamos fazer uma grid sobre a mesa da empresa.
controllers.py
from functools import reduce
from py4web.utils.grid import Grid
from py4web import action
from .common import db, session, auth
@action('companies', method=['POST', 'GET'])
@action('companies/<path:path>', method=['POST', 'GET'])
@action.uses(session, db, auth, 'grid.html')
def companies(path=None):
grid = Grid(path,
query=(db.company.id > 0),
orderby=[db.company.name],
search_queries=[['Search by Name', lambda val: db.company.name.contains(val)]])
return dict(grid=grid)
grid.html
[[extend 'layout.html']]
[[=grid.render()]]
Assinatura¶
class Grid:
def __init__(
self,
path,
query,
search_form=None,
search_queries=None,
columns=None,
show_id=False,
orderby=None,
left=None,
headings=None,
create=True,
details=True,
editable=True,
deletable=True,
pre_action_buttons=None,
post_action_buttons=None,
auto_process=True,
rows_per_page=15,
include_action_button_text=True,
search_button_text="Filter",
formstyle=FormStyleDefault,
grid_class_style=GridClassStyle,
):
caminho: a rota do pedido
query: consulta pydal a ser processado
search_form: FORM py4web a ser incluído como o formulário de busca. Se search_form é passado em seguida, o desenvolvedor é responsável por aplicar o filtro para a consulta passada. Isso é diferente de search_queries.
search_queries: lista de listas de consulta para usar para construir o formulário de busca. Ignorado se search_form é usado. O formato é
columns: list of fields or columns to display on the list page, if blank, the table will use all readable fields of the searched table
show_id: mostrar o campo ID de registo na página de lista - default = false
orderby: Campo orderby pydal ou lista de campos
esquerda: se juntando outras tabelas, especifique a expressão esquerda pydal aqui
títulos: lista de títulos a ser utilizado para página da lista - se não for fornecido o uso do rótulo do campo
detalhes: URL para redirecionar para os registros exibindo - Defina como true para gerar automaticamente a URL - definido como falso para não exibir o botão
criar: URL para redirecionar para a criação de registros - definido como verdadeiro para gerar automaticamente o URL - definido como falso para não exibir o botão
editável: URL para redirecionar para a edição de registros - Defina como true para gerar automaticamente a URL - definido como falso para não exibir o botão
deletable: URL para redirecionar para a exclusão de registros - Defina como true para gerar automaticamente a URL - definido como falso para não exibir o botão
pre_action_buttons: lista de instâncias action_button para incluir antes de os botões de ação padrão
post_action_buttons: lista de instâncias action_button para incluir após os botões de ação padrão
auto_process: Boolean - se ou não a grid deve ser processado imediatamente. Se False, desenvolvedor deve chamar grid.process () uma vez todos os parâmetros são configurados
rows_per_page: número de linhas a serem exibidos por página. padrão 15
include_action_button_text: boolean dizendo a grid se deseja ou não de texto em botões de ação dentro de sua grid
search_button_text: texto a ser exibido no botão enviar em seu formulário de pesquisa
formstyle: py4web Form formstyle usado para estilo seu formulário ao construir automaticamente formulários CRUD
grid_class_style: objeto GridClassStyle usado para os padrões de substituição para denominar sua grid prestados. Permite especificar classes ou estilos para aplicar em certos pontos da grid.
Searching / Filtering¶
Há duas maneiras de construir um formulário de pesquisa.
Fornecer uma lista search_queries
Construa a sua própria forma de pesquisa personalizada
Se você fornecer uma lista search_queries à grid, ele irá:
build a search form. If more than one search query in the list, it will also generate a dropdown to select which search field to search against
recolher valores de filtro e filtrar a grid
No entanto, se isso não lhe dá flexibilidade suficiente, você pode fornecer o seu próprio formulário de busca e lidar com toda a filtragem (construção das consultas) por si mesmo.
CRUD¶
A grid oferece recursos CRUD (CRUD) utilizando formulário py4web.
Você pode desligar CRUD apresenta pela configuração criar / details / editável / elimináveis durante a instanciação grid.
Além disso, você pode fornecer uma URL separada para a criação / detalhes / editáveis / parâmetros elimináveis para ignorar as páginas CRUD gerados automaticamente e lidar com as páginas de detalhes do mesmo.
Custom Columns¶
If the grid does not involve a join but displays results from a single table you can specify a list of columns and columns are highly customizable.
from py4web.utils.grid import Column
from yatl helpers import A
columns = [
db.company.id,
db.company.name,
Column("Web Site", lambda row: f"https://{row.name}.com"),
Column("Go To", lambda row: A("link", _href=f"https://{row.name}.com"))
]
grid = Grid(... columns=columns ...)
Notice in this example the first two columns are regular fields,
The third column has a header «Web Site» and consists of URL strings generated from the rows.
The fourth column has a header «Go To» and generates actual clickable links using the A
helper.
Usando templates¶
Use o seguinte para tornar a sua grid ou formas CRUD em seus templates.
Mostrar a grid ou um formulário CRUD
[[=grid.render()]]
Para permitir a personalização de layout do formulário CRUD (como com web2py) você pode usar o seguinte
[[form = grid.render() ]]
[[form.custom["begin"] ]]
...
[[form.custom["submit"]
[[form.custom["end"]
Ao manusear formulário personalizado layouts que você precisa saber se você está exibindo a grid ou um formulário. Use o seguinte para decidir
[[if request.query.get('action') in ('details', 'edit'):]]
# Display the custom form
[[form = grid.render() ]]
[[form.custom["begin"] ]]
...
[[form.custom["submit"]
[[form.custom["end"]
[[else:]]
[[grid.render() ]]
[[pass]]
Personalizando Estilo¶
Você pode fornecer suas próprias formstyle ou grid classes e estilo ao grid.
formstyle é o mesmo que um formstyle Forma, usadas para as formas estilo CRUD.
grid_class_style é uma classe que fornece as classes e / ou estilos utilizados para certas porções da grelha.
O GridClassStyle padrão - baseado em no.css, principalmente usa estilos para modificar o layout da grid
class GridClassStyle:
"""
Default grid style
Internal element names match default class name, other classes can be added
Style use should be minimized since it cannot be overridden by CSS
"""
classes = {
"grid-wrapper": "grid-wrapper",
"grid-header": "grid-header",
"grid-new-button": "grid-new-button info",
"grid-search": "grid-search",
"grid-table-wrapper": "grid-table-wrapper",
"grid-table": "grid-table",
"grid-sorter-icon-up": "grid-sort-icon-up fas fa-sort-up",
"grid-sorter-icon-down": "grid-sort-icon-down fas fa-sort-down",
"grid-th-action-button": "grid-col-action-button",
"grid-td-action-button": "grid-col-action-button",
"grid-tr": "",
"grid-th": "",
"grid-td": "",
"grid-details-button": "grid-details-button info",
"grid-edit-button": "grid-edit-button info",
"grid-delete-button": "grid-delete-button info",
"grid-footer": "grid-footer",
"grid-info": "grid-info",
"grid-pagination": "grid-pagination",
"grid-pagination-button": "grid-pagination-button info",
"grid-pagination-button-current": "grid-pagination-button-current default",
"grid-cell-type-string": "grid-cell-type-string",
"grid-cell-type-text": "grid-cell-type-text",
"grid-cell-type-boolean": "grid-cell-type-boolean",
"grid-cell-type-float": "grid-cell-type-float",
"grid-cell-type-int": "grid-cell-type-int",
"grid-cell-type-date": "grid-cell-type-date",
"grid-cell-type-time": "grid-cell-type-time",
"grid-cell-type-datetime": "grid-cell-type-datetime",
"grid-cell-type-upload": "grid-cell-type-upload",
"grid-cell-type-list": "grid-cell-type-list",
# specific for custom form
"search_form": "search-form",
"search_form_table": "search-form-table",
"search_form_tr": "search-form-tr",
"search_form_td": "search-form-td",
}
styles = {
"grid-wrapper": "",
"grid-header": "display: table; width: 100%",
"grid-new-button": "display: table-cell;",
"grid-search": "display: table-cell; float:right",
"grid-table-wrapper": "overflow-x: auto; width:100%",
"grid-table": "",
"grid-sorter-icon-up": "",
"grid-sorter-icon-down": "",
"grid-th-action-button": "",
"grid-td-action-button": "",
"grid-tr": "",
"grid-th": "white-space: nowrap; vertical-align: middle",
"grid-td": "white-space: nowrap; vertical-align: middle",
"grid-details-button": "margin-bottom: 0",
"grid-edit-button": "margin-bottom: 0",
"grid-delete-button": "margin-bottom: 0",
"grid-footer": "display: table; width:100%",
"grid-info": "display: table-cell;",
"grid-pagination": "display: table-cell; text-align:right",
"grid-pagination-button": "min-width: 20px",
"grid-pagination-button-current": "min-width: 20px; pointer-events:none; opacity: 0.7",
"grid-cell-type-string": "white-space: nowrap; vertical-align: middle; text-align: left; text-overflow: ellipsis; max-width: 200px",
"grid-cell-type-text": "vertical-align: middle; text-align: left; text-overflow: ellipsis; max-width: 200px",
"grid-cell-type-boolean": "white-space: nowrap; vertical-align: middle; text-align: center",
"grid-cell-type-float": "white-space: nowrap; vertical-align: middle; text-align: right",
"grid-cell-type-int": "white-space: nowrap; vertical-align: middle; text-align: right",
"grid-cell-type-date": "white-space: nowrap; vertical-align: middle; text-align: right",
"grid-cell-type-time": "white-space: nowrap; vertical-align: middle; text-align: right",
"grid-cell-type-datetime": "white-space: nowrap; vertical-align: middle; text-align: right",
"grid-cell-type-upload": "white-space: nowrap; vertical-align: middle; text-align: center",
"grid-cell-type-list": "white-space: nowrap; vertical-align: middle; text-align: left",
# specific for custom form
"search_form": "",
"search_form_table": "",
"search_form_tr": "",
"search_form_td": "",
}
@classmethod
def get(cls, element):
"""returns a dict with _class and _style for the element name"""
return {
"_class": cls.classes.get(element),
"_style": cls.styles.get(element),
}
GridClassStyleBulma - implementação bulma
class GridClassStyleBulma(GridClassStyle):
classes = {
"grid-wrapper": "grid-wrapper field",
"grid-header": "grid-header pb-2",
"grid-new-button": "grid-new-button button",
"grid-search": "grid-search is-pulled-right pb-2",
"grid-table-wrapper": "grid-table-wrapper table_wrapper",
"grid-table": "grid-table table is-bordered is-striped is-hoverable is-fullwidth",
"grid-sorter-icon-up": "grid-sort-icon-up fas fa-sort-up is-pulled-right",
"grid-sorter-icon-down": "grid-sort-icon-down fas fa-sort-down is-pulled-right",
"grid-th-action-button": "grid-col-action-button is-narrow",
"grid-td-action-button": "grid-col-action-button is-narrow",
"grid-tr": "",
"grid-th": "",
"grid-td": "",
"grid-details-button": "grid-details-button button is-small",
"grid-edit-button": "grid-edit-button button is-small",
"grid-delete-button": "grid-delete-button button is-small",
"grid-footer": "grid-footer",
"grid-info": "grid-info is-pulled-left",
"grid-pagination": "grid-pagination is-pulled-right",
"grid-pagination-button": "grid-pagination-button button is-small",
"grid-pagination-button-current": "grid-pagination-button-current button is-primary is-small",
"grid-cell-type-string": "grid-cell-type-string",
"grid-cell-type-text": "grid-cell-type-text",
"grid-cell-type-boolean": "grid-cell-type-boolean has-text-centered",
"grid-cell-type-float": "grid-cell-type-float",
"grid-cell-type-int": "grid-cell-type-int",
"grid-cell-type-date": "grid-cell-type-date",
"grid-cell-type-time": "grid-cell-type-time",
"grid-cell-type-datetime": "grid-cell-type-datetime",
"grid-cell-type-upload": "grid-cell-type-upload",
"grid-cell-type-list": "grid-cell-type-list",
# specific for custom form
"search_form": "search-form is-pulled-right pb-2",
"search_form_table": "search-form-table",
"search_form_tr": "search-form-tr",
"search_form_td": "search-form-td pr-1",
}
styles = {
"grid-wrapper": "",
"grid-header": "",
"grid-new-button": "",
"grid-search": "",
"grid-table-wrapper": "",
"grid-table": "",
"grid-sorter-icon-up": "",
"grid-sorter-icon-down": "",
"grid-th-action-button": "",
"grid-td-action-button": "",
"grid-tr": "",
"grid-th": "text-align: center; text-transform: uppercase;",
"grid-td": "",
"grid-details-button": "",
"grid-edit-button": "",
"grid-delete-button": "",
"grid-footer": "padding-top: .5em;",
"grid-info": "",
"grid-pagination": "",
"grid-pagination-button": "margin-left: .25em;",
"grid-pagination-button-current": "margin-left: .25em;",
"grid-cell-type-string": "",
"grid-cell-type-text": "",
"grid-cell-type-boolean": "",
"grid-cell-type-float": "",
"grid-cell-type-int": "",
"grid-cell-type-date": "",
"grid-cell-type-time": "",
"grid-cell-type-datetime": "",
"grid-cell-type-upload": "",
"grid-cell-type-list": "",
# specific for custom form
"search_form": "",
"search_form_table": "",
"search_form_tr": "",
"search_form_td": "",
}
Você pode construir seu próprio class_style para ser usado com o quadro css de sua escolha.
Ação personalizada Botões¶
Tal como acontece com web2py, você pode adicionar botões adicionais para cada linha em sua grid. Você faz isso fornecendo pre_action_buttons ou post_action_buttons à Rede ** inicialização ** método.
pre_action_buttons - lista de instâncias action_button para incluir antes de os botões de ação padrão
post_action_buttons - lista de instâncias action_button para incluir após os botões de ação padrão
Você pode construir sua própria classe de ação do botão para passar para pré / botões de ação pós baseados no template abaixo (isso não é fornecido com py4web)
Botão Classe Ação Amostra¶
def __init__(self,
url,
text,
icon="fa-calendar",
additional_classes=None,
message=None,
append_id=False):
url: a página para navegar até quando o botão é clicado
texto: texto para exibição no botão
ícone: o ícone font-incrível para exibição antes do texto
additional_classes: uma lista separada por espaços de aulas para incluir no elemento botão
mensagem: mensagem de confirmação para exibição se a classe ‘confirmação’ é adicionado a classes adicionais
append_id: Se for verdade, adicionar id_field_name = id_value à querystring url para o botão
Os campos de referência¶
Ao exibir campos em uma tabela PyDAL, às vezes você deseja exibir um campo mais descritivo do que um valor de chave estrangeira. Há um par de maneiras de lidar com isso com a grid py4web.
filter_out
on PyDAL field definition - here is an example of a foreign
key field
Field('company', 'reference company',
requires=IS_NULL_OR(IS_IN_DB(db, 'company.id',
'%(name)s',
zero='..')),
filter_out=lambda x: x.name if x else ''),
Isto irá exibir o nome da empresa na grid em vez do ID empresa
A queda de usar este método é que classificação e filtragem são baseados no campo da empresa na tabela de empregado e não o nome da empresa
esquerda juntar-se e especificar campos da tabela juntou - especificado no parâmetro esquerdo da grid instanciação
db.company.on(db.employee.company == db.company.id)
Você pode especificar um PyDAL padrão LEFT JOIN, incluindo uma lista de junta a considerar.
Agora o campo nome da empresa pode ser incluído em sua lista de campos pode ser clicado e ordenados.
Agora você também pode especificar uma consulta como:
queries.append((db.employee.last_name.contains(search_text)) | (db.employee.first_name.contains(search_text)) | db.company.name.contains(search_text)))
Este método permite classificar e filtrar, mas não permite que você para combinar campos a serem exibidos em conjunto, como o método filter_out faria
Você precisa determinar qual método é melhor para o seu caso de uso compreender as grids diferentes no mesmo aplicativo pode precisar de se comportar de forma diferente.