================ Row Model CRUD ================ In addition to the main model CRUD for a master view, it's possible to also expose CRUD features for "row model" data. The basic idea is to work with "secondary" records which pertain to a given "primary" record. For instance a :term:`batch` is a special kind of record which has data rows. A minimal version of this feature is when the secondary rows are made visible in a grid, when viewing a primary record. Beyond that, full row CRUD may or may not be implemented, depending on the need. As an example let's say you have Authors and Books, and each already has a master view setup. It'd be nice to see all the books for an author, when viewing the author. But since there is already a master view for books, we don't need to add CRUD for books within the authors view. This shows how to go about that. Note the "view row URL" is customized so it points to the books master view:: from wuttaweb.views import MasterView from poser.db.model import Author, Book class AuthorView(MasterView): model_class = Author # .... has_rows = True row_model_class = Book # nb. omit author column since that's the parent record row_grid_columns = [ "book_title", "page_count", "publisher_name", "date_published", ] # sort books by title rows_sort_defaults = "book_title" # must declare how to get row grid data for a parent record def get_row_grid_data(self, author): session = self.Session() # nb. we do *not* sort this query, since user can click # column headers to control sorting separately. (in certain # other scenarios e.g. where data is not a query, you may # want to pre-sort the row data.) return session.query(Book).filter(Book.author == author) # also must declare how to get the parent record for a given row def get_row_parent(self, book): return book.author # configuring a row grid works the same, just a different method name def configure_row_grid(self, grid): super().configure_row_grid(grid) # add view hyperlink for title column grid.set_link("book_title") # add 'view' action, if user is allowed access if self.request.has_perm("books.view"): grid.add_action("view", icon="eye", url=self.get_row_action_url_view) # view URL generator for a given row def get_row_action_url_view(self, book, i): return self.request.route_url("books.view", uuid=book.uuid) Attributes to Override ---------------------- Here are the relevant attributes you may need to set when implementing this feature: **Main Toggle** * :attr:`~wuttaweb.views.master.MasterView.has_rows` **Model** * :attr:`~wuttaweb.views.master.MasterView.row_model_class` * :attr:`~wuttaweb.views.master.MasterView.row_model_name` * :attr:`~wuttaweb.views.master.MasterView.row_model_key` * :attr:`~wuttaweb.views.master.MasterView.row_model_title` * :attr:`~wuttaweb.views.master.MasterView.row_model_title_plural` * :attr:`~wuttaweb.views.master.MasterView.rows_title` **Feature Flags** * :attr:`~wuttaweb.views.master.MasterView.rows_creatable` * :attr:`~wuttaweb.views.master.MasterView.rows_viewable` * :attr:`~wuttaweb.views.master.MasterView.rows_editable` * :attr:`~wuttaweb.views.master.MasterView.rows_deletable` **Grid Config** * :attr:`~wuttaweb.views.master.MasterView.row_grid_columns` * :attr:`~wuttaweb.views.master.MasterView.rows_sortable` * :attr:`~wuttaweb.views.master.MasterView.rows_sort_on_backend` * :attr:`~wuttaweb.views.master.MasterView.rows_sort_defaults` **Form Config** * :attr:`~wuttaweb.views.master.MasterView.row_form_fields` Methods to Override ------------------- Here are some methods you may need to override for this feature: **Fetching Data** * :meth:`~wuttaweb.views.master.MasterView.get_row_grid_data()` * :meth:`~wuttaweb.views.master.MasterView.get_row_instance()` * :meth:`~wuttaweb.views.master.MasterView.get_row_parent()` **Grid Handling** * :meth:`~wuttaweb.views.master.MasterView.make_row_model_grid()` * :meth:`~wuttaweb.views.master.MasterView.configure_row_grid()` **Form Handling** * :meth:`~wuttaweb.views.master.MasterView.make_row_model_form()` * :meth:`~wuttaweb.views.master.MasterView.configure_row_form()` * :meth:`~wuttaweb.views.master.MasterView.objectify()` * :meth:`~wuttaweb.views.master.MasterView.persist()` * :meth:`~wuttaweb.views.master.MasterView.delete_row_instance()` .. note:: Row CRUD uses the same ``objectify()`` and ``persist()`` methods which are used for the main model CRUD. You can override if the logic needs to differ between those. **Creating** * :meth:`~wuttaweb.views.master.MasterView.create_row_save_form()` * :meth:`~wuttaweb.views.master.MasterView.redirect_after_create_row()` **Editing** * :meth:`~wuttaweb.views.master.MasterView.edit_row_save_form()` * :meth:`~wuttaweb.views.master.MasterView.redirect_after_edit_row()` **Deleting** * :meth:`~wuttaweb.views.master.MasterView.make_delete_row_form()` * :meth:`~wuttaweb.views.master.MasterView.save_delete_row_form()` * :meth:`~wuttaweb.views.master.MasterView.redirect_after_delete_row()`