From 20551c02fba716773d6546f0868ddd95f294c39c Mon Sep 17 00:00:00 2001 From: Nico Zanferrari Date: Fri, 28 Jun 2024 16:39:28 +0200 Subject: [PATCH] DOCS: Pydal, Tags and some screenshots update --- docs/chapter-06.rst | 23 ++++++--- docs/chapter-07.rst | 102 +++++++++++++++++++++++++++---------- docs/chapter-13.rst | 32 ++++++++++-- docs/images/example_db.png | Bin 0 -> 145939 bytes docs/images/first_run.png | Bin 50011 -> 71622 bytes 5 files changed, 117 insertions(+), 40 deletions(-) create mode 100644 docs/images/example_db.png diff --git a/docs/chapter-06.rst b/docs/chapter-06.rst index 8fc7c87d9..b3437314a 100644 --- a/docs/chapter-06.rst +++ b/docs/chapter-06.rst @@ -21,7 +21,8 @@ save the session back in the database if data has changed. PY4WEB fixtures provide a mechanism to specify what an action needs so that py4web can accomplish the required tasks (and skip non required ones) in the most efficient manner. Fixtures make the code efficient and -reduce the need for boilerplate code. +reduce the need for boilerplate code. Think of fixtures as per action +(as opposed to per app) middleware. PY4WEB fixtures are similar to WSGI middleware and BottlePy plugin except that they apply to individual actions, not to all of them, and @@ -409,9 +410,14 @@ Client-side session in cookies By default the session object is stored inside a cookie called ``appname_session``. It's a JWT, hence encoded in a URL-friendly string format and signed using the provided secret for preventing tampering. -Notice that it's not encrypted (in fact it's quite trivial to read its -content from http communications or from disk), so do not place any -sensitive information inside, and use a complex secret. + +.. warning:: + + Data embedded in cookies is signed, not encrypted! In fact it's quite + trivial to read its content from http communications or from disk, so + do not place any sensitive information inside, and use a complex secret. + + If the secret changes existing sessions are invalidated. If the user switches from HTTP to HTTPS or vice versa, the user session is also invalidated. Session in cookies have a @@ -539,7 +545,7 @@ data from another app (app1) running on the same server: The Condition fixture --------------------- -Some times you want to restrict access to an action based on a +Sometimes you want to restrict access to an action based on a given condition. For example to enforce a workflow: .. code:: python @@ -585,9 +591,10 @@ for example, to redirect to another page: Condition(cond, on_false=lambda: redirect(URL('step1'))) -You can use condition to check permissions. For example, assuming you are using -`Tags` as explained in chapter 13 and you are giving group memberships to users, -then you can require that users action have specific group membership: +You can use condition to check permissions. For example, if you +are giving group memberships to users using `Tags` (it will be explained +later on the :ref:`Authorization using Tags` chapter), then you can +require that users action have specific group membership: .. code:: python diff --git a/docs/chapter-07.rst b/docs/chapter-07.rst index 0f379d066..4e15ed98a 100644 --- a/docs/chapter-07.rst +++ b/docs/chapter-07.rst @@ -5,7 +5,7 @@ The Database Abstraction Layer (DAL) DAL introduction ---------------- -py4web rely on a database abstraction layer (DAL), an API that maps +py4web rely on a database abstraction layer (**DAL**), an API that maps Python objects into database objects such as queries, tables, and records. The DAL dynamically generates the SQL in real time using the specified dialect for the database back end, so that you do not have to @@ -16,6 +16,13 @@ The DAL choosen is a pure Python one called `pyDAL >>`` are also directly executable via a py4web shell. -This is a simple example, using the provided ``examples`` app: +This is a simple example, using the provided ``showcase`` app: .. code:: python - >>> from py4web import DAL, Field - >>> from apps.examples import db + >>> from apps.showcase.examples.models import db >>> db.tables() - ['auth_user', 'auth_user_tag_groups', 'person', 'superhero', 'superpower', 'tag', 'product', 'thing'] + ['auth_user', 'auth_user_tag_groups', 'person', 'superhero', 'superpower', 'tag', 'thing', 'user_token', 'dummy'] >>> rows = db(db.superhero.name != None).select() >>> rows.first() , 'name': 'Superman', 'real_identity': 1}> @@ -210,6 +219,24 @@ You can also start by creating a connection from zero. For the sake of simplicit can use SQLite. Nothing in this discussion changes when you switch the back-end engine. +Using the dashboard app with databases +-------------------------------------- + +Generally you can use the dashboard app for viewing and modifying the databases +of a particular app. However this is not bulletproof, so for +security reason this by default is not applied to the showcase app. +But if your installation is local (not exposed to public networks), you can enable it +by simply adding to the file``apps/showcase/__init__.py`` the line: + +.. code:: python + + from .examples.models import db + + +This allow you to look graphically inside the showcase application database: + +.. image:: images/example_db.png + DAL constructor --------------- @@ -321,6 +348,10 @@ Database Connection string - in SQLite the database consists of a single file. If it does not exist, it is created. This file is locked every time it is accessed. + In addition to the file 'storage.sqlite' that contains the data, there will + be also a sql.log file plus one additional file called longhash_tablename.table + for every table definition. The table definition files are used during migrations; + in case of problems they could be deleted (they'll be automatically recreated). - in the case of MySQL, PostgreSQL, MSSQL, FireBird, Oracle, DB2, Ingres and Informix the database “test” must be created outside py4web. Once the connection is established, py4web will create, alter, and drop @@ -415,8 +446,8 @@ table is actually referenced. Model-less applications ~~~~~~~~~~~~~~~~~~~~~~~ -In py4web the code defined outside of actions (where normally DAL tables -are defined) is only executed at startup. +Normally in py4web the code that define DAL tables lives in the file +``models.py``, hence it's only executed at startup because it's outside of actions. However, it is possible to define DAL tables on demand inside actions. This is referred to as “model-less” development by the py4web community. @@ -546,15 +577,24 @@ Database folder location ^^^^^^^^^^^^^^^^^^^^^^^^ ``folder`` sets the place where migration files will be created (see -Migrations_ for details). -It is also used for SQLite databases. Automatically set within py4web. -Set a path when using DAL outside py4web. +Migrations_ for details). By default it's automatically set within py4web on the same +folder of the database itself, but you have to specify it when using DAL outside py4web. + +Note that for SQLite databases it's normally necessary, +otherwise you'll implictly choose an in memory database (where folder and +migrations don't have any sense). So these constructors have the same meaning: + +.. code:: python + + db = DAL('sqlite://storage.sqlite') # folder parameter not specified + db = DAL('sqlite:memory') # in memory database + Default migration settings ^^^^^^^^^^^^^^^^^^^^^^^^^^ The DAL constructor migration settings are booleans affecting defaults -and global behaviour. +and global behaviour (again, see Migrations_ for details) ``migrate = True`` sets default migrate behavior for all tables @@ -574,7 +614,7 @@ operations may be executed immediately, depending on the database engine. If you pass ``db`` in an ``action.uses`` decorator, you don't need to call -commit in the controller, it is done for you. (Also, if you use +commit in the controller, it is automatically done for you (also, if you use ``authenticated`` or ``unauthenticated`` decorator.) .. tip:: @@ -1644,9 +1684,9 @@ database. db = DAL("sqlite:memory") db.define_table("thing", Field("name")) - properties = Tags(db.thing) id1 = db.thing.insert(name="chair") id2 = db.thing.insert(name="table") + properties = Tags(db.thing) properties.add(id1, "color/red") properties.add(id1, "style/modern") properties.add(id2, "color/green") @@ -1664,14 +1704,16 @@ database. rows = db(properties.find(["color"])).select() assert len(rows) == 2 -It is internally implemented as a table, which in + +``Tags`` are hierarchical. Then ``find([“color”])`` would return id1 and id2 +because both records have tags with “color”. + +It is internally implemented with the creation of an additional table, which in this example would be db.thing_tags_default, because no tail was -specified on the Tags(table, tail=“default”) constructor. +specified on the ``Tags(table, tail=“default”)`` constructor. -The ``find`` method is doing a search by ``startswith`` of the -parameter. Then find([“color”]) would return id1 and id2 -because both records have tags starting with “color”. py4web uses tags as a -flexible mechanism to manage permissions. +py4web uses ``Tags`` as a flexible mechanism to manage permissions, we'll see +all the details later on the :ref:`Authorization using Tags` chapter. Raw SQL @@ -3303,10 +3345,9 @@ and all owners of Boat: Alex Curt -A lighter alternative to many-to-many relations is tagging, you can -found an example of this in the next section. Tagging works even on -database backends that do not support JOINs like the Google App Engine -NoSQL. +A lighter alternative to many-to-many relations is tagging, see the +:ref:`Authorization using Tags` chapter. Tagging works even on database backends +that do not support JOINs like the Google App Engine NoSQL. Self-Reference and aliases ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -3962,7 +4003,7 @@ it to XML/HTML: If you need to serialize the Rows in any other XML format with custom -tags, you can easily do that using the universal :ref:`TAG` helper +tags, you can easily do that using the universal ``TAG`` XML helper that we'll see later and the Python syntax ``*`` allowed in function calls: @@ -3979,6 +4020,13 @@ that we'll see later and the Python syntax 3Carl +.. warning:: + + Do not confuse the `TAG` XML helper used here (see the :ref:`TAG` + chapter) with the ``Tags`` method that will be extensively explained + on the :ref:`Authorization using Tags` chapter. + + Data representation ~~~~~~~~~~~~~~~~~~~ diff --git a/docs/chapter-13.rst b/docs/chapter-13.rst index 192b8321f..64441a954 100644 --- a/docs/chapter-13.rst +++ b/docs/chapter-13.rst @@ -356,7 +356,8 @@ Authorization using Tags As already mentioned, authorization is the process of verifying what specific applications, files, and data a user has access to. This is accomplished -in py4web using ``Tags``. +in py4web using ``Tags``, that we've already discovered on :ref:`Tagging records` +in the DAL chapter. Tags and Permissions @@ -382,14 +383,16 @@ from ``pydal.tools``. Then create a Tags object to tag a table: .. code:: python from pydal.tools.tags import Tags - groups = Tags(db.auth_user) + groups = Tags(db.auth_user, 'groups') -If you look at the database level, a new table will be created with a -name equals to tagged_db + '_tag' + tagged_name, in this case -``auth_user_tag_groups``: +The tail_name parameter is optional and if not specified the 'default' +value will be used. If you look at the database level, a new table will +be created with a name equals to ``tagged_db + '_tag_' + tail_name``, +in this case ``auth_user_tag_groups``: .. image:: images/tags_db.png + Then you can add one or more tags to records of the table as well as remove existing tags: @@ -434,6 +437,25 @@ tag(s): users = db(groups.find([group_name])).select(orderby=db.auth_user.first_name | db.auth_user.last_name) return {'users': users} +We've already seen a simple ``requires_membership`` fixture on :ref:``The Condition fixture``. It +enables the following syntax: + +.. code:: python + + groups = Tags(db.auth_user) + + def requires_membership(group_name): + return Condition( + lambda: group_name in groups.get(auth.user_id), + exception=HTTP(404) + ) + + @action('index') + @action.uses(requires_membership('teacher')) + def index(): + return 'hello teacher' + + We leave it to you as an exercise to create a fixture ``has_membership`` to enable the following syntax: diff --git a/docs/images/example_db.png b/docs/images/example_db.png new file mode 100644 index 0000000000000000000000000000000000000000..7ae7315ba825f9b98443dfca18dc0586b5938470 GIT binary patch literal 145939 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

LSUx)qusc5En;eA7oSpAnjP*zY@)nTWTsQC;R$O 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

p$vuMwxaa)-B_Cgjq$cY2!h3JQgE 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