Extend an existing Blok

In this chapter we will discover how to add a field to the ̀ `Address`` model.

Note: You can clone III-01_external-blok branch from AnyBlok/anyblok-book-examples repo to get ready to start with this chapter.

Clean up examples

Before extending the anyblok_address in the next chapter let's do some clean-up and remove examples generated by cookiecutter template in the previous section

  • Start by removing the following files:
rm rooms_booking/room/model.py \
   rooms_booking/room/tests/test_model.py \
   rooms_booking/room/tests/test_pyramid.py \
   rooms_booking/room/views.py
  • Remove pyramid configurations:
-    @classmethod
-    def pyramid_load_config(cls, config):
-        config.add_route("root", "/")
-        config.add_route("example_list", "/example")
-        config.add_route("example", "/example/{id}")
-        config.scan(cls.__module__ + '.views')

Extend the Adress model

We need to add an access information field on each address. Let's add two new files in your project:

  • rooms_booking/room/address.py: We are going to extend Address model in this python module
  • rooms_booking/room/tests/test_address.py: here we are going to test our additional features on this model.

In order to import the address python module (the address.py file), in an Anyblok project you must do it in the import declaration module method and the reload declaration module method in the Blok class declaration.

    # file: ``/rooms_booking/room/__init__.py``
    # class: Room(Blok)

    @classmethod
    def import_declaration_module(cls):
        """Python module to import bloks in the given order at start-up
        """
        from . import address  # noqa

    @classmethod
    def reload_declaration_module(cls, reload):
        """Python module to import while reloading server (i.e. when
        adding Blok at runtime)
        """
        from . import address  # noqa
        reload(address)

Those methods are called when the registry is created or reloaded for a given database.

In our case if the Room blok state is installed in a database address.py will be imported. Otherwise all our code will be present but not used because the blok is uninstalled.

Note: When you need to know if a blok is installed, launch an anyblok_interpreter and query registry.System.Blok

In [1]: registry.System.Blok.query().all()
Out[1]: 
[anyblok-core (installed),
 auth (uninstalled),
 auth-password (uninstalled),
 authorization (uninstalled),
 model_authz (uninstalled),
 room (installed),
 anyblok-test (uninstalled)]

In [2]:

In the same file, adapt the update method. At the moment we are not going to do anything here while updating or installing the Room Blok:

    # file: ``/rooms_booking/room/__init__.py``
    # class: Room(Blok)

    def update(self, latest_version):
         """Update blok"""
         # if we install this blok in the database we add a new record
        if not latest_version:
            self.install()

    def install(self):
        pass

(loving TDD) Before you start coding, add the following unit tests. This way, we can test that we can add some access information on an Address:

# file: rooms_booking/room/tests/test_address.py
from anyblok.tests.testcase import BlokTestCase


class TestAddress(BlokTestCase):
    """ Test extended registry.Address model"""

    def test_create_address(self):
        address_count = self.registry.Address.query().count()
        queens_college_address = self.registry.Address.insert(
            first_name="The Queen's College",
            last_name="University of oxford",
            street1="High Street",
            zip_code="OX1 4AW",
            city="Oxford",
            country="GBR",
            access="Kick the door to open it!"
        )
        self.assertEqual(
            self.registry.Address.query().count(),
            address_count + 1
        )
        self.assertEqual(
            queens_college_address.access,
            "Kick the door to open it!"
        )

If you run this test you'll probably notice the following error as we haven't created the access field on our Address model yet.

make setup-tests
make test
[...]
TypeError: 'access' is an invalid keyword argument for ModelAddress
[...]
----------------------------------------------------------------------
Ran 1 test in 0.780s

FAILED (errors=1)

It's time to extend the Address model to make our previous test successful:

# file: rooms_booking/room/address.py

from anyblok import Declarations
from anyblok.column import String

Model = Declarations.Model
register = Declarations.register


@register(Model)
class Address:
    """Extend and specialize anyblok_address blok"""
    access = String(label="Access information")

As you can see, it's simple to overload a model you only have to use a decorator @register(Model) on your class using the model name (here Address) and declare a new field access using a String column.

If you're wondering which columns types are supported you may read columns types in the documentation.

You can add other column type support not provided by AnyBlok Core like AnyBlok postgres package that provide JsonB support while using Postgresql database.

Your test should pass but before any further step, you have to update your blok in order to create missing fields in your test database (or re-create the database from scratch using make setup-tests):

# Be sure your project python package is installed in develop mode
pip install -e .
# Update the database model to add the access field
anyblok_updatedb -c app.test.cfg --update-bloks room
# Run unit test
make test

results matching ""

    No results matching ""