Our choice for the constraint that every number between 1 and 9 must be used exactly once is essentially the same as the code I wrote last year.
Last year I was trying to find a formulation with these decision variables that is linear except for integrality constraint (i.e. the constraint that the decision variables have to be integer). I tried all the ideas we discussed (e.g. abs(a_i - a_j ) positive) and decided that this approach does not lead to a linear formulation. I seem to have remembered "it doesn't work" - hence my hesitation to consider the approach. I forgot that it works just fine if a nonlinear formulation is acceptable.
Note that you can express the problem as a binary integer linear programming problem (i.e. one that is linear except for the constraint that all decision variables are either 0 or 1). See if you can figure this out! (Start with determining the decision variables should represent.)
BTW, I think here we have a rare example where it is reasonable to loop (in Python) over the rows of an array in order to form our constraints. However, you should pre-allocate your array (e.g. np.zeros) and then modify it rather than creating it by appending row after row.