py4web Web Framework
repository·master·Indexed 43 days ago
https://github.com/web2py/py4webA fast, stable, and lightweight comprehensive web framework (version 0.1.0). It features a Database Abstraction Layer (DAL), yatl for server-side templating, and built-in support for Vue components with a Python back-end. py4web supports containerized deployments via Docker and Docker Compose, including integrations with PostgreSQL, Celery, and Redis, as well as deployment to Google App Engine.
What's inside py4web
- py4web is a web framework designed for rapid development of efficient, database-driven web applications. It is a modular evolution of the web2py framework, redesigned to be faster, more lightweight, and easier to integrate with third-party components. Unlike its predecessor, py4web is built on Python 3 and follows standard Python packaging and import conventions.
py4web Community and Resources
masterDocumentation
- Reference Manual: Available online at https://py4web.com/_documentation/static/index.html (includes PDF and EBOOK versions).
Discussion and Support
- Google Group: The primary source for developer discussions and troubleshooting: https://groups.google.com/g/py4web.
- Discord: For quick questions and real-time chat: https://discord.gg/xCzQ9KTk3W.
Source Code
- GitHub: The project is Open Source (BSD v3 license) and hosted at https://github.com/web2py/py4web.
What is the pyDAL RestAPI and how does it work?
masterThe RestAPI is a feature included in pyDAL (since version 19.5.10) that provides a restful interface for querying and manipulating database records via HTTP methods (GET, POST, PUT, DELETE).
Inspired by GraphQL, it allows clients to query information using the
GETmethod and specify response formats, including following references and denormalizing data. Unlike GraphQL, it uses aPolicysystem to dynamically restrict which queries, tables, or fields are accessible based on the request context (user, state, etc.).What is the Database Abstraction Layer (DAL)?
masterpy4web uses pyDAL, a pure-Python Database Abstraction Layer that maps Python objects to database objects (tables, records, and queries). Instead of writing raw SQL, you use the DAL API to generate SQL in real time based on your configured database backend. This allows the same application code to run against different database vendors without modification.
Unlike traditional Object Relational Mappers (ORMs) like SQLAlchemy or Django ORM that map rows to class instances, pyDAL returns rows as Python dictionaries, keeping the API closely aligned with SQL logic. It supports features like transactions, aggregates, joins, and nested selects.
Overview of py4web Forms
masterThe
Formclass provides a high-level API for building CRUD (Create, Update, Delete) forms. It can generate forms from an existing database table or from a manual list ofFieldobjects.There are three primary patterns for using forms:
- CRUD Create forms: Associated with a DAL table to insert new records.
- CRUD Update forms: Associated with a DAL table and a specific record ID to modify existing records.
- Non-CRUD forms: Not associated with a database; used for collecting arbitrary data (e.g., contact forms, search queries) by defining a list of
Fieldobjects manually.
# CRUD Create form = Form(db.thing) # CRUD Update form = Form(db.thing, thing_id) # Non-CRUD fields = [Field("name"), Field("color")] form = Form(fields)How the Database Abstraction Layer (DAL) and Adapters work
masterThe Database Abstraction Layer (DAL) provides a modular interface for interacting with various databases. When you perform operations on a
TableorSetobject (e.g.,db.mytable.insert(...)), the DAL delegates the task to an Adapter.An adapter is responsible for:
- Generating the appropriate SQL query or function call.
- Executing the query via a database driver.
BaseAdapterdefines the standard interface that all specific database adapters (likeMySQLAdapter,PostgreSQLAdapter, etc.) must implement. This architecture allows py4web to support a wide variety of relational and NoSQL databases through a unified API.# Example of how a high-level call is delegated db.mytable.insert(myfield='myvalue') # Internally, the Table object calls the adapter: db._adapter.insert(db.mytable, db._adapter._listify(dict(myfield='myvalue')))How py4web handles response formats
masterUnlike web2py, py4web does not rely on file extensions (like
.htmlor.json) in the URL to determine the response type. Instead, the response format is determined by the return value of the action:- If the action returns a
dict()and has an associated template, the dictionary will be rendered by the template. - If the action returns a
dict()but has no template, it will be rendered as JSON.
For more complex response behaviors, you can use decorators.
- If the action returns a
How the Grid object works in py4web
masterThe
Gridobject is a core component of py4web that provides a highly customizable interface for CRUD (Create, Read, Update, and Delete) operations on database tables. It automatically handles pagination, sorting (by clicking column heads), searching, and action buttons.To use it, you instantiate a
Gridobject within a py4web action, passing it a PyDAL query. You then render the grid in your template using[[=grid.render()]]. The grid can be styled using various CSS frameworks like Bulma, Bootstrap 4, or Bootstrap 5 by configuringformstyle,grid_class_style, andicon_style.@action('index', method=['POST', 'GET']) @action.uses('grid.html', db) def index(): grid = Grid( query=(db.person.id > 0), orderby=[db.person.name], search_queries=[['Search by Name', lambda val: db.person.name.contains(val)]] ) return dict(grid=grid)Implement One-to-Many relations in the DAL
masterTo create a one-to-many relationship, define a field in the 'child' table using the
'reference tablename'syntax. This field stores the ID of the record in the 'parent' table.Once defined, the DAL automatically adds a virtual attribute to the parent table records (a
Set) that allows you to easily access all related child records.# Define parent table db.define_table('person', Field('name')) # Define child table with a reference to 'person' db.define_table('thing', Field('name'), Field('owner_id', 'reference person')) # Insert data db.person.insert(name='Alex') # returns ID 1 db.thing.insert(name='Boat', owner_id=1) # Accessing related records via the virtual attribute for person in db().select(db.person.ALL): print(person.name) for thing in person.thing.select(): print(' ', thing.name)Synchronize records across remote databases using UUIDs
masterStandard database IDs are only unique within a single database instance. To synchronize or merge data between different py4web installations without breaking references or creating duplicates, you must implement a UUID-based model.
Requirements for Sync-ready Models:
- Unique ID: Include a
uuidfield (e.g.,Field('uuid', length=64)). - Modification Tracking: Include a
modified_onfield (e.g.,Field('modified_on', 'datetime', default=now, update=now)). - UUID References: Use the
uuidfield for references instead of the integerid. UseIS_IN_DB(db, 'tablename.uuid', '%(field)s')to enforce this.
Implementation Pattern: Set the
uuiddefault to a lambda that generates a new UUID string for every insertion to ensure uniqueness even in bulk transactions.import datetime import uuid now = datetime.datetime.utcnow db.define_table('person', Field('uuid', length=64), Field('modified_on', 'datetime', default=now, update=now), Field('name') ) # Set default to a lambda for unique generation per record db.person.uuid.default = lambda: str(uuid.uuid4()) # Example usage nid = str(uuid.uuid4()) db.person.insert(uuid=nid, name='Massimo')- Unique ID: Include a
Guard dangerous operations with MODE checks
masterWhen developing endpoints that perform destructive or sensitive operations (like file deletion or app management), check the
MODEvariable to ensure they are only executed in the intended environment (e.g.,fullmode).if MODE != "full": abort(403) # or raise HTTP(403)How to specialize component behavior via subclassing
masterTo change how a component handles data or user interactions (e.g., saving a star rating to a database instead of just returning a value), you must subclass the component class and override the callback methods.
Instead of using the default callback logic, implement your own logic within the overridden methods to handle the specific business requirements of your application.