Generated model classes Relations API

Use this forum to request new features or suggest modifications to existing features

Generated model classes Relations API

Postby jimmytaker » Sat Dec 24, 2016 2:07 am

Currently API for getting a nested model looks like this:
Code: Select all
int row, bool bLoadFromDatabase = false, const QString & sAppendRelations = QString()

It would be nice to add
Code: Select all
const QSet<int>& rows, bool bLoadFromDatabase = false, const QString & sAppendRelations = QString()

to get all the elements referenced by rows in one model.
jimmytaker
 
Posts: 19
Joined: Fri Dec 23, 2016 11:04 pm

Re: Generated model classes Relations API

Postby qxorm » Sat Dec 24, 2016 1:33 pm

Hello,

Do you mean : you want a result model which include relationships from several rows of your parent model ?

Could you please give an example where it could be useful ?
Just to understand your use case.
qxorm
Site Admin
 
Posts: 481
Joined: Mon Apr 12, 2010 7:45 am

Re: Generated model classes Relations API

Postby jimmytaker » Sun Dec 25, 2016 1:38 am

Hi,

yes, that is exactly what I meant.

My use case:

2 tables: UserGroups and Users. You put UserGroups_model in a QTableView and click on a line (a user group), this calls list_of_user and gives me a User_model with only the users in this group loaded - which is great. Now I enable multiple selection and select 2 groups in the QTableView, and I cannot get a model containing the users in both groups.


Now thinking more abstract. Why limit myself to UserGroups and calling the "list_of_user" relation. All the tools are there to have "list_of_user" in the reflection system, so that I could call something along the lines of:
Code: Select all
qx::QxClassX::invoke("UserGroups_model", "list_of_user", mUserGroups);

I am sure you can see where I am going with this. In pseudocode:
Code: Select all
QTableView* tv = new QTableView;
tv.setModel(new MyModel);
connect(tv->selectionModel(),
      SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
      SLOT(onSelectionChanged(const QItemSelection &, const QItemSelection &)));

void MyClass::onSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
{
   QItemSelectionModel* sm = qobject_cast<QItemSelectionModel*>(sender());
IxModel* model = qobject_cast<IxModel*>(sm->model());
foreach (Relation : model) { //you have the API to get them through model->getClass()->getDataMemberX(), and then check if each datamember is a relation
QTableView* childTV = new QTableView;
childTV->setModel(GetChildModel(selected rows, model, Relation)); //all that is missing to write this function is having the relations added to the reflection system from the generated classes
}
}
jimmytaker
 
Posts: 19
Joined: Fri Dec 23, 2016 11:04 pm

Re: Generated model classes Relations API

Postby jimmytaker » Sun Dec 25, 2016 4:33 am

I guess the JavaScript engine could do this. I will try to post a script here. Manually it is like this:

For a generated:
Code: Select all
   Q_INVOKABLE QObject * users(int row, bool bLoadFromDatabase = false, const QString & sAppendRelations = QString());


What would need to go in the .h and .cpp to register it with QxClassX::invoke is:

mv is my model_view namespace
.h:
Code: Select all
/************************************************************************************************
** File created by QxEntityEditor 1.2.1 (2016/12/23 23:23) : please, do NOT modify this file ! **
************************************************************************************************/

#ifndef _QXPMDB_USER_GRP_MODEL_VIEW_H_
#define _QXPMDB_USER_GRP_MODEL_VIEW_H_

#ifdef _QX_NO_PRECOMPILED_HEADER
#ifndef Q_MOC_RUN
#include "../include/QxPMDB_precompiled_header.model_view.gen.h" // Need to include precompiled header for the generated moc file
#endif // Q_MOC_RUN
#endif // _QX_NO_PRECOMPILED_HEADER

#include "../../C/include/user_grp.gen.h"

namespace mv {

typedef qx::QxModel<user_grp> user_grp_model_base_class;

class QXPMDB_MODEL_VIEW_EXPORT user_grp_model : public user_grp_model_base_class
{

   Q_OBJECT

public:

   user_grp_model(QObject * parent = 0);
   user_grp_model(qx::IxModel * other, QObject * parent);
   virtual ~user_grp_model();

   Q_INVOKABLE QObject * users(int row, bool bLoadFromDatabase = false, const QString & sAppendRelations = QString());

   /* List of properties exposed by the model (2) :
      - user_grp_id
      - name
   */

protected:

   virtual void syncNestedModel(int row, const QStringList & relation);
   virtual void syncAllNestedModel(const QStringList & relation);

};

} // namespace mv

QX_REGISTER_COMPLEX_CLASS_NAME_HPP_QXPMDB_MODEL_VIEW(mv::user_grp_model,
                                QObject, 0, user_grp_model) //Important - QObject here

#endif // _QXPMDB_USER_GRP_MODEL_VIEW_H_


and cpp:
Code: Select all
/************************************************************************************************
** File created by QxEntityEditor 1.2.1 (2016/12/23 23:23) : please, do NOT modify this file ! **
************************************************************************************************/

#include "../include/QxPMDB_precompiled_header.model_view.gen.h"

#include "../include/user_grp.model_view.gen.h"
#include "../include/user.model_view.gen.h"

#include <QxOrm_Impl.h>

QX_REGISTER_COMPLEX_CLASS_NAME_CPP_QXPMDB_MODEL_VIEW(mv::user_grp_model, user_grp_model)

namespace qx {

template <>
void register_class(QxClass<mv::user_grp_model> & t)
{
    t.fct_3<QObject *, int, bool, const QString&>(& mv::user_grp_model::users, "users");
}

} // namespace qx

namespace mv {

user_grp_model::user_grp_model(QObject * parent /* = 0 */) : user_grp_model_base_class(parent) { ; }

user_grp_model::user_grp_model(qx::IxModel * other, QObject * parent) : user_grp_model_base_class(other, parent) { ; }

user_grp_model::~user_grp_model() { ; }

QObject * user_grp_model::users(int row, bool bLoadFromDatabase /* = false */, const QString & sAppendRelations /* = QString() */)
{
   QString sRelation = "users";
   qx::IxModel * pChild = (bLoadFromDatabase ? NULL : this->getChild(row, sRelation));
   if (pChild) { return static_cast<QObject *>(pChild); }

   if ((row < 0) || (row >= this->m_model.count())) { qAssert(false); return NULL; }
   user_grp_model_base_class::type_ptr ptr = this->m_model.getByIndex(row);
   if (! ptr) { qAssert(false); return NULL; }
   long id = ptr->getuser_grp_id();
   user_grp::type_users value = ptr->getusers();

   if (bLoadFromDatabase)
   {
      if (! sAppendRelations.isEmpty() && ! sAppendRelations.startsWith("->") && ! sAppendRelations.startsWith(">>")) { sRelation += "->" + sAppendRelations; }
      else if (! sAppendRelations.isEmpty()) { sRelation += sAppendRelations; }
      user_grp tmp;
      tmp.setuser_grp_id(id);
      this->m_lastError = qx::dao::fetch_by_id_with_relation(sRelation, tmp);
      if (this->m_lastError.isValid()) { return NULL; }
      value = tmp.getusers();
      ptr->setusers(value);
   }

   mv::user_model * pNewChild = NULL;
   pChild = qx::model_view::create_nested_model_with_type(this, QModelIndex(), value, pNewChild);
   if (pChild) { this->insertChild(row, "users", pChild); }
   return static_cast<QObject *>(pChild);
}

void user_grp_model::syncNestedModel(int row, const QStringList & relation)
{
   Q_UNUSED(relation);
   qx::IxModel * pNestedModel = NULL;
   if ((row < 0) || (row >= this->m_model.count())) { return; }
   user_grp_model_base_class::type_ptr ptr = this->m_model.getByIndex(row);
   if (! ptr) { return; }

   pNestedModel = this->getChild(row, "users");
   if (pNestedModel)
   {
      this->syncNestedModelRecursive(pNestedModel, relation);
      user_grp::type_users value;
      qx::model_view::sync_nested_model(pNestedModel, value);
      ptr->setusers(value);
   }
}

void user_grp_model::syncAllNestedModel(const QStringList & relation)
{
   if (this->m_lstChild.count() <= 0) { return; }
   for (long l = 0; l < this->m_model.count(); l++)
   { this->syncNestedModel(static_cast<int>(l), relation); }
}

} // namespace mv


Which compiles successfully and I can then do:
Code: Select all
   user_grp_model* mgrp = new user_grp_model;
   mgrp->qxFetchAll();
   mgrp->users(0);
   std::vector<boost::any> v;
   v.resize(3);
   QString s;
   v[0] = 0;
   v[1] = false;
   v[2] = (const QString&) s;
   qx_bool bInvokeOk = QxClassX::invoke("user_grp_model", "users", mgrp, v);
   // qx_bool bInvokeOk = QxClassX::invoke("user_grp_model", "users", mgrp, "0"); //how do you use the QString params interface in this case that some values have default values? I tried a few variations, but none worked.
   qAssert(bInvokeOk);
jimmytaker
 
Posts: 19
Joined: Fri Dec 23, 2016 11:04 pm

Re: Generated model classes Relations API

Postby jimmytaker » Sun Dec 25, 2016 8:01 am

OK, I got it done. Quite a few changes necessary, but it is working:

1. in QxEntityEditor, I export to C++ using the qxPersistable template. Not relevant for this case I think.

2. For the ModelViewExport, I created a new template and a .js. Here they are:

h template:
Code: Select all
@@COMMENT_FILE_CREATED_BY@@

#ifndef @@INCLUDE_MACRO_GUARD@@
#define @@INCLUDE_MACRO_GUARD@@

#ifdef _QX_NO_PRECOMPILED_HEADER
#ifndef Q_MOC_RUN
#include "../include/@@PROJECT_NAME@@_precompiled_header.model_view.gen.h" // Need to include precompiled header for the generated moc file
#endif // Q_MOC_RUN
#endif // _QX_NO_PRECOMPILED_HEADER

@@LIST_INCLUDE_DEPENDENCIES@@

@@NAMESPACE_BEGIN@@

typedef qx::QxModel<@@CLASS_NAME@@> @@MODEL_BASE_CLASS@@;

class @@CLASS_MACRO_EXPORT@@ @@CLASS_NAME@@_model : public @@MODEL_BASE_CLASS@@
{

   Q_OBJECT

public:

   @@CLASS_NAME@@_model(QObject * parent = 0);
   @@CLASS_NAME@@_model(qx::IxModel * other, QObject * parent);
   virtual ~@@CLASS_NAME@@_model();

@@LIST_RELATIONS_Q_INVOKABLE@@

@@COMMENT_LIST_PROPERTIES_EXPOSED_BY_MODEL@@

@@SYNC_NESTED_MODEL@@

};

@@NAMESPACE_END@@

@@MACRO_QX_REGISTER_CLASS_HPP@@(@@CUSTOM_NAMESPACE@@@@FULL_CLASS_NAME@@_model,
    QObject, @@CLASS_VERSION@@, @@FULL_CLASS_NAME@@_model)

#endif // @@INCLUDE_MACRO_GUARD@@



cpp template:
Code: Select all
@@COMMENT_FILE_CREATED_BY@@

#include "../include/@@PROJECT_NAME@@_precompiled_header.model_view.gen.h"

@@LIST_INCLUDE_DEPENDENCIES_CPP@@

#include <QxOrm_Impl.h>

@@MACRO_QX_REGISTER_CLASS_CPP@@(@@CUSTOM_NAMESPACE@@@@FULL_CLASS_NAME@@_model,
    @@FULL_CLASS_NAME@@_model)

namespace qx {

template <>
void register_class(QxClass<@@CUSTOM_NAMESPACE@@@@FULL_CLASS_NAME@@_model> & t)
{@@CUSTOM_QX_REGISTER_CLASS_RELATIONS@@}

} // namespace qx

@@NAMESPACE_BEGIN@@

@@CLASS_NAME@@_model::@@CLASS_NAME@@_model(QObject * parent /* = 0 */) : @@MODEL_BASE_CLASS@@(parent) { ; }

@@CLASS_NAME@@_model::@@CLASS_NAME@@_model(qx::IxModel * other, QObject * parent) : @@MODEL_BASE_CLASS@@(other, parent) { ; }

@@CLASS_NAME@@_model::~@@CLASS_NAME@@_model() { ; }

@@IMPLEMENT_LIST_RELATIONS_Q_INVOKABLE@@

@@IMPLEMENT_SYNC_NESTED_MODEL@@

@@NAMESPACE_END@@


tried to attach, but the forum does not allow. js files...
Code: Select all
({

customProcess : function(params)
{
   try
   {
      var action = params[5];
      var ns = "mv::"; //TODO how to get the namespace?
      if (action == "CUSTOM_NAMESPACE")
         return ns;
      if (action == "CUSTOM_QX_REGISTER_CLASS_RELATIONS")
      {
         var entity_id = params[13];
         var entity_details = helper.getEntityDetails(entity_id);
         var entity_list_of_properties_id = ((entity_details.length > 0) ? entity_details[10] : "");
         var entity_list_of_properties_array = entity_list_of_properties_id.split("|");
         var r = "\n";
         for (var idx = 0; idx < entity_list_of_properties_array.length; idx++)
         {
            var property_id = entity_list_of_properties_array[idx];
            var property_details = helper.getPropertyDetails(property_id);
            if (property_details[27] == 1) //property_is_relationship
               r += "\tt.fct_3<QObject *, int, bool, const QString&>(& " + ns + entity_details[2] + "_model::" +
                  property_details[2] + ", \"" + property_details[2] + "\");\n"
         }         
         return r;
      }
      // quit with 'params[0]' means : not change the default behaviour
      return params[0];
   }
   catch (err)
   { return ("[CustomScriptError] an unexpected error occurred : " + err); }
}

})


There are 2 problems with it:
1. I cannot get the namespace in the js, so I hardcoded my namespace "mv::". How do you get the namespace?
2. Your macro @@IMPLEMENT_LIST_RELATIONS_Q_INVOKABLE@@ is now incompatible with my patch for the empty line in the model.
This generated line now always fails, and should be changed.
Code: Select all
   if ((row < 0) || (row >= this->m_model.count())) { qAssert(false); return NULL; } //this is the old
   if ((row < 0) || (row >= this->m_model.count())) { if (row >= this->m_model.count() + (int) getShowEmptyLine()) qAssert(false); return NULL; } //this is the adapted

Maybe you can give me the script that implements this macro, so that I can create a custom macro to substitute it and continue my work? Right now I am substituting each occurence in the generated code.

And to use this a snippet from my testing code:
Code: Select all
      long lCount = (model->getClass()->getDataMemberX() ? model->getClass()->getDataMemberX()->count() : 0);
      if (model->getClass()->getDataMemberX())
      {
         m_pAdminTool->AddLevel(); //ads a holding widget with layout
         std::vector<boost::any> v(3, true);
         QString s;
         v[0] = l.first().row();
         v[2] = (const QString&) s;
         for (long i = 0; i < lCount; i++)
         {
            IxDataMember * p = model->getClass()->getDataMemberX()->get(i); if (!p) { continue; }
            if (!p->getSqlRelation()) { continue; }
            TableViewWithDel* tv = new TableViewWithDel(m_pAdminTool);
            m_pAdminTool->m_lwViews.last()->layout()->addWidget(tv); //gets the last added holding widget (in AddLevel() above)
            boost::any r;
            qx_bool bInvokeOk = QxClassX::invoke("mv::" + model->getClass()->getKey() + "_model",
               p->getKey(), model, v, &r);
            qAssert(bInvokeOk);
            tv->SetModel(qobject_cast<IxModel*>(boost::any_cast<QObject*>(r))); //now if you really get rid of boost for the next release, maybe this casting frenzy can go away...
         }
      }

jimmytaker
 
Posts: 19
Joined: Fri Dec 23, 2016 11:04 pm


Return to QxEntityEditor - Feature request

Who is online

Users browsing this forum: No registered users and 2 guests