Exemple #1
0
    def api(path):
        # this is not final, requires pydal 19.5
        args = path.split("/")
        app_name = args[0]
        from py4web.core import Reloader, DAL
        from pydal.restapi import RestAPI, Policy

        if MODE != "full":
            raise HTTP(403)
        module = Reloader.MODULES[app_name]

        def url(*args):
            return request.url + "/" + "/".join(args)

        databases = [
            name for name in dir(module) if isinstance(getattr(module, name), DAL)
        ]
        if len(args) == 1:

            def tables(name):
                db = getattr(module, name)
                return [
                    {
                        "name": t._tablename,
                        "fields": t.fields,
                        "link": url(name, t._tablename) + "?model=true",
                    }
                    for t in getattr(module, name)
                ]

            return {
                "databases": [
                    {"name": name, "tables": tables(name)} for name in databases
                ]
            }
        elif len(args) > 2 and args[1] in databases:
            db = getattr(module, args[1])
            id = args[3] if len(args) == 4 else None
            policy = Policy()
            for table in db:
                policy.set(table._tablename, 'GET', authorize=True,
                           allowed_patterns=["**"], allow_lookup=True,
                           fields=table.fields)
                policy.set(table._tablename,'PUT', authorize=True, fields=table.fields)
                policy.set(table._tablename,'POST', authorize=True, fields=table.fields)
                policy.set(table._tablename,'DELETE', authorize=True)
            data = action.uses(db, T)(
                lambda: RestAPI(db, policy)(
                    request.method, args[2], id, request.query, request.json
                )
            )()
        else:
            data = {}
        if "code" in data:
            response.status = data["code"]
        return data
Exemple #2
0
 def __init__(
     self,
     table,
     query=None,
     fields=None,
     limit=100,
     create=True,
     editable=True,
     deletable=True,
 ):
     self.db = table._db
     self.table = table
     self.query = query
     self.fields = fields or [f.name for f in table if f.readable]
     self.limit = limit
     self.create = create
     self.editable = editable
     self.deletable = deletable
     self.policy = Policy()
     self.policy.set(
         table._tablename,
         "GET",
         query=query,
         authorize=True,
         allowed_patterns=["*"],
         fields=fields,
         limit=limit,
     )
     self.restapi = RestAPI(self.db, self.policy)
     self.labels = {}
     self.renderers = {"id": self.idlink}
     self.guessing_renderers = [
         GuessingRenderers.hide_null,
         GuessingRenderers.boolean_renderer,
         GuessingRenderers.link_renderer,
         GuessingRenderers.list_renderer,
         GuessingRenderers.dict_renderer,
         GuessingRenderers.html_renderer,
         GuessingRenderers.large_text_renderer,
     ]
     self.form_attributes = {}
     self.T = lambda value: value
     self.denormalize = {}
Exemple #3
0
    fcalendar1= Form(db.dfcalendar1, dbio=False, formstyle=FormStyleBulma)

    if fcalendar1.accepted:
        mess1 = 'accepted: ' if prn_form_vars( fcalendar1, db.dfcalendar1 ) == False else 'inserted: '
        return put_json_messages(mess1 + str( fcalendar1.form_name ))
    elif fcalendar1.errors:
        print("fcalendar1 has errors: %s" % (fcalendar1.errors))
        return put_json_messages('error: ' + str( fcalendar1.form_name ))
 

    return locals()


from pydal.restapi import RestAPI, Policy

policy = Policy()

policy.set('*', 'GET', authorize=True, allowed_patterns=['*'])
policy.set('*', 'PUT', authorize=True)
policy.set('*', 'POST', authorize=True)
policy.set('*', 'DELETE', authorize=True)

@action('api/<tablename>/', method=["GET", "POST", "PUT", "DELETE"])
@action('api/<tablename>/<rec_id>', method=["GET", "POST", "PUT", "DELETE"])
def api(tablename, rec_id=None):
    return RestAPI(db, policy)(request.method,
                               tablename,
                               rec_id,
                               request.GET,
                               request.POST
                               )
Exemple #4
0
class Grid:

    """
    Example:

    @unauthenticated()
    def index():
       grid = Grid(db.thing, query=None, create=True, editable=True, deletable=True)
       grid.labels = {key: key.title() for key in db.thing.fields}
       grid.renderers['name'] = lambda name: SPAN(name, _class='name')
       return dict(form=grid.make())
    """

    def __init__(
        self,
        table,
        query=None,
        fields=None,
        limit=100,
        create=True,
        editable=True,
        deletable=True,
    ):
        self.db = table._db
        self.table = table
        self.query = query
        self.fields = fields or [f.name for f in table if f.readable]
        self.limit = limit
        self.create = create
        self.editable = editable
        self.deletable = deletable
        self.policy = Policy()
        self.policy.set(
            table._tablename,
            "GET",
            query=query,
            authorize=True,
            allowed_patterns=["*"],
            fields=fields,
            limit=limit,
        )
        self.restapi = RestAPI(self.db, self.policy)
        self.labels = {}
        self.renderers = {"id": self.idlink}
        self.guessing_renderers = [
            GuessingRenderers.hide_null,
            GuessingRenderers.boolean_renderer,
            GuessingRenderers.link_renderer,
            GuessingRenderers.list_renderer,
            GuessingRenderers.dict_renderer,
            GuessingRenderers.html_renderer,
            GuessingRenderers.large_text_renderer]
        self.form_attributes = {}
        self.T = lambda value: value
        self.denormalize = {}

    def xml(self):
        return self.make().xml()

    def make(self):
        """makes the grid, must be called inside an action"""

        T, db, table, query = self.T, self.db, self.table, self.query
        # bypass rest API if it is a form
        id = safeint(request.query.get("id") or request.forms.get("id"))
        if id is not None:
            if (id == 0 and not self.create) or (id > 0 and not self.editable):
                raise HTTP(404)
            record = db(query)(table.id == id).select().first() if id else None
            form = Form(table, record, deletable=self.deletable, **self.form_attributes)
            form.action = (
                request.url.split("?")[0] + "?" + urllib.parse.urlencode(request.query)
            )
            del request.query["id"]
            if form.deleted:
                message = T("Record deleted")
            elif form.accepted:
                message = T("Record created") if not id else T("Record Saved")
            else:
                return DIV(self.header(id), form, _class="py4web-grid")
        else:
            message = ""

        if self.denormalize:
            lookup = []
            for k, fs in self.denormalize.items():
                lookup.append('%s!:%s[%s]' % (k, k, ','.join(fs)))
            request.query['@lookup'] = ','.join(lookup)
        offset = safeint(request.query.get("@offset", 0))
        request.query['@count'] = 'true'
        request.query['@limit'] = offset + self.limit
        id = None
        data = self.restapi("GET", self.table._tablename, None, request.query)
        items = data.get("items", [])
        count = data.get("count", 0)
        table = TABLE(_class="table")
        fields = items[0].keys() if items else [f.rsplit('.',1)[0] for f in request.query if f[:1]!='@']
        table.append(TR(*[TH(self.sortlink(key)) for key in fields]))
        table.append(TR(*[TH(self.filterlink(key)) for key in fields]))
        for item in items:
            table.append(
                TR(*[TD(self.render(key, value)) for key, value in item.items()])
            )
        header = self.header(id, message)
        footer = self.footer(count, offset, len(items))
        return DIV(header, table, footer, _class="py4web-grid")

    def render(self, key, value):
        """renders a value"""
        if key in self.renderers:
            return self.renderers[key](value)
        for guess in self.guessing_renderers:
            rendered = guess(key, value, self.render)
            if rendered is not None:
                return rendered
        return value

    def idlink(self, id):
        """returns the link to edit an id"""
        query = dict(request.query)
        query["id"] = id
        url = request.url.split("?")[0] + "?" + urllib.parse.urlencode(query)
        return A(id, _class="button", _href=url)

    FIELD_TYPES = [
        'id',
        "string",
        "integer",
        "float",
        "boolean",
        "decimal",
        "time",
        "date",
        "datetime",
    ]

    def is_searchable(self, key):
        if not '.' in key:
            return key in self.table.fields and self.table[key].type in self.FIELD_TYPES
        elif key.count('.') == 1:
            fieldname1, fieldname2 = key.split('.')
            field1 = self.table[fieldname1]
            tablename2 = field1.type.split(' ')[1].split('.')[0]
            field2 = self.db[tablename2][fieldname2]
            return field2.type in self.FIELD_TYPES
        return False

    def is_sortable(self, key):
        if not '.' in key:
            return key in self.table.fields and self.table[key].type in self.FIELD_TYPES
        return False

    def sortlink(self, key):
        """returns the link to sort by key"""
        label = self.labels.get(key, key)
        if not self.is_sortable(key):
            return label
        order = request.query.get("@order")
        if order == key:
            new_order, caret = "~" + key, "▼"
        elif order == "~" + key:
            new_order, caret = key, "▲"
        else:
            new_order, caret = key, ""
        return A(label + caret, _href=self.url(page=0, order=new_order))

    def filterlink(self, key):
        """creates an input to filter by key"""
        if not self.is_searchable(key):
            return ""
        return INPUT(_id="py4web-grid-filter-" + key, _class="py4web-grid-filter")

    def url(self, page, order=None):
        """generates a url for a page and a sorting"""
        query = dict(request.query)
        query["@offset"] = page * self.limit
        if order:
            query["@order"] = order
        return request.url.split("?")[0] + "?" + urllib.parse.urlencode(query)

    def header(self, id, message=""):
        """generates the header"""
        T = self.T
        div = DIV(_class="py4web-grid-header")
        if message:
            div.append(DIV(message, _class="py4web-grid-message"))
        if self.create and id is None:
            div.append(
                A(
                    T("Create Record"),
                    _class="button",
                    _href=request.url.split("?")[0] + "?id=0",
                )
            )
        elif (self.create and id == 0) or (self.editable and id and id > 0):
            query = dict(request.query)
            query.pop("id", None)
            url = request.url.split("?")[0] + "?" + urllib.parse.urlencode(query)
            div.append(A(T("Back"), _class="button", _href=url))
            div.append(T("New Record") if id == 0 else T("Edit Record"))
        return div

    def footer(self, count, offset, len_items):
        """generates the footer"""
        T = self.T
        page = int(offset / self.limit)
        last_page = math.floor(count / self.limit)
        return DIV(
            A("First", _href=self.url(0), _class="button") if page > 1 else "",
            A("Previous", _href=self.url(page - 1), _class="button")
            if page > 0
            else "",
            SPAN(
                T("page %s/%s (%s/%s records)")
                % (page + 1, last_page + 1, len_items, count)
            ),
            A("Next", _href=self.url(page + 1), _class="button")
            if page < last_page
            else "",
            A("Last", _href=self.url(last_page - 1), _class="button")
            if page < last_page - 1
            else "",
            SCRIPT(Grid.script),
            _class="py4web-grid-footer",
        )

    script = r"""
Exemple #5
0
    def api(path):
        # this is not final, requires pydal 19.5
        args = path.split("/")
        app_name = args[0]
        if MODE != "full":
            raise HTTP(403)
        module = Reloader.MODULES.get(app_name)

        if not module:
            raise HTTP(404)

        def url(*args):
            return request.url + "/" + "/".join(args)

        databases = [
            name for name in dir(module) if isinstance(getattr(module, name), DAL)
        ]
        if len(args) == 1:

            def tables(name):
                db = getattr(module, name)                
                make_safe(db)
                return [
                    {
                        "name": t._tablename,
                        "fields": t.fields,
                        "link": url(name, t._tablename) + "?model=true",
                    }
                    for t in getattr(module, name)
                ]

            return {
                "databases": [
                    {"name": name, "tables": tables(name)} for name in databases
                ]
            }
        elif len(args) > 2 and args[1] in databases:
            db = getattr(module, args[1])
            make_safe(db)          
            id = args[3] if len(args) == 4 else None
            policy = Policy()
            for table in db:
                policy.set(
                    table._tablename,
                    "GET",
                    authorize=True,
                    allowed_patterns=["**"],
                    allow_lookup=True,
                    fields=table.fields,
                )
                policy.set(table._tablename, "PUT", authorize=True, fields=table.fields)
                policy.set(
                    table._tablename, "POST", authorize=True, fields=table.fields
                )
                policy.set(table._tablename, "DELETE", authorize=True)

            # must wrap into action uses to make sure it closes transactions
            data = action.uses(db)(lambda: RestAPI(db, policy)(
                request.method, args[2], id, request.query, request.json
            ))()
        else:
            data = {}
        if "code" in data:
            response.status = data["code"]
        return data