def test_model_crud_composite_pk(self):
        """
        MVC CRUD generic-alter datasource where model has composite
        primary keys
        """
        try:
            from urllib import quote
        except ImportError:
            from urllib.parse import quote

        client = self.app.test_client()
        rv = self.browser_login(client, USERNAME_ADMIN, PASSWORD_ADMIN)

        rv = client.post(
            "/model3view/add",
            data=dict(pk1="1", pk2=datetime.datetime(2017, 1, 1), field_string="foo2"),
            follow_redirects=True,
        )

        self.assertEqual(rv.status_code, 200)
        model = (
            self.appbuilder.get_session.query(Model3).filter_by(pk1="1").one_or_none()
        )
        self.assertEqual(model.pk1, 1)
        self.assertEqual(model.pk2, datetime.datetime(2017, 1, 1))
        self.assertEqual(model.field_string, "foo2")

        pk = '[1, {"_type": "datetime", "value": "2017-01-01T00:00:00.000000"}]'
        rv = client.get(f"/model3view/show/{quote(pk)}", follow_redirects=True)
        self.assertEqual(rv.status_code, 200)

        rv = client.post(
            "/model3view/edit/" + quote(pk),
            data=dict(pk1="2", pk2="2017-02-02 00:00:00", field_string="bar"),
            follow_redirects=True,
        )
        self.assertEqual(rv.status_code, 200)

        model = (
            self.appbuilder.get_session.query(Model3)
            .filter_by(pk1="2", pk2="2017-02-02 00:00:00")
            .one_or_none()
        )
        self.assertEqual(model.pk1, 2)
        self.assertEqual(model.pk2, datetime.datetime(2017, 2, 2))
        self.assertEqual(model.field_string, "bar")

        pk = '[2, {"_type": "datetime", "value": "2017-02-02T00:00:00.000000"}]'
        rv = client.get("/model3view/delete/" + quote(pk), follow_redirects=True)
        self.assertEqual(rv.status_code, 200)
        model = self.db.session.query(Model3).filter_by(pk1=2).one_or_none()
        self.assertEqual(model, None)

        # Add it back, then delete via muldelete
        self.appbuilder.get_session.add(
            Model3(pk1=1, pk2=datetime.datetime(2017, 1, 1), field_string="baz")
        )
        self.appbuilder.get_session.commit()
        rv = client.post(
            "/model3view/action_post",
            data=dict(
                action="muldelete",
                rowid=[
                    json.dumps(
                        [
                            "1",
                            {
                                "_type": "datetime",
                                "value": "2017-01-01T00:00:00.000000",
                            },
                        ]
                    )
                ],
            ),
            follow_redirects=True,
        )
        self.assertEqual(rv.status_code, 200)
        model = self.db.session.query(Model3).filter_by(pk1=1).one_or_none()
        self.assertEqual(model, None)

        MVC CRUD generic-alter datasource where model has composite
        primary keys
        
tests/test_mvc.py