<div dir="ltr"><div class="gmail_default" style="font-family:tahoma,sans-serif"><br></div><div class="gmail_extra"><br><div class="gmail_quote">On 3 May 2018 at 13:05, Phil Thompson <span dir="ltr"><<a href="mailto:phil@riverbankcomputing.com" target="_blank">phil@riverbankcomputing.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><span class="gmail-">On 3 May 2018, at 12:25 pm, J Barchan <<a href="mailto:jnbarchan@gmail.com">jnbarchan@gmail.com</a>> wrote:<br>
> <br>
> ​​<br>
> I am finding (in PyQt 5.7 at least) that sub-classing QSqlQueryModel and overriding its data() method produces an incorrect result when the value retrieved from a MySQL database is NULL.​<br>
> <br>
> Full details are in <a href="https://forum.qt.io/topic/90363/inexplicable-qsqlquerymodel-handling-of-null-value" rel="noreferrer" target="_blank">https://forum.qt.io/topic/<wbr>90363/inexplicable-<wbr>qsqlquerymodel-handling-of-<wbr>null-value</a>, and particularly post # <a href="https://forum.qt.io/topic/90363/inexplicable-qsqlquerymodel-handling-of-null-value" rel="noreferrer" target="_blank">https://forum.qt.io/topic/<wbr>90363/inexplicable-<wbr>qsqlquerymodel-handling-of-<wbr>null-value</a>.  Nobody has tried it in C++ for me to date to verify, but I'm suspecting this might be a PyQt bug?<br>
> <br>
> Briefly:<br>
> My SELECT query returns a column which is NULLable, and has NULL as its value.  Where I expect "blank" as the end value, I actually get, for example, 0 if the column type is int or '' if the type is string, etc.<br>
> <br>
> This is when I sub-class QSqlQueryModel.  If all I have is:<br>
> class DBQueryModel(QtSql.<wbr>QSqlQueryModel):<br>
>     def __init__(self, parent=None):<br>
>         super().__init__(parent)<br>
> I get the "NULL"/"blank".  However, as soon as I add just:<br>
> def data(self, index: QtCore.QModelIndex, role=QtCore.Qt.DisplayRole) -> typing.Any:<br>
>     return super().data(index, role)<br>
> I get those values instead of NULL.<br>
> <br>
> Note that my override is based on the Qt definition of the method at <a href="http://doc.qt.io/qt-5/qsqlquerymodel.html#data" rel="noreferrer" target="_blank">http://doc.qt.io/qt-5/<wbr>qsqlquerymodel.html#data</a>:<br>
> QVariant QSqlQueryModel::data(const QModelIndex &item, int role = Qt::DisplayRole) const<br>
> <br>
> Note that the default for role is Qt::DisplayRole.  However, in QtSql.py I see:<br>
> def data(self, QModelIndex, role=None): # real signature unknown; restored from __doc__<br>
>     """ data(self, QModelIndex, role: int = Qt.DisplayRole) -> Any """<br>
>     pass<br>
>  You will notice that the comment shows the default should be Qt.DisplayRole, but the declaration defaults it to None instead.<br>
<br>
</span>What is QtSql.py?<br>
<br>
If you want to know the signature of a method pss it to help().<br>
<span class="gmail-"><br>
> I don't know enough to be sure, but would that be the underlying cause of the unexpected behaviour?<br>
> <br>
> FWIW, I have tried making my override be:<br>
> def data(self, index: QtCore.QModelIndex, role=None)<br>
> instead, but same bad behaviour.<br>
> <br>
> 1. Is this indeed a bug in PyQt, and the cause of my issue?<br>
<br>
</span>No and no.<br>
<div class="gmail-HOEnZb"><div class="gmail-h5"><br>
> 2. If so, I presume you (Phil!) will be kind enough to fix.  However, for my part I am stuck with PyQt 5.7 for the foreseeable future.  If the fix is indeed to change code in the latest/next release, is there anything I can do in existing code (my override) to make it work in 5.7, as a workaround?  (in real code I need the override, as I do other processing)<br>
> <br>
> My coding has come to halt as I cannot proceed without a fix.  So I should be obliged for any early response as to whether this is the cause of my woes.  I do realise PyQt support/fixes are quite voluntary, and so thank whoever in advance!<br>
<br>
</div></div><span class="gmail-HOEnZb"><font color="#888888">Phil</font></span></blockquote></div></div><div class="gmail_extra"><br></div><div class="gmail_extra"><div style="font-family:tahoma,sans-serif" class="gmail_default">​For <span style="font-family:monospace,monospace">QtSql.py</span>:​</div></div><div class="gmail_extra"><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default">​Hmm​, I had not realised.  I use PyCharm as my IDE.  From there, while I am coding, I can click on anything PyQt and ask for "Go to definition/declaration".  The editor then 9in this case) opens me up into a file named <span style="font-family:monospace,monospace">QtSql.py</span>, showing me in this case [extract]:</div><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="font-family:tahoma,sans-serif" class="gmail_default"><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:9pt"><span style="color:rgb(0,0,128);font-weight:bold">class </span>QSqlQueryModel(__PyQt5_QtCore.QAbstractTableModel):</pre>...</div><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default"><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:9pt"><span style="color:rgb(0,0,128);font-weight:bold">def </span>columnCount(<span style="color:rgb(148,85,141)">self</span>, parent=<span style="color:rgb(0,0,128);font-weight:bold">None</span>, *args, **kwargs): <span style="color:rgb(128,128,128);font-style:italic"># real signature unknown; NOTE: unreliably restored from __doc__ <br></span><span style="color:rgb(128,128,128);font-style:italic">    """ columnCount(self, parent: QModelIndex = QModelIndex()) -> int """<br></span><span style="color:rgb(128,128,128);font-style:italic">    </span><span style="color:rgb(0,0,128);font-weight:bold">pass<br></span><span style="color:rgb(0,0,128);font-weight:bold"><br></span><span style="color:rgb(0,0,128);font-weight:bold">def </span>data(<span style="color:rgb(148,85,141)">self</span>, QModelIndex, role=<span style="color:rgb(0,0,128);font-weight:bold">None</span>): <span style="color:rgb(128,128,128);font-style:italic"># real signature unknown; restored from __doc__<br></span><span style="color:rgb(128,128,128);font-style:italic">    """ data(self, QModelIndex, role: int = Qt.DisplayRole) -> Any """<br></span><span style="color:rgb(128,128,128);font-style:italic">    </span><span style="color:rgb(0,0,128);font-weight:bold">pass<br></span><span style="color:rgb(0,0,128);font-weight:bold"><br></span><span style="color:rgb(0,0,128);font-weight:bold">def </span>endInsertColumns(<span style="color:rgb(148,85,141)">self</span>): <span style="color:rgb(128,128,128);font-style:italic"># real signature unknown; restored from __doc__<br></span><span style="color:rgb(128,128,128);font-style:italic">    """ endInsertColumns(self) """<br></span><span style="color:rgb(128,128,128);font-style:italic">    </span><span style="color:rgb(0,0,128);font-weight:bold">pass<br></span></pre></div></blockquote><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default">etc.  I had <i>assumed</i> this was a file supplied with PyQt.  I guess now it's "generated on the fly" by PyCharm (I see its path is in a PyCharm temporary directory).  At other times, it might open, say, <span style="font-family:monospace,monospace">/usr/lib/python3/dist-packages/PyQt5/QtCore.pyi</span>, which I think is a file you supply.  Oh, at the head of this <span style="font-family:monospace,monospace">QtSql.py</span> I see:</div><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default"><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:9pt"><span style="color:rgb(128,128,128);font-style:italic"># encoding: utf-8<br></span><span style="color:rgb(128,128,128);font-style:italic"># module PyQt5.QtSql<br></span><span style="color:rgb(128,128,128);font-style:italic"># from /usr/lib/python3/dist-packages/PyQt5/<a href="http://QtSql.cpython-35m-x86_64-linux-gnu.so">QtSql.cpython-35m-x86_64-linux-gnu.so</a><br></span><span style="color:rgb(128,128,128);font-style:italic"># by generator 1.145<br></span><span style="color:rgb(128,128,128);font-style:italic"># no doc</span></pre><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default">​You'll probably understand all this better than I!​</div><br></div><div class="gmail_extra"><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default">​For my problem:​</div><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default">I think I now understand better why it's not a PyQt method definition issue.</div><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default">However, from the linked Qt forum discussion, I'm stuck between a rock & a hard place, because the only help I'm getting is that it might be a PyQt issue.  I do not have C++ to try that out.  So, I wonder if I might ask you if you can make any suggestion as to the cause, even if it is not a PyQt issue, given that you are familiar with Qt at least?</div><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default">To summarise my problem as briefly as possible:</div><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default">1.<br></div><div style="font-family:tahoma,sans-serif" class="gmail_default">I start with:</div><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default"><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:9pt">model = QtSql.QSqlQueryModel(<span style="color:rgb(148,85,141)">self</span>)<br>model.setQuery(<span style="color:rgb(0,128,128);font-weight:bold">"SELECT LandlordNo, SMTPAccountId </span><span style="color:rgb(0,128,128);font-weight:bold">FROM landlords WHERE SMTPAccountId IS NULL")</span><br></pre><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:9pt"># or plain "SELECT NULL AS SMTPAccountId", to eliminate anything about the column definition being an issue<br></pre><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:9pt">rowCount = model.rowCount()<br><span style="color:rgb(0,0,128);font-weight:bold">if </span>rowCount > <span style="color:rgb(0,0,255)">0</span>:<br>    rec = model.record(<span style="color:rgb(0,0,255)">0</span>)<br>    field = rec.field(<span style="color:rgb(0,128,128);font-weight:bold">"SMTPAccountId"</span>)<br>    <span style="color:rgb(128,128,128)">isn </span>= field.isNull()<br>  </pre><span style="font-family:monospace,monospace">SMTPAccointId</span> returns <span style="font-family:monospace,monospace">NULL</span> from MySQL.  <i>At this point <span style="font-family:monospace,monospace">field.isNull()</span> correctly returns <span style="font-family:monospace,monospace">True</span>.</i><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default">2.</div><div style="font-family:tahoma,sans-serif" class="gmail_default">I sub-class QSqlQueryModel, and use that, with quite simply, exactly:</div><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default"><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:9pt"><span style="color:rgb(0,0,128);font-weight:bold">class </span>DBQueryModel(QtSql.QSqlQueryModel):<br>    <span style="color:rgb(0,0,128);font-weight:bold">def </span><span style="color:rgb(178,0,178)">__init__</span>(<span style="color:rgb(148,85,141)">self</span>, parent=<span style="color:rgb(0,0,128);font-weight:bold">None</span>):<br>        <span style="color:rgb(0,0,128)">super</span>().<span style="color:rgb(178,0,178)">__init__</span>(parent)</pre>and use that sub-class in place of <span style="font-family:monospace,monospace">QSqlQueryModel</span>:   <span style="font-family:monospace,monospace">model = DBQueryModel(<span style="color:rgb(148,85,141)">self</span>)</span><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default">​<i>And it this point ​point <span style="font-family:monospace,monospace">field.isNull()</span> </i>still<i> correctly returns <span style="font-family:monospace,monospace">True</span>.</i></div><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default">3.</div><div style="font-family:tahoma,sans-serif" class="gmail_default">Then I add <i>just exactly this</i> to my sub-class:</div><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default"><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:9pt"><span style="color:rgb(0,0,128);font-weight:bold">def </span>data(<span style="color:rgb(148,85,141)">self</span>, index: QtCore.QModelIndex, role=QtCore.Qt.DisplayRole) -> typing.Any:<br>    <span style="color:rgb(0,0,128);font-weight:bold">return </span><span style="color:rgb(0,0,128)">super</span>().data(index, role)</pre>(I've also tried <span style="font-family:monospace,monospace">role=None</span>) You can see that simply calls the base class method.  <i>But now <span style="font-family:monospace,monospace">field.isNull()</span> returns <span style="font-family:monospace,monospace">False</span>!!</i>  The application sees <span style="font-family:monospace,monospace">0</span> instead of NULL for the value (<span style="font-family:monospace,monospace">SMTPAccountId</span> is declared <span style="font-family:monospace,monospace">INT NULL</span>), or <span style="font-family:monospace,monospace">''</span> if I use <span style="font-family:monospace,monospace">SELECT NULL AS SMTPAccountId</span> so it counts as string.<br></div><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default">This leaves me completely stumped.  I have no idea where the problem is (there shouldn't be a problem!).  I have to sub-class the <span style="font-family:monospace,monospace">data()</span> method for other purposes, but then it handles NULL (only) incorrectly.</div><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default">Would you have any idea what is going on here?  My thanks in advance.<br></div><br clear="all"><br>-- <br><div class="gmail_signature"><div dir="ltr"><div><div dir="ltr"><div><span style="font-family:tahoma,sans-serif">Kindest,</span></div><div><span style="font-family:tahoma,sans-serif">Jonathan</span></div></div></div></div></div>
</div></div>