diff --git a/.editorconfig b/.editorconfig
deleted file mode 100644
index c05d96d..0000000
--- a/.editorconfig
+++ /dev/null
@@ -1,15 +0,0 @@
-# https://editorconfig.org/
-
-root = true
-
-[*]
-indent_style = space
-indent_size = 4
-insert_final_newline = true
-trim_trailing_whitespace = true
-end_of_line = lf
-charset = utf-8
-max_line_length = 119
-
-[*.{yml,yaml}]
-indent_size = 2
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
deleted file mode 100644
index a9913b3..0000000
--- a/.github/workflows/codeql-analysis.yml
+++ /dev/null
@@ -1,67 +0,0 @@
-# For most projects, this workflow file will not need changing; you simply need
-# to commit it to your repository.
-#
-# You may wish to alter this file to override the set of languages analyzed,
-# or to provide custom queries or build logic.
-#
-# ******** NOTE ********
-# We have attempted to detect the languages in your repository. Please check
-# the `language` matrix defined below to confirm you have the correct set of
-# supported CodeQL languages.
-#
-name: "CodeQL"
-
-on:
-  push:
-    branches: [ dev ]
-  pull_request:
-    # The branches below must be a subset of the branches above
-    branches: [ dev ]
-  schedule:
-    - cron: '40 13 * * 3'
-
-jobs:
-  analyze:
-    name: Analyze
-    runs-on: ubuntu-latest
-
-    strategy:
-      fail-fast: false
-      matrix:
-        language: [ 'python' ]
-        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
-        # Learn more:
-        # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
-
-    steps:
-    - name: Checkout repository
-      uses: actions/checkout@v2
-
-    # Initializes the CodeQL tools for scanning.
-    - name: Initialize CodeQL
-      uses: github/codeql-action/init@v1
-      with:
-        languages: ${{ matrix.language }}
-        # If you wish to specify custom queries, you can do so here or in a config file.
-        # By default, queries listed here will override any specified in a config file.
-        # Prefix the list here with "+" to use these queries and those in the config file.
-        # queries: ./path/to/local/query, your-org/your-repo/queries@main
-
-    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
-    # If this step fails, then you should remove it and run the build manually (see below)
-    - name: Autobuild
-      uses: github/codeql-action/autobuild@v1
-
-    # ℹī¸ Command-line programs to run using the OS shell.
-    # 📚 https://git.io/JvXDl
-
-    # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines
-    #    and modify them (or add more) to build your code if your project
-    #    uses a compiled language
-
-    #- run: |
-    #   make bootstrap
-    #   make release
-
-    - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@v1
diff --git a/.github/workflows/devskim.yml b/.github/workflows/devskim.yml
deleted file mode 100644
index b08b9ef..0000000
--- a/.github/workflows/devskim.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-# This workflow uses actions that are not certified by GitHub.
-# They are provided by a third-party and are governed by
-# separate terms of service, privacy policy, and support
-# documentation.
-
-name: DevSkim
-
-on:
-  push:
-    branches: [ dev, master ]
-  pull_request:
-    branches: [ dev ]
-  schedule:
-    - cron: '29 14 * * 3'
-
-jobs:
-  lint:
-    name: DevSkim
-    runs-on: ubuntu-20.04
-    permissions:
-      actions: read
-      contents: read
-      security-events: write
-    steps:
-      - name: Checkout code
-        uses: actions/checkout@v3
-
-      - name: Run DevSkim scanner
-        uses: microsoft/DevSkim-Action@v1
-        
-      - name: Upload DevSkim scan results to GitHub Security tab
-        uses: github/codeql-action/upload-sarif@v2
-        with:
-          sarif_file: devskim-results.sarif
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 1377bf2..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,14 +0,0 @@
-*.py[co]
-*.sw[a-z]
-*.orig
-*~
-.DS_Store
-Thumbs.db
-
-*.egg-info
-*.dll
-tests/local_settings.py
-
-# Virtual Env
-/venv/
-.idea/
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
deleted file mode 100644
index 6257f2e..0000000
--- a/CODE_OF_CONDUCT.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Microsoft Open Source Code of Conduct
-
-This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
-
-Resources:
-
-- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
-- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
-- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 568fd40..0000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# Contributing
-
-## How to contribute
-
-### Run unit tests
-After changes made to the project, it's a good idea to run the unit tests before making a pull request. 
-
-
-1. **Run SQL Server**  
-   Make sure you have SQL Server running in your local machine. 
-   Download and install SQL Server [here](https://www.microsoft.com/en-us/sql-server/sql-server-downloads), or you could use docker. Change `testapp/settings.py` to match your SQL Server login username and password.  
-   
-   ```
-   docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=MyPassword42' -p 1433:1433 -d mcr.microsoft.com/mssql/server:2019-latest
-   ```
-2. **Clone Django**   
-   In `mssql-django` folder. 
-   ```
-   # Install your local mssql-django
-   pip install -e .
-
-   # The unit test suite are in `Django` folder, so we need to clone it
-   git clone https://github.com/django/django.git --depth 1
-   ```
-   
-3. **Install Tox**  
-   ```
-   # we use `tox` to run tests and install dependencies
-   pip install tox
-   ``` 
-4. **Run Tox**  
-   ```
-   # eg. run django 3.1 tests with Python 3.7
-   tox -e py37-django31
-   ```
-
----
-This project welcomes contributions and suggestions.  Most contributions require you to agree to a
-Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
-the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
-
-When you submit a pull request, a CLA bot will automatically determine whether you need to provide
-a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
-provided by the bot. You will only need to do this once across all repos using our CLA.
-
-This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
-For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
-contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
\ No newline at end of file
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..af05fd2
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,314 @@
+Metadata-Version: 2.1
+Name: mssql-django
+Version: 1.1.3
+Summary: Django backend for Microsoft SQL Server
+Home-page: https://github.com/microsoft/mssql-django
+Author: Microsoft
+Author-email: opencode@microsoft.com
+License: BSD
+Project-URL: Release Notes, https://github.com/microsoft/mssql-django/releases
+Keywords: django
+Platform: UNKNOWN
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Framework :: Django
+Classifier: Operating System :: POSIX :: Linux
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Framework :: Django :: 3.2
+Classifier: Framework :: Django :: 4.0
+Description-Content-Type: text/markdown
+License-File: LICENSE.txt
+License-File: NOTICE.md
+
+# SQL Server backend for Django
+
+Welcome to the MSSQL-Django 3rd party backend project!
+
+*mssql-django* is a fork of [django-mssql-backend](https://pypi.org/project/django-mssql-backend/). This project provides an enterprise database connectivity option for the Django Web Framework, with support for Microsoft SQL Server and Azure SQL Database.
+
+We'd like to give thanks to the community that made this project possible, with particular recognition of the contributors: OskarPersson, michiya, dlo and the original Google Code django-pyodbc team. Moving forward we encourage partipation in this project from both old and new contributors!
+
+We hope you enjoy using the MSSQL-Django 3rd party backend.
+
+## Features
+
+-  Supports Django 3.2 and 4.0
+-  Tested on Microsoft SQL Server 2016, 2017, 2019
+-  Passes most of the tests of the Django test suite
+-  Compatible with
+   [Micosoft ODBC Driver for SQL Server](https://docs.microsoft.com/en-us/sql/connect/odbc/microsoft-odbc-driver-for-sql-server),
+   [SQL Server Native Client](https://msdn.microsoft.com/en-us/library/ms131321(v=sql.120).aspx),
+   and [FreeTDS](https://www.freetds.org/) ODBC drivers
+
+## Dependencies
+
+-  pyodbc 3.0 or newer
+
+## Installation
+
+1. Install pyodbc 3.0 (or newer) and Django
+2. Install mssql-django:
+
+       pip install mssql-django
+
+3. Set the `ENGINE` setting in the `settings.py` file used by
+   your Django application or project to `'mssql'`:
+
+       'ENGINE': 'mssql'
+
+## Configuration
+
+### Standard Django settings
+
+The following entries in a database-level settings dictionary
+in DATABASES control the behavior of the backend:
+
+-  ENGINE
+
+   String. It must be `"mssql"`.
+
+-  NAME
+
+   String. Database name. Required.
+
+-  HOST
+
+   String. SQL Server instance in `"server\instance"` format.
+
+-  PORT
+
+   String. Server instance port.
+   An empty string means the default port.
+
+-  USER
+
+   String. Database user name in `"user"` format.
+   If not given then MS Integrated Security will be used.
+
+-  PASSWORD
+
+   String. Database user password.
+
+-  TOKEN
+
+   String. Access token fetched as a user or service principal which
+   has access to the database. E.g. when using `azure.identity`, the
+   result of `DefaultAzureCredential().get_token('https://database.windows.net/.default')`
+   can be passed.
+
+-  AUTOCOMMIT
+
+   Boolean. Set this to `False` if you want to disable
+   Django's transaction management and implement your own.
+
+-  Trusted_Connection
+
+   String. Default is `"yes"`. Can be set to `"no"` if required.
+
+and the following entries are also available in the `TEST` dictionary
+for any given database-level settings dictionary:
+
+-  NAME
+
+   String. The name of database to use when running the test suite.
+   If the default value (`None`) is used, the test database will use
+   the name `"test_" + NAME`.
+
+-  COLLATION
+
+   String. The collation order to use when creating the test database.
+   If the default value (`None`) is used, the test database is assigned
+   the default collation of the instance of SQL Server.
+
+-  DEPENDENCIES
+
+   String. The creation-order dependencies of the database.
+   See the official Django documentation for more details.
+
+-  MIRROR
+
+   String. The alias of the database that this database should
+   mirror during testing. Default value is `None`.
+   See the official Django documentation for more details.
+
+### OPTIONS
+
+Dictionary. Current available keys are:
+
+-  driver
+
+   String. ODBC Driver to use (`"ODBC Driver 17 for SQL Server"`,
+   `"SQL Server Native Client 11.0"`, `"FreeTDS"` etc).
+   Default is `"ODBC Driver 17 for SQL Server"`.
+
+-  isolation_level
+
+   String. Sets [transaction isolation level](https://docs.microsoft.com/en-us/sql/t-sql/statements/set-transaction-isolation-level-transact-sql)
+   for each database session. Valid values for this entry are
+   `READ UNCOMMITTED`, `READ COMMITTED`, `REPEATABLE READ`,
+   `SNAPSHOT`, and `SERIALIZABLE`. Default is `None` which means
+   no isolation level is set to a database session and SQL Server default
+   will be used.
+
+-  dsn
+
+   String. A named DSN can be used instead of `HOST`.
+
+-  host_is_server
+
+   Boolean. Only relevant if using the FreeTDS ODBC driver under
+   Unix/Linux.
+
+   By default, when using the FreeTDS ODBC driver the value specified in
+   the ``HOST`` setting is used in a ``SERVERNAME`` ODBC connection
+   string component instead of being used in a ``SERVER`` component;
+   this means that this value should be the name of a *dataserver*
+   definition present in the ``freetds.conf`` FreeTDS configuration file
+   instead of a hostname or an IP address.
+
+   But if this option is present and its value is ``True``, this
+   special behavior is turned off. Instead, connections to the database
+   server will be established using ``HOST`` and ``PORT`` options, without
+   requiring ``freetds.conf`` to be configured.
+
+   See https://www.freetds.org/userguide/dsnless.html for more information.
+
+-  unicode_results
+
+   Boolean. If it is set to ``True``, pyodbc's *unicode_results* feature
+   is activated and strings returned from pyodbc are always Unicode.
+   Default value is ``False``.
+
+-  extra_params
+
+   String. Additional parameters for the ODBC connection. The format is
+   ``"param=value;param=value"``, [Azure AD Authentication](https://github.com/microsoft/mssql-django/wiki/Azure-AD-Authentication) (Service Principal, Interactive, Msi) can be added to this field.
+
+-  collation
+
+   String. Name of the collation to use when performing text field
+   lookups against the database. Default is ``None``; this means no
+   collation specifier is added to your lookup SQL (the default
+   collation of your database will be used). For Chinese language you
+   can set it to ``"Chinese_PRC_CI_AS"``.
+
+-  connection_timeout
+
+   Integer. Sets the timeout in seconds for the database connection process.
+   Default value is ``0`` which disables the timeout.
+
+-  connection_retries
+
+   Integer. Sets the times to retry the database connection process.
+   Default value is ``5``.
+
+-  connection_retry_backoff_time
+
+   Integer. Sets the back off time in seconds for reries of
+   the database connection process. Default value is ``5``.
+
+-  query_timeout
+
+   Integer. Sets the timeout in seconds for the database query.
+   Default value is ``0`` which disables the timeout.
+
+- [setencoding](https://github.com/mkleehammer/pyodbc/wiki/Connection#setencoding) and [setdecoding](https://github.com/mkleehammer/pyodbc/wiki/Connection#setdecoding)
+
+    ```python
+    # Example
+    "OPTIONS": {
+            "setdecoding": [
+                {"sqltype": pyodbc.SQL_CHAR, "encoding": 'utf-8'},
+                {"sqltype": pyodbc.SQL_WCHAR, "encoding": 'utf-8'}],
+            "setencoding": [
+                {"encoding": "utf-8"}],
+            ...
+            },
+    ```
+
+### Backend-specific settings
+
+The following project-level settings also control the behavior of the backend:
+
+-  DATABASE_CONNECTION_POOLING
+
+   Boolean. If it is set to ``False``, pyodbc's connection pooling feature
+   won't be activated.
+
+### Example
+
+Here is an example of the database settings:
+
+```python
+    DATABASES = {
+        'default': {
+            'ENGINE': 'mssql',
+            'NAME': 'mydb',
+            'USER': 'user@myserver',
+            'PASSWORD': 'password',
+            'HOST': 'myserver.database.windows.net',
+            'PORT': '',
+
+            'OPTIONS': {
+                'driver': 'ODBC Driver 17 for SQL Server',
+            },
+        },
+    }
+
+    # set this to False if you want to turn off pyodbc's connection pooling
+    DATABASE_CONNECTION_POOLING = False
+```
+
+## Limitations
+
+The following features are currently not fully supported:
+- Altering a model field from or to AutoField at migration
+- Django annotate functions have floating point arithmetic problems in some cases
+- Annotate function with exists
+- Exists function in order_by
+- Righthand power and arithmetic with datatimes
+- Timezones, timedeltas not fully supported
+- Rename field/model with foreign key constraint
+- Database level constraints
+- Math degrees power or radians
+- Bit-shift operators
+- Filtered index
+- Date extract function
+- Hashing functions
+
+JSONField lookups have limitations, more details [here](https://github.com/microsoft/mssql-django/wiki/JSONField).
+
+## Contributing
+
+More details on contributing can be found [here](CONTRIBUTING.md).
+
+This project welcomes contributions and suggestions.  Most contributions require you to agree to a
+Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
+the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
+
+When you submit a pull request, a CLA bot will automatically determine whether you need to provide
+a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
+provided by the bot. You will only need to do this once across all repos using our CLA.
+
+This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
+For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
+contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
+
+## Security Reporting Instructions
+
+For security reporting instructions please refer to the [`SECURITY.md`](SECURITY.md) file in this repository.
+
+## Trademarks
+
+This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft
+trademarks or logos is subject to and must follow
+[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
+Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
+Any use of third-party trademarks or logos are subject to those third-party's policies.
+
+
diff --git a/README.md b/README.md
index aa529cc..b5a9144 100644
--- a/README.md
+++ b/README.md
@@ -125,7 +125,7 @@ Dictionary. Current available keys are:
    for each database session. Valid values for this entry are
    `READ UNCOMMITTED`, `READ COMMITTED`, `REPEATABLE READ`,
    `SNAPSHOT`, and `SERIALIZABLE`. Default is `None` which means
-   no isolation levei is set to a database session and SQL Server default
+   no isolation level is set to a database session and SQL Server default
    will be used.
 
 -  dsn
diff --git a/SECURITY.md b/SECURITY.md
deleted file mode 100644
index 9dad406..0000000
--- a/SECURITY.md
+++ /dev/null
@@ -1,39 +0,0 @@
-# Security
-
-Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
-
-If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below.
-
-## Reporting Security Issues
-
-**Please do not report security vulnerabilities through public GitHub issues.**
-
-Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).
-
-If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com).  If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).
-
-You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 
-
-Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
-
-  * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
-  * Full paths of source file(s) related to the manifestation of the issue
-  * The location of the affected source code (tag/branch/commit or direct URL)
-  * Any special configuration required to reproduce the issue
-  * Step-by-step instructions to reproduce the issue
-  * Proof-of-concept or exploit code (if possible)
-  * Impact of the issue, including how an attacker might exploit the issue
-
-This information will help us triage your report more quickly.
-
-If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.
-
-## Preferred Languages
-
-We prefer all communications to be in English.
-
-## Policy
-
-Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
-
-<!-- END MICROSOFT SECURITY.MD BLOCK -->
\ No newline at end of file
diff --git a/SUPPORT.md b/SUPPORT.md
deleted file mode 100644
index cb57469..0000000
--- a/SUPPORT.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# Support
-
-## How to file issues and get help  
-
-This project uses GitHub Issues to track bugs and feature requests. Please search the existing 
-issues before filing new issues to avoid duplicates.  For new issues, file your bug or 
-feature request as a new Issue.
-
-## Microsoft Support Policy  
-
-Support for this project is limited to the resources listed above.
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
deleted file mode 100644
index 2120748..0000000
--- a/azure-pipelines.yml
+++ /dev/null
@@ -1,220 +0,0 @@
-trigger:
-  - master
-  - dev
-  - 1ES
-
-schedules:
-- cron: "0 9 * * *"
-  displayName: Daily midnight build
-  branches:
-    include:
-    - dev
-  always: true
-
-jobs:
-  - job: Windows
-    pool:
-      name: Django-1ES-pool
-      demands:
-      - imageOverride -equals JDBC-MMS2019-SQL2019
-    timeoutInMinutes: 120
-
-    strategy:
-      matrix:
-        Python3.10 - Django 4.0:
-          python.version: '3.10'
-          tox.env: 'py310-django40'
-        Python 3.9 - Django 4.0:
-          python.version: '3.9'
-          tox.env: 'py39-django40'
-        Python 3.8 - Django 4.0:
-          python.version: '3.8'
-          tox.env: 'py38-django40'
-
-        Python 3.9 - Django 3.2:
-          python.version: '3.9'
-          tox.env: 'py39-django32'
-        Python 3.8 - Django 3.2:
-          python.version: '3.8'
-          tox.env: 'py38-django32'
-        Python 3.7 - Django 3.2:
-          python.version: '3.7'
-          tox.env: 'py37-django32'
-        Python 3.6 - Django 3.2:
-          python.version: '3.6'
-          tox.env: 'py36-django32'
-
-        Python 3.9 - Django 3.1:
-          python.version: '3.9'
-          tox.env: 'py39-django31'
-        Python 3.8 - Django 3.1:
-          python.version: '3.8'
-          tox.env: 'py38-django31'
-        Python 3.7 - Django 3.1:
-          python.version: '3.7'
-          tox.env: 'py37-django31'
-        Python 3.6 - Django 3.1:
-          python.version: '3.6'
-          tox.env: 'py36-django31'
-
-        Python 3.9 - Django 3.0:
-          python.version: '3.9'
-          tox.env: 'py39-django30'
-        Python 3.8 - Django 3.0:
-          python.version: '3.8'
-          tox.env: 'py38-django30'
-        Python 3.7 - Django 3.0:
-          python.version: '3.7'
-          tox.env: 'py37-django30'
-        Python 3.6 - Django 3.0:
-          python.version: '3.6'
-          tox.env: 'py36-django30'
-
-        Python 3.7 - Django 2.2:
-          python.version: '3.7'
-          tox.env: 'py37-django22'
-        Python 3.6 - Django 2.2:
-          python.version: '3.6'
-          tox.env: 'py36-django22'
-
-    steps:
-      - task: CredScan@2
-        inputs:
-          toolMajorVersion: 'V2'
-
-      - task: UsePythonVersion@0
-        inputs:
-          versionSpec: "$(python.version)"
-        displayName: Use Python $(python.version)
-
-      - powershell: |
-          $IP=Get-NetIPAddress -AddressFamily IPv4 -InterfaceIndex $(Get-NetConnectionProfile -IPv4Connectivity Internet | Select-Object -ExpandProperty InterfaceIndex) | Select-Object -ExpandProperty IPAddress
-
-          (Get-Content $pwd/testapp/settings.py).replace('localhost', $IP) | Set-Content $pwd/testapp/settings.py
-
-          Invoke-WebRequest https://download.microsoft.com/download/E/6/B/E6BFDC7A-5BCD-4C51-9912-635646DA801E/en-US/17.5.2.1/x64/msodbcsql.msi -OutFile msodbcsql.msi
-          msiexec /quiet /passive /qn /i msodbcsql.msi IACCEPTMSODBCSQLLICENSETERMS=YES
-          Get-OdbcDriver
-        displayName: Install ODBC
-
-      - powershell: |
-          Import-Module "sqlps"
-          Invoke-Sqlcmd @"
-              EXEC xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'LoginMode', REG_DWORD, 2
-              ALTER LOGIN [sa] ENABLE;
-              ALTER LOGIN [sa] WITH PASSWORD = 'MyPassword42', CHECK_POLICY=OFF;
-          "@
-        displayName: Set up SQL Server
-
-      - powershell: |
-          Restart-Service -Name MSSQLSERVER -Force
-        displayName: Restart SQL Server
-
-      - powershell: |
-          python -m pip install --upgrade pip wheel setuptools
-          python -m pip install tox
-          git clone https://github.com/django/django.git
-
-          python -m tox -e $(tox.env)
-        displayName: Run tox
-
-  - job: Linux
-    pool:
-      name: Django-1ES-pool
-      demands:
-      - imageOverride -equals MMSUbuntu20.04
-    timeoutInMinutes: 120
-
-    strategy:
-      matrix:
-        Python3.10 - Django 4.0:
-          python.version: '3.10'
-          tox.env: 'py310-django40'
-        Python 3.9 - Django 4.0:
-          python.version: '3.9'
-          tox.env: 'py39-django40'
-        Python 3.8 - Django 4.0:
-          python.version: '3.8'
-          tox.env: 'py38-django40'
-
-        Python 3.9 - Django 3.2:
-          python.version: '3.9'
-          tox.env: 'py39-django32'
-        Python 3.8 - Django 3.2:
-          python.version: '3.8'
-          tox.env: 'py38-django32'
-        Python 3.7 - Django 3.2:
-          python.version: '3.7'
-          tox.env: 'py37-django32'
-        Python 3.6 - Django 3.2:
-          python.version: '3.6'
-          tox.env: 'py36-django32'
-
-        Python 3.9 - Django 3.1:
-          python.version: '3.9'
-          tox.env: 'py39-django31'
-        Python 3.8 - Django 3.1:
-          python.version: '3.8'
-          tox.env: 'py38-django31'
-        Python 3.7 - Django 3.1:
-          python.version: '3.7'
-          tox.env: 'py37-django31'
-        Python 3.6 - Django 3.1:
-          python.version: '3.6'
-          tox.env: 'py36-django31'
-
-        Python 3.9 - Django 3.0:
-          python.version: '3.9'
-          tox.env: 'py39-django30'
-        Python 3.8 - Django 3.0:
-          python.version: '3.8'
-          tox.env: 'py38-django30'
-        Python 3.7 - Django 3.0:
-          python.version: '3.7'
-          tox.env: 'py37-django30'
-        Python 3.6 - Django 3.0:
-          python.version: '3.6'
-          tox.env: 'py36-django30'
-
-        Python 3.7 - Django 2.2:
-          python.version: '3.7'
-          tox.env: 'py37-django22'
-        Python 3.6 - Django 2.2:
-          python.version: '3.6'
-          tox.env: 'py36-django22'
-
-    steps:
-      - task: UsePythonVersion@0
-        inputs:
-          versionSpec: "$(python.version)"
-        displayName: Use Python $(python.version)
-
-      - script: |
-          docker pull mcr.microsoft.com/mssql/server:2019-latest
-          docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=MyPassword42' -p 1433:1433 -d mcr.microsoft.com/mssql/server:2019-latest
-          curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
-          curl https://packages.microsoft.com/config/ubuntu/20.04/prod.list | sudo tee /etc/apt/sources.list.d/mssql-release.list
-          sudo apt-get update
-          sudo ACCEPT_EULA=Y apt-get install -y msodbcsql17 g++ unixodbc-dev libmemcached-dev
-        displayName: Install SQL Server
-
-      - script: |
-          python -m pip install --upgrade pip wheel setuptools
-          pip install tox
-          git clone https://github.com/django/django.git
-        displayName: Install requirements
-
-      - script: tox -e $(tox.env)
-        displayName: Run tox
-
-      - task: PublishCodeCoverageResults@1
-        inputs:
-          codeCoverageTool: 'Cobertura'
-          summaryFileLocation: 'django/coverage.xml'
-
-      - task: PublishTestResults@2
-        displayName: Publish test results via jUnit
-        inputs:
-          testResultsFormat: 'JUnit'
-          testResultsFiles: 'django/result.xml'
-          testRunTitle: 'junit-$(Agent.OS)-$(Agent.OSArchitecture)-$(tox.env)'
diff --git a/debian/changelog b/debian/changelog
index 59b7be1..fb066ad 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+mssql-django (1.1.3+git20220623.1.781e9bf+ds-1) UNRELEASED; urgency=low
+
+  * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Wed, 20 Jul 2022 21:11:44 -0000
+
 mssql-django (1.1.3-1) unstable; urgency=medium
 
   [ Debian Janitor ]
diff --git a/manage.py b/manage.py
deleted file mode 100755
index 4a2e3bf..0000000
--- a/manage.py
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env python
-
-# Copyright (c) Microsoft Corporation.
-# Licensed under the BSD license.
-
-import os
-import sys
-
-if __name__ == "__main__":
-    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "testapp.settings")
-
-    from django.core.management import execute_from_command_line
-
-    execute_from_command_line(sys.argv)
diff --git a/mssql/base.py b/mssql/base.py
index fbdd979..6a930cd 100644
--- a/mssql/base.py
+++ b/mssql/base.py
@@ -8,6 +8,7 @@ import os
 import re
 import time
 import struct
+import datetime
 
 from django.core.exceptions import ImproperlyConfigured
 
@@ -35,7 +36,7 @@ if hasattr(settings, 'DATABASE_CONNECTION_POOLING'):
 from .client import DatabaseClient  # noqa
 from .creation import DatabaseCreation  # noqa
 from .features import DatabaseFeatures  # noqa
-from .introspection import DatabaseIntrospection  # noqa
+from .introspection import DatabaseIntrospection, SQL_TIMESTAMP_WITH_TIMEZONE  # noqa
 from .operations import DatabaseOperations  # noqa
 from .schema import DatabaseSchemaEditor  # noqa
 
@@ -80,6 +81,13 @@ def encode_value(v):
     return v
 
 
+def handle_datetimeoffset(dto_value):
+    # Decode bytes returned from SQL Server
+    # source: https://github.com/mkleehammer/pyodbc/wiki/Using-an-Output-Converter-function
+    tup = struct.unpack("<6hI2h", dto_value)  # e.g., (2017, 3, 16, 10, 35, 18, 500000000)
+    return datetime.datetime(tup[0], tup[1], tup[2], tup[3], tup[4], tup[5], tup[6] // 1000)
+
+
 class DatabaseWrapper(BaseDatabaseWrapper):
     vendor = 'microsoft'
     display_name = 'SQL Server'
@@ -95,7 +103,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
         'BooleanField': 'bit',
         'CharField': 'nvarchar(%(max_length)s)',
         'DateField': 'date',
-        'DateTimeField': 'datetime2',
+        'DateTimeField': 'datetimeoffset',
         'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
         'DurationField': 'bigint',
         'FileField': 'nvarchar(%(max_length)s)',
@@ -364,6 +372,9 @@ class DatabaseWrapper(BaseDatabaseWrapper):
                 if not need_to_retry:
                     raise
 
+        # Handling values from DATETIMEOFFSET columns
+        # source: https://github.com/mkleehammer/pyodbc/wiki/Using-an-Output-Converter-function
+        conn.add_output_converter(SQL_TIMESTAMP_WITH_TIMEZONE, handle_datetimeoffset)
         conn.timeout = query_timeout
         if setencoding:
             for entry in setencoding:
diff --git a/mssql/compiler.py b/mssql/compiler.py
index 8a3da54..352703a 100644
--- a/mssql/compiler.py
+++ b/mssql/compiler.py
@@ -8,7 +8,7 @@ import django
 from django.db.models.aggregates import Avg, Count, StdDev, Variance
 from django.db.models.expressions import Ref, Subquery, Value
 from django.db.models.functions import (
-    Chr, ConcatPair, Greatest, Least, Length, LPad, Repeat, RPad, StrIndex, Substr, Trim
+    Chr, ConcatPair, Greatest, Least, Length, LPad, Random, Repeat, RPad, StrIndex, Substr, Trim
 )
 from django.db.models.sql import compiler
 from django.db.transaction import TransactionManagementError
@@ -311,7 +311,13 @@ class SQLCompiler(compiler.SQLCompiler):
 
             if order_by:
                 ordering = []
-                for _, (o_sql, o_params, _) in order_by:
+                for expr, (o_sql, o_params, _) in order_by:
+                    if expr:
+                        src = next(iter(expr.get_source_expressions()))
+                        if isinstance(src, Random):
+                            # ORDER BY RAND() doesn't return rows in random order
+                            # replace it with NEWID()
+                            o_sql = o_sql.replace('RAND()', 'NEWID()')
                     ordering.append(o_sql)
                     params.extend(o_params)
                 result.append('ORDER BY %s' % ', '.join(ordering))
diff --git a/mssql/features.py b/mssql/features.py
index 8b6dff4..1672015 100644
--- a/mssql/features.py
+++ b/mssql/features.py
@@ -46,7 +46,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
     supports_subqueries_in_group_by = False
     supports_tablespaces = True
     supports_temporal_subtraction = True
-    supports_timezones = False
+    supports_timezones = True
     supports_transactions = True
     uses_savepoints = True
     has_bulk_insert = True
diff --git a/mssql/functions.py b/mssql/functions.py
index b0958d6..96e74df 100644
--- a/mssql/functions.py
+++ b/mssql/functions.py
@@ -9,7 +9,7 @@ from django.db import NotSupportedError, connections, transaction
 from django.db.models import BooleanField, CheckConstraint, Value
 from django.db.models.expressions import Case, Exists, Expression, OrderBy, When, Window
 from django.db.models.fields import BinaryField, Field
-from django.db.models.functions import Cast, NthValue
+from django.db.models.functions import Cast, NthValue, MD5, SHA1, SHA224, SHA256, SHA384, SHA512
 from django.db.models.functions.math import ATan2, Ln, Log, Mod, Round
 from django.db.models.lookups import In, Lookup
 from django.db.models.query import QuerySet
@@ -288,6 +288,99 @@ def bulk_update_with_default(self, objs, fields, batch_size=None, default=0):
     return rows_updated
 
 
+def sqlserver_md5(self, compiler, connection, **extra_context):
+    # UTF-8 support added in SQL Server 2019
+    if (connection.sql_server_version < 2019):
+        raise NotSupportedError("Hashing is not supported on this version SQL Server. Upgrade to 2019 or above")
+
+    column_name = self.get_source_fields()[0].name
+
+    with connection.cursor() as cursor:
+        cursor.execute("SELECT MAX(DATALENGTH(%s)) FROM %s" % (column_name, compiler.query.model._meta.db_table))
+        max_size = cursor.fetchone()[0]
+
+    # Collation of SQL Server by default is UTF-16 but Django always assumes UTF-8 enconding
+    # https://docs.djangoproject.com/en/4.0/ref/unicode/#general-string-handling
+    return self.as_sql(
+        compiler,
+        connection,
+        template="LOWER(CONVERT(CHAR(32), HASHBYTES('%s', CAST(%s COLLATE Latin1_General_100_CI_AI_SC_UTF8 AS VARCHAR(%s))), 2))" % ('%(function)s', column_name, max_size),
+        **extra_context,
+    )
+
+
+def sqlserver_sha1(self, compiler, connection, **extra_context):
+    # UTF-8 support added in SQL Server 2019
+    if (connection.sql_server_version < 2019):
+        raise NotSupportedError("Hashing is not supported on this version SQL Server. Upgrade to 2019 or above")
+
+    column_name = self.get_source_fields()[0].name
+
+    # Collation of SQL Server by default is UTF-16 but Django always assumes UTF-8 enconding
+    # https://docs.djangoproject.com/en/4.0/ref/unicode/#general-string-handling
+    with connection.cursor() as cursor:
+        cursor.execute("SELECT MAX(DATALENGTH(%s)) FROM %s" % (column_name, compiler.query.model._meta.db_table))
+        max_size = cursor.fetchone()[0]
+
+    return self.as_sql(
+        compiler,
+        connection,
+        template="LOWER(CONVERT(CHAR(40), HASHBYTES('%s', CAST(%s COLLATE Latin1_General_100_CI_AI_SC_UTF8 AS VARCHAR(%s))), 2))" % ('%(function)s', column_name, max_size),
+        **extra_context,
+    )
+
+
+def sqlserver_sha224(self, compiler, connection, **extra_context):
+    raise NotSupportedError("SHA224 is not supported on SQL Server.")
+
+
+def sqlserver_sha256(self, compiler, connection, **extra_context):
+    # UTF-8 support added in SQL Server 2019
+    if (connection.sql_server_version < 2019):
+        raise NotSupportedError("Hashing is not supported on this version SQL Server. Upgrade to 2019 or above")
+
+    column_name = self.get_source_fields()[0].name
+
+    # Collation of SQL Server by default is UTF-16 but Django always assumes UTF-8 enconding
+    # https://docs.djangoproject.com/en/4.0/ref/unicode/#general-string-handling
+    with connection.cursor() as cursor:
+        cursor.execute("SELECT MAX(DATALENGTH(%s)) FROM %s" % (column_name, compiler.query.model._meta.db_table))
+        max_size = cursor.fetchone()[0]
+
+    return self.as_sql(
+        compiler,
+        connection,
+        template="LOWER(CONVERT(CHAR(64), HASHBYTES('SHA2_256', CAST(%s COLLATE Latin1_General_100_CI_AI_SC_UTF8 AS VARCHAR(%s))), 2))" % (column_name, max_size),
+        **extra_context,
+    )
+
+
+def sqlserver_sha384(self, compiler, connection, **extra_context):
+    raise NotSupportedError("SHA384 is not supported on SQL Server.")
+
+
+def sqlserver_sha512(self, compiler, connection, **extra_context):
+    # UTF-8 support added in SQL Server 2019
+    if (connection.sql_server_version < 2019):
+        raise NotSupportedError("Hashing is not supported on this version SQL Server. Upgrade to 2019 or above")
+
+    column_name = self.get_source_fields()[0].name
+
+    # Collation of SQL Server by default is UTF-16 but Django always assumes UTF-8 enconding
+    # https://docs.djangoproject.com/en/4.0/ref/unicode/#general-string-handling
+    with connection.cursor() as cursor:
+        cursor.execute("SELECT MAX(DATALENGTH(%s)) FROM %s" % (column_name, compiler.query.model._meta.db_table))
+        max_size = cursor.fetchone()[0]
+
+    return self.as_sql(
+        compiler,
+        connection,
+        template="LOWER(CONVERT(CHAR(128), HASHBYTES('SHA2_512', CAST(%s COLLATE Latin1_General_100_CI_AI_SC_UTF8 AS VARCHAR(%s))), 2))" % (column_name, max_size),
+        **extra_context,
+    )
+
+
+# `as_microsoft` called by django.db.models.sql.compiler based on connection.vendor
 ATan2.as_microsoft = sqlserver_atan2
 # Need copy of old In.split_parameter_list_as_sql for other backends to call
 in_split_parameter_list_as_sql = In.split_parameter_list_as_sql
@@ -304,6 +397,12 @@ Mod.as_microsoft = sqlserver_mod
 NthValue.as_microsoft = sqlserver_nth_value
 Round.as_microsoft = sqlserver_round
 Window.as_microsoft = sqlserver_window
+MD5.as_microsoft = sqlserver_md5
+SHA1.as_microsoft = sqlserver_sha1
+SHA224.as_microsoft = sqlserver_sha224
+SHA256.as_microsoft = sqlserver_sha256
+SHA384.as_microsoft = sqlserver_sha384
+SHA512.as_microsoft = sqlserver_sha512
 BinaryField.__init__ = BinaryField_init
 CheckConstraint._get_check_sql = _get_check_sql
 
diff --git a/mssql/introspection.py b/mssql/introspection.py
index b69efd5..1a47d9e 100644
--- a/mssql/introspection.py
+++ b/mssql/introspection.py
@@ -12,10 +12,13 @@ from django.conf import settings
 
 SQL_AUTOFIELD = -777555
 SQL_BIGAUTOFIELD = -777444
+SQL_TIMESTAMP_WITH_TIMEZONE = -155
+
 
 def get_schema_name():
     return getattr(settings, 'SCHEMA_TO_INSPECT', 'SCHEMA_NAME()')
 
+
 class DatabaseIntrospection(BaseDatabaseIntrospection):
     # Map type codes to Django Field types.
     data_types_reverse = {
@@ -39,7 +42,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
         Database.SQL_TINYINT: 'SmallIntegerField',
         Database.SQL_TYPE_DATE: 'DateField',
         Database.SQL_TYPE_TIME: 'TimeField',
-        Database.SQL_TYPE_TIMESTAMP: 'DateTimeField',
+        SQL_TIMESTAMP_WITH_TIMEZONE: 'DateTimeField',
         Database.SQL_VARBINARY: 'BinaryField',
         Database.SQL_VARCHAR: 'TextField',
         Database.SQL_WCHAR: 'CharField',
@@ -66,7 +69,8 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
         """
         Returns a list of table and view names in the current database.
         """
-        sql = f'SELECT TABLE_NAME, TABLE_TYPE FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = {get_schema_name()}'
+        sql = 'SELECT TABLE_NAME, TABLE_TYPE FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = %s' % (
+            get_schema_name())
         cursor.execute(sql)
         types = {'BASE TABLE': 't', 'VIEW': 'v'}
         return [TableInfo(row[0], types.get(row[1]))
@@ -114,7 +118,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
                             """ % (table_name, column[0])
                     cursor.execute(sql)
                     collation_name = cursor.fetchone()
-                    column.append(collation_name[0] if collation_name  else '')
+                    column.append(collation_name[0] if collation_name else '')
                 else:
                     column.append('')
 
diff --git a/mssql/operations.py b/mssql/operations.py
index 8d607fd..44eec87 100644
--- a/mssql/operations.py
+++ b/mssql/operations.py
@@ -4,6 +4,7 @@
 import datetime
 import uuid
 import warnings
+import sys
 
 from django.conf import settings
 from django.db.backends.base.operations import BaseDatabaseOperations
@@ -26,7 +27,7 @@ class DatabaseOperations(BaseDatabaseOperations):
         return 2048
 
     def _convert_field_to_tz(self, field_name, tzname):
-        if settings.USE_TZ and not tzname == 'UTC':
+        if tzname and settings.USE_TZ and self.connection.timezone_name != tzname:
             offset = self._get_utcoffset(tzname)
             field_name = 'DATEADD(second, %d, %s)' % (offset, field_name)
         return field_name
@@ -107,7 +108,7 @@ class DatabaseOperations(BaseDatabaseOperations):
 
     def convert_datetimefield_value(self, value, expression, connection):
         if value is not None:
-            if settings.USE_TZ:
+            if settings.USE_TZ and not timezone.is_aware(value):
                 value = timezone.make_aware(value, self.connection.timezone)
         return value
 
@@ -129,6 +130,8 @@ class DatabaseOperations(BaseDatabaseOperations):
             return "DATEPART(weekday, %s)" % field_name
         elif lookup_type == 'week':
             return "DATEPART(iso_week, %s)" % field_name
+        elif lookup_type == 'iso_week_day':
+            return "DATEPART(weekday, DATEADD(day, -1, %s))" % field_name
         elif lookup_type == 'iso_year':
             return "YEAR(DATEADD(day, 26 - DATEPART(isoww, %s), %s))" % (field_name, field_name)
         else:
@@ -144,7 +147,8 @@ class DatabaseOperations(BaseDatabaseOperations):
             sql = 'DATEADD(microsecond, %d%%s, CAST(%s AS datetime2))' % (timedelta.microseconds, sql)
         return sql
 
-    def date_trunc_sql(self, lookup_type, field_name, tzname=''):
+    def date_trunc_sql(self, lookup_type, field_name, tzname=None):
+        field_name = self._convert_field_to_tz(field_name, tzname)
         CONVERT_YEAR = 'CONVERT(varchar, DATEPART(year, %s))' % field_name
         CONVERT_QUARTER = 'CONVERT(varchar, 1+((DATEPART(quarter, %s)-1)*3))' % field_name
         CONVERT_MONTH = 'CONVERT(varchar, DATEPART(month, %s))' % field_name
@@ -480,9 +484,22 @@ class DatabaseOperations(BaseDatabaseOperations):
         """
         if value is None:
             return None
-        if settings.USE_TZ and timezone.is_aware(value):
-            # pyodbc donesn't support datetimeoffset
-            value = value.astimezone(self.connection.timezone).replace(tzinfo=None)
+
+        # Expression values are adapted by the database.
+        if hasattr(value, 'resolve_expression'):
+            return value
+
+        if timezone.is_aware(value):
+            if settings.USE_TZ:
+                # When support for time zones is enabled, Django stores datetime information
+                # in UTC in the database and uses time-zone-aware objects internally
+                # source: https://docs.djangoproject.com/en/dev/topics/i18n/timezones/#overview
+                value = value.astimezone(timezone.utc)
+            else:
+                # When USE_TZ is False, settings.TIME_ZONE is the time zone in
+                # which Django will store all datetimes
+                # source: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-TIME_ZONE
+                value = timezone.make_naive(value, self.connection.timezone)
         return value
 
     def time_trunc_sql(self, lookup_type, field_name, tzname=''):
diff --git a/mssql/schema.py b/mssql/schema.py
index 8556c15..29da104 100644
--- a/mssql/schema.py
+++ b/mssql/schema.py
@@ -69,20 +69,20 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
     sql_delete_column = "ALTER TABLE %(table)s DROP COLUMN %(column)s"
     sql_delete_index = "DROP INDEX %(name)s ON %(table)s"
     sql_delete_table = """
-        DECLARE @sql_froeign_constraint_name nvarchar(128)
+        DECLARE @sql_foreign_constraint_name nvarchar(128)
         DECLARE @sql_drop_constraint nvarchar(300)
         WHILE EXISTS(SELECT 1
             FROM sys.foreign_keys
             WHERE referenced_object_id = object_id('%(table)s'))
         BEGIN
-            SELECT TOP 1 @sql_froeign_constraint_name = name
+            SELECT TOP 1 @sql_foreign_constraint_name = name
             FROM sys.foreign_keys
             WHERE referenced_object_id = object_id('%(table)s')
             SELECT
             @sql_drop_constraint = 'ALTER TABLE [' + OBJECT_NAME(parent_object_id) + '] ' +
-            'DROP CONSTRAINT [' + @sql_froeign_constraint_name + '] '
+            'DROP CONSTRAINT [' + @sql_foreign_constraint_name + '] '
             FROM sys.foreign_keys
-            WHERE referenced_object_id = object_id('%(table)s') and name = @sql_froeign_constraint_name
+            WHERE referenced_object_id = object_id('%(table)s') and name = @sql_foreign_constraint_name
             exec sp_executesql @sql_drop_constraint
         END
         DROP TABLE %(table)s
@@ -234,9 +234,12 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
                                    unique=None, primary_key=None, index=None, foreign_key=None,
                                    check=None, type_=None, exclude=None, unique_constraint=None):
         """
-        Return all constraint names matching the columns and conditions. Modified from base `_constraint_names`
-        `any_column_matches`=False: (default) only return constraints covering exactly `column_names`
-        `any_column_matches`=True : return any constraints which include at least 1 of `column_names`
+        Return all constraint names matching the columns and conditions.
+        Modified from base `_constraint_names` but with the following new arguments:
+         - `unique_constraint` which explicitly finds unique implemented by CONSTRAINT not by an INDEX
+         - `column_match_any`:
+                False: (default) only return constraints covering exactly `column_names`
+                True : return any constraints which include at least 1 of `column_names`
         """
         if column_names is not None:
             column_names = [
@@ -280,11 +283,19 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
                      old_db_params, new_db_params, strict=False):
         """Actually perform a "physical" (non-ManyToMany) field update."""
 
-        # the backend doesn't support altering from/to (Big)AutoField
-        # because of the limited capability of SQL Server to edit IDENTITY property
+        # the backend doesn't support altering a column to/from AutoField as
+        # SQL Server cannot alter columns to add and remove IDENTITY properties
+        old_is_auto = False
+        new_is_auto = False
         for t in (AutoField, BigAutoField):
-            if isinstance(old_field, t) or isinstance(new_field, t):
-                raise NotImplementedError("the backend doesn't support altering from/to %s." % t.__name__)
+            if isinstance(old_field, t):
+                old_is_auto = True
+            if isinstance(new_field, t):
+                new_is_auto = True
+        if (old_is_auto and not new_is_auto) or (not old_is_auto and new_is_auto):
+            raise NotImplementedError("the backend doesn't support altering from %s to %s." %
+                (old_field.get_internal_type(), new_field.get_internal_type()))
+        
         # Drop any FK constraints, we'll remake them later
         fks_dropped = set()
         if old_field.remote_field and old_field.db_constraint:
@@ -324,6 +335,17 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
                 )
                 for fk_name in rel_fk_names:
                     self.execute(self._delete_constraint_sql(self.sql_delete_fk, new_rel.related_model, fk_name))
+        # If working with an AutoField or BigAutoField drop all indexes on the related table
+        # This is needed when doing ALTER column statements on IDENTITY fields
+        # https://stackoverflow.com/questions/33429775/sql-server-alter-table-alter-column-giving-set-option-error
+        for t in (AutoField, BigAutoField):
+            if isinstance(old_field, t) or isinstance(new_field, t):
+                index_names = self._constraint_names(model, index=True)
+                for index_name in index_names:
+                    self.execute(
+                        self._delete_constraint_sql(self.sql_delete_index, model, index_name)
+                    )
+                break
         # Removed an index? (no strict check, as multiple indexes are possible)
         # Remove indexes if db_index switched to False or a unique constraint
         # will now be used in lieu of an index. The following lines from the
@@ -389,7 +411,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
                     result = cursor.fetchall()
                     columns_to_recreate_index = ', '.join(['%s' % self.quote_name(column[0]) for column in result])
                     filter_definition = result[0][1]
-                sql_restore_index += f'CREATE UNIQUE INDEX {index_name} ON {model._meta.db_table} ({columns_to_recreate_index}) WHERE {filter_definition};'
+                sql_restore_index += 'CREATE UNIQUE INDEX %s ON %s (%s) WHERE %s;' % (
+                    index_name, model._meta.db_table, columns_to_recreate_index, filter_definition)
                 self.execute(self._db_table_delete_constraint_sql(
                     self.sql_delete_index, model._meta.db_table, index_name))
             self.execute(self._rename_field_sql(model._meta.db_table, old_field, new_field, new_type))
@@ -450,7 +473,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
                     (old_field.db_index or not new_field.db_index) and
                     new_field.db_index or
                     ((indexes_dropped and sorted(indexes_dropped) == sorted([index.name for index in model._meta.indexes])) or
-                    (indexes_dropped and sorted(indexes_dropped) == sorted(auto_index_names)))
+                     (indexes_dropped and sorted(indexes_dropped) == sorted(auto_index_names)))
                 ):
                     create_index_sql_statement = self._create_index_sql(model, [new_field])
                     if create_index_sql_statement.__str__() not in [sql.__str__() for sql in self.deferred_sql]:
@@ -533,12 +556,13 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
 
         # Restore indexes & unique constraints deleted above, SQL Server requires explicit restoration
         if (old_type != new_type or (old_field.null != new_field.null)) and (
-            old_field.column == new_field.column
+            old_field.column == new_field.column  # column rename is handled separately above
         ):
             # Restore unique constraints
             # Note: if nullable they are implemented via an explicit filtered UNIQUE INDEX (not CONSTRAINT)
             # in order to get ANSI-compliant NULL behaviour (i.e. NULL != NULL, multiple are allowed)
-            if old_field.unique and new_field.unique:
+            # Note: Don't restore primary keys, we need to re-create those seperately
+            if old_field.unique and new_field.unique and not new_field.primary_key:
                 if new_field.null:
                     self.execute(
                         self._create_index_sql(
@@ -564,7 +588,47 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
                         if old_field.column in columns:
                             condition = ' AND '.join(["[%s] IS NOT NULL" % col for col in columns])
                             self.execute(self._create_unique_sql(model, columns, condition=condition))
+            # Restore primary keys
+            if old_field.primary_key and new_field.primary_key:
+                self.execute(
+                    self.sql_create_pk % {
+                        "table": self.quote_name(model._meta.db_table),
+                        "name": self.quote_name(
+                            self._create_index_name(model._meta.db_table, [new_field.column], suffix="_pk")
+                        ),
+                        "columns": self.quote_name(new_field.column),
+                    }
+                )
+            # Restore unqiue_together
+            # If we have ALTERed an AutoField or BigAutoField we need to recreate all unique_together clauses
+            for t in (AutoField, BigAutoField):
+                if isinstance(old_field, t) or isinstance(new_field, t):
+                    for field_names in model._meta.unique_together:
+                        columns = [model._meta.get_field(field).column for field in field_names]
+                        fields = [model._meta.get_field(field) for field in field_names]
+                        condition = ' AND '.join(["[%s] IS NOT NULL" % col for col in columns])
+                        # We need to pass fields instead of columns when using >= Django 4.0 because
+                        # of a backwards incompatible change to _create_unique_sql
+                        if django_version >= (4, 0):
+                            self.execute(
+                                self._create_unique_sql(model, fields, condition=condition)
+                            )
+                        else:
+                            self.execute(
+                                self._create_unique_sql(model, columns, condition=condition)
+                            )
+                    break
+
             # Restore indexes
+            # If we have ALTERed an AutoField or BigAutoField we need to recreate all indexes
+            for t in (AutoField, BigAutoField):
+                if isinstance(old_field, t) or isinstance(new_field, t):
+                    for field in model._meta.fields:
+                        if field.db_index:
+                            self.execute(
+                                self._create_index_sql(model, [field])
+                            )
+                    break
             index_columns = []
             if old_field.db_index and new_field.db_index:
                 index_columns.append([old_field])
@@ -577,8 +641,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
                 for columns in index_columns:
                     create_index_sql_statement = self._create_index_sql(model, columns)
                     if (create_index_sql_statement.__str__()
-                        not in [sql.__str__() for sql in self.deferred_sql] + [statement[0].__str__() for statement in post_actions]
-                        ):
+                            not in [sql.__str__() for sql in self.deferred_sql] + [statement[0].__str__() for statement in post_actions]
+                            ):
                         self.execute(create_index_sql_statement)
 
         # Type alteration on primary key? Then we need to alter the column
@@ -622,7 +686,26 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
             for sql, params in other_actions:
                 self.execute(sql, params)
             # Restore related_model indexes
-            self.execute(self._create_index_sql(new_rel.related_model, [new_rel.field]))
+            for field in new_rel.related_model._meta.fields:
+                if field.db_index:
+                    self.execute(
+                        self._create_index_sql(new_rel.related_model, [field])
+                    )
+            # Restore unique_together clauses
+            for field_names in new_rel.related_model._meta.unique_together:
+                columns = [new_rel.related_model._meta.get_field(field).column for field in field_names]
+                fields = [new_rel.related_model._meta.get_field(field) for field in field_names]
+                condition = ' AND '.join(["[%s] IS NOT NULL" % col for col in columns])
+                # We need to pass fields instead of columns when using >= Django 4.0 because
+                # of a backwards incompatible change to _create_unique_sql
+                if django_version >= (4, 0):
+                    self.execute(
+                        self._create_unique_sql(new_rel.related_model, fields, condition=condition)
+                    )
+                else:
+                    self.execute(
+                        self._create_unique_sql(new_rel.related_model, columns, condition=condition)
+                    )
         # Does it have a foreign key?
         if (new_field.remote_field and
                 (fks_dropped or not old_field.remote_field or not old_field.db_constraint) and
@@ -688,8 +771,20 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
 
     def _delete_unique_constraints(self, model, old_field, new_field, strict=False):
         unique_columns = []
+        # Considering just this column, we only need to drop unique constraints in advance of altering the field
+        # *if* it remains unique - if it wasn't unique before there's nothing to drop; if it won't remain unique
+        # afterwards then that is handled separately in _alter_field
         if old_field.unique and new_field.unique:
             unique_columns.append([old_field.column])
+
+        # Also consider unique_together because, although this is implemented with a filtered unique INDEX now, we
+        # need to handle the possibility that we're acting on a database previously created by an older version of
+        # this backend, where unique_together used to be implemented with a CONSTRAINT
+        for fields in model._meta.unique_together:
+            columns = [model._meta.get_field(field).column for field in fields]
+            if old_field.column in columns:
+                unique_columns.append(columns)
+
         if unique_columns:
             for columns in unique_columns:
                 self._delete_unique_constraint_for_columns(model, columns, strict=strict)
@@ -709,10 +804,12 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
                 len(constraint_names),
                 repr(columns),
             ))
+        # Delete constraints which are implemented as a table CONSTRAINT (this may include some created by an
+        # older version of this backend, even if the current version would implement it with an INDEX instead)
         for constraint_name in constraint_names_normal:
             self.execute(self._delete_constraint_sql(self.sql_delete_unique, model, constraint_name))
-        # Unique indexes which are not table constraints must be deleted using the appropriate SQL.
-        # These may exist for example to enforce ANSI-compliant unique constraints on nullable columns.
+        # Delete constraints which are implemented with an explicit index instead (not a table CONSTRAINT)
+        # These are used for example to enforce ANSI-compliant unique constraints on nullable columns.
         for index_name in constraint_names_index:
             self.execute(self._delete_constraint_sql(self.sql_delete_index, model, index_name))
 
diff --git a/mssql_django.egg-info/PKG-INFO b/mssql_django.egg-info/PKG-INFO
new file mode 100644
index 0000000..af05fd2
--- /dev/null
+++ b/mssql_django.egg-info/PKG-INFO
@@ -0,0 +1,314 @@
+Metadata-Version: 2.1
+Name: mssql-django
+Version: 1.1.3
+Summary: Django backend for Microsoft SQL Server
+Home-page: https://github.com/microsoft/mssql-django
+Author: Microsoft
+Author-email: opencode@microsoft.com
+License: BSD
+Project-URL: Release Notes, https://github.com/microsoft/mssql-django/releases
+Keywords: django
+Platform: UNKNOWN
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Framework :: Django
+Classifier: Operating System :: POSIX :: Linux
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Framework :: Django :: 3.2
+Classifier: Framework :: Django :: 4.0
+Description-Content-Type: text/markdown
+License-File: LICENSE.txt
+License-File: NOTICE.md
+
+# SQL Server backend for Django
+
+Welcome to the MSSQL-Django 3rd party backend project!
+
+*mssql-django* is a fork of [django-mssql-backend](https://pypi.org/project/django-mssql-backend/). This project provides an enterprise database connectivity option for the Django Web Framework, with support for Microsoft SQL Server and Azure SQL Database.
+
+We'd like to give thanks to the community that made this project possible, with particular recognition of the contributors: OskarPersson, michiya, dlo and the original Google Code django-pyodbc team. Moving forward we encourage partipation in this project from both old and new contributors!
+
+We hope you enjoy using the MSSQL-Django 3rd party backend.
+
+## Features
+
+-  Supports Django 3.2 and 4.0
+-  Tested on Microsoft SQL Server 2016, 2017, 2019
+-  Passes most of the tests of the Django test suite
+-  Compatible with
+   [Micosoft ODBC Driver for SQL Server](https://docs.microsoft.com/en-us/sql/connect/odbc/microsoft-odbc-driver-for-sql-server),
+   [SQL Server Native Client](https://msdn.microsoft.com/en-us/library/ms131321(v=sql.120).aspx),
+   and [FreeTDS](https://www.freetds.org/) ODBC drivers
+
+## Dependencies
+
+-  pyodbc 3.0 or newer
+
+## Installation
+
+1. Install pyodbc 3.0 (or newer) and Django
+2. Install mssql-django:
+
+       pip install mssql-django
+
+3. Set the `ENGINE` setting in the `settings.py` file used by
+   your Django application or project to `'mssql'`:
+
+       'ENGINE': 'mssql'
+
+## Configuration
+
+### Standard Django settings
+
+The following entries in a database-level settings dictionary
+in DATABASES control the behavior of the backend:
+
+-  ENGINE
+
+   String. It must be `"mssql"`.
+
+-  NAME
+
+   String. Database name. Required.
+
+-  HOST
+
+   String. SQL Server instance in `"server\instance"` format.
+
+-  PORT
+
+   String. Server instance port.
+   An empty string means the default port.
+
+-  USER
+
+   String. Database user name in `"user"` format.
+   If not given then MS Integrated Security will be used.
+
+-  PASSWORD
+
+   String. Database user password.
+
+-  TOKEN
+
+   String. Access token fetched as a user or service principal which
+   has access to the database. E.g. when using `azure.identity`, the
+   result of `DefaultAzureCredential().get_token('https://database.windows.net/.default')`
+   can be passed.
+
+-  AUTOCOMMIT
+
+   Boolean. Set this to `False` if you want to disable
+   Django's transaction management and implement your own.
+
+-  Trusted_Connection
+
+   String. Default is `"yes"`. Can be set to `"no"` if required.
+
+and the following entries are also available in the `TEST` dictionary
+for any given database-level settings dictionary:
+
+-  NAME
+
+   String. The name of database to use when running the test suite.
+   If the default value (`None`) is used, the test database will use
+   the name `"test_" + NAME`.
+
+-  COLLATION
+
+   String. The collation order to use when creating the test database.
+   If the default value (`None`) is used, the test database is assigned
+   the default collation of the instance of SQL Server.
+
+-  DEPENDENCIES
+
+   String. The creation-order dependencies of the database.
+   See the official Django documentation for more details.
+
+-  MIRROR
+
+   String. The alias of the database that this database should
+   mirror during testing. Default value is `None`.
+   See the official Django documentation for more details.
+
+### OPTIONS
+
+Dictionary. Current available keys are:
+
+-  driver
+
+   String. ODBC Driver to use (`"ODBC Driver 17 for SQL Server"`,
+   `"SQL Server Native Client 11.0"`, `"FreeTDS"` etc).
+   Default is `"ODBC Driver 17 for SQL Server"`.
+
+-  isolation_level
+
+   String. Sets [transaction isolation level](https://docs.microsoft.com/en-us/sql/t-sql/statements/set-transaction-isolation-level-transact-sql)
+   for each database session. Valid values for this entry are
+   `READ UNCOMMITTED`, `READ COMMITTED`, `REPEATABLE READ`,
+   `SNAPSHOT`, and `SERIALIZABLE`. Default is `None` which means
+   no isolation level is set to a database session and SQL Server default
+   will be used.
+
+-  dsn
+
+   String. A named DSN can be used instead of `HOST`.
+
+-  host_is_server
+
+   Boolean. Only relevant if using the FreeTDS ODBC driver under
+   Unix/Linux.
+
+   By default, when using the FreeTDS ODBC driver the value specified in
+   the ``HOST`` setting is used in a ``SERVERNAME`` ODBC connection
+   string component instead of being used in a ``SERVER`` component;
+   this means that this value should be the name of a *dataserver*
+   definition present in the ``freetds.conf`` FreeTDS configuration file
+   instead of a hostname or an IP address.
+
+   But if this option is present and its value is ``True``, this
+   special behavior is turned off. Instead, connections to the database
+   server will be established using ``HOST`` and ``PORT`` options, without
+   requiring ``freetds.conf`` to be configured.
+
+   See https://www.freetds.org/userguide/dsnless.html for more information.
+
+-  unicode_results
+
+   Boolean. If it is set to ``True``, pyodbc's *unicode_results* feature
+   is activated and strings returned from pyodbc are always Unicode.
+   Default value is ``False``.
+
+-  extra_params
+
+   String. Additional parameters for the ODBC connection. The format is
+   ``"param=value;param=value"``, [Azure AD Authentication](https://github.com/microsoft/mssql-django/wiki/Azure-AD-Authentication) (Service Principal, Interactive, Msi) can be added to this field.
+
+-  collation
+
+   String. Name of the collation to use when performing text field
+   lookups against the database. Default is ``None``; this means no
+   collation specifier is added to your lookup SQL (the default
+   collation of your database will be used). For Chinese language you
+   can set it to ``"Chinese_PRC_CI_AS"``.
+
+-  connection_timeout
+
+   Integer. Sets the timeout in seconds for the database connection process.
+   Default value is ``0`` which disables the timeout.
+
+-  connection_retries
+
+   Integer. Sets the times to retry the database connection process.
+   Default value is ``5``.
+
+-  connection_retry_backoff_time
+
+   Integer. Sets the back off time in seconds for reries of
+   the database connection process. Default value is ``5``.
+
+-  query_timeout
+
+   Integer. Sets the timeout in seconds for the database query.
+   Default value is ``0`` which disables the timeout.
+
+- [setencoding](https://github.com/mkleehammer/pyodbc/wiki/Connection#setencoding) and [setdecoding](https://github.com/mkleehammer/pyodbc/wiki/Connection#setdecoding)
+
+    ```python
+    # Example
+    "OPTIONS": {
+            "setdecoding": [
+                {"sqltype": pyodbc.SQL_CHAR, "encoding": 'utf-8'},
+                {"sqltype": pyodbc.SQL_WCHAR, "encoding": 'utf-8'}],
+            "setencoding": [
+                {"encoding": "utf-8"}],
+            ...
+            },
+    ```
+
+### Backend-specific settings
+
+The following project-level settings also control the behavior of the backend:
+
+-  DATABASE_CONNECTION_POOLING
+
+   Boolean. If it is set to ``False``, pyodbc's connection pooling feature
+   won't be activated.
+
+### Example
+
+Here is an example of the database settings:
+
+```python
+    DATABASES = {
+        'default': {
+            'ENGINE': 'mssql',
+            'NAME': 'mydb',
+            'USER': 'user@myserver',
+            'PASSWORD': 'password',
+            'HOST': 'myserver.database.windows.net',
+            'PORT': '',
+
+            'OPTIONS': {
+                'driver': 'ODBC Driver 17 for SQL Server',
+            },
+        },
+    }
+
+    # set this to False if you want to turn off pyodbc's connection pooling
+    DATABASE_CONNECTION_POOLING = False
+```
+
+## Limitations
+
+The following features are currently not fully supported:
+- Altering a model field from or to AutoField at migration
+- Django annotate functions have floating point arithmetic problems in some cases
+- Annotate function with exists
+- Exists function in order_by
+- Righthand power and arithmetic with datatimes
+- Timezones, timedeltas not fully supported
+- Rename field/model with foreign key constraint
+- Database level constraints
+- Math degrees power or radians
+- Bit-shift operators
+- Filtered index
+- Date extract function
+- Hashing functions
+
+JSONField lookups have limitations, more details [here](https://github.com/microsoft/mssql-django/wiki/JSONField).
+
+## Contributing
+
+More details on contributing can be found [here](CONTRIBUTING.md).
+
+This project welcomes contributions and suggestions.  Most contributions require you to agree to a
+Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
+the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
+
+When you submit a pull request, a CLA bot will automatically determine whether you need to provide
+a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
+provided by the bot. You will only need to do this once across all repos using our CLA.
+
+This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
+For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
+contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
+
+## Security Reporting Instructions
+
+For security reporting instructions please refer to the [`SECURITY.md`](SECURITY.md) file in this repository.
+
+## Trademarks
+
+This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft
+trademarks or logos is subject to and must follow
+[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
+Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
+Any use of third-party trademarks or logos are subject to those third-party's policies.
+
+
diff --git a/mssql_django.egg-info/SOURCES.txt b/mssql_django.egg-info/SOURCES.txt
new file mode 100644
index 0000000..f08d7f2
--- /dev/null
+++ b/mssql_django.egg-info/SOURCES.txt
@@ -0,0 +1,61 @@
+LICENSE.txt
+MANIFEST.in
+NOTICE.md
+README.md
+setup.cfg
+setup.py
+mssql/__init__.py
+mssql/base.py
+mssql/client.py
+mssql/compiler.py
+mssql/creation.py
+mssql/features.py
+mssql/functions.py
+mssql/introspection.py
+mssql/operations.py
+mssql/schema.py
+mssql/management/__init__.py
+mssql/management/commands/__init__.py
+mssql/management/commands/inspectdb.py
+mssql/management/commands/install_regex_clr.py
+mssql_django.egg-info/PKG-INFO
+mssql_django.egg-info/SOURCES.txt
+mssql_django.egg-info/dependency_links.txt
+mssql_django.egg-info/requires.txt
+mssql_django.egg-info/top_level.txt
+testapp/__init__.py
+testapp/models.py
+testapp/runners.py
+testapp/settings.py
+testapp/migrations/0001_initial.py
+testapp/migrations/0002_test_unique_nullable_part1.py
+testapp/migrations/0003_test_unique_nullable_part2.py
+testapp/migrations/0004_test_unique_type_change_part1.py
+testapp/migrations/0005_test_unique_type_change_part2.py
+testapp/migrations/0006_test_remove_onetoone_field_part1.py
+testapp/migrations/0007_test_remove_onetoone_field_part2.py
+testapp/migrations/0008_test_drop_table_with_foreign_key_reference_part1.py
+testapp/migrations/0009_test_drop_table_with_foreign_key_reference_part2.py
+testapp/migrations/0010_pizza_topping.py
+testapp/migrations/0011_test_unique_constraints.py
+testapp/migrations/0012_test_indexes_retained_part1.py
+testapp/migrations/0013_test_indexes_retained_part2.py
+testapp/migrations/0014_test_rename_m2mfield_part1.py
+testapp/migrations/0015_test_rename_m2mfield_part2.py
+testapp/migrations/0016_jsonmodel.py
+testapp/migrations/0017_binarydata_testcheckconstraintwithunicode_and_more.py
+testapp/migrations/0018_choice_question.py
+testapp/migrations/0019_customer_name_customer_address.py
+testapp/migrations/0020_autofield_to_bigautofield.py
+testapp/migrations/0021_multiple_autofield_to_bigauto.py
+testapp/migrations/0022_timezone.py
+testapp/migrations/__init__.py
+testapp/tests/__init__.py
+testapp/tests/test_constraints.py
+testapp/tests/test_expressions.py
+testapp/tests/test_fields.py
+testapp/tests/test_indexes.py
+testapp/tests/test_jsonfield.py
+testapp/tests/test_lookups.py
+testapp/tests/test_multiple_databases.py
+testapp/tests/test_timezones.py
\ No newline at end of file
diff --git a/mssql_django.egg-info/dependency_links.txt b/mssql_django.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/mssql_django.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/mssql_django.egg-info/requires.txt b/mssql_django.egg-info/requires.txt
new file mode 100644
index 0000000..884e004
--- /dev/null
+++ b/mssql_django.egg-info/requires.txt
@@ -0,0 +1,3 @@
+django<4.1,>=2.2
+pyodbc>=3.0
+pytz
diff --git a/mssql_django.egg-info/top_level.txt b/mssql_django.egg-info/top_level.txt
new file mode 100644
index 0000000..9f84960
--- /dev/null
+++ b/mssql_django.egg-info/top_level.txt
@@ -0,0 +1,2 @@
+mssql
+testapp
diff --git a/setup.cfg b/setup.cfg
index 918b972..ff1116c 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,9 @@
 [flake8]
 exclude = .git,__pycache__,migrations
-# W504 is mutually exclusive with W503
 ignore = W504
 max-line-length = 119
+
+[egg_info]
+tag_build = 
+tag_date = 0
+
diff --git a/test.sh b/test.sh
deleted file mode 100755
index 9fc96e0..0000000
--- a/test.sh
+++ /dev/null
@@ -1,114 +0,0 @@
-# TODO:
-#
-# * m2m_through_regress
-# * many_to_one_null
-
-set -e
-
-DJANGO_VERSION="$(python -m django --version)"
-
-cd django
-git fetch --depth=1 origin +refs/tags/*:refs/tags/*
-git checkout $DJANGO_VERSION
-pip install -r tests/requirements/py3.txt
-
-coverage run tests/runtests.py --settings=testapp.settings --noinput \
-    aggregation \
-    aggregation_regress \
-    annotations \
-    backends \
-    basic \
-    bulk_create \
-    constraints \
-    custom_columns \
-    custom_lookups \
-    custom_managers \
-    custom_methods \
-    custom_migration_operations \
-    custom_pk \
-    datatypes \
-    dates \
-    datetimes \
-    db_functions \
-    db_typecasts \
-    db_utils \
-    dbshell \
-    defer \
-    defer_regress \
-    delete \
-    delete_regress \
-    distinct_on_fields \
-    empty \
-    expressions \
-    expressions_case \
-    expressions_window \
-    extra_regress \
-    field_deconstruction \
-    field_defaults \
-    field_subclassing \
-    filtered_relation \
-    fixtures \
-    fixtures_model_package \
-    fixtures_regress \
-    force_insert_update \
-    foreign_object \
-    from_db_value \
-    generic_relations \
-    generic_relations_regress \
-    get_earliest_or_latest \
-    get_object_or_404 \
-    get_or_create \
-    indexes \
-    inspectdb \
-    introspection \
-    invalid_models_tests \
-    known_related_objects \
-    lookup \
-    m2m_and_m2o \
-    m2m_intermediary \
-    m2m_multiple \
-    m2m_recursive \
-    m2m_regress \
-    m2m_signals \
-    m2m_through \
-    m2o_recursive \
-    managers_regress \
-    many_to_many \
-    many_to_one \
-    max_lengths \
-    migrate_signals \
-    migration_test_data_persistence \
-    migrations \
-    migrations2 \
-    model_fields \
-    model_indexes \
-    model_options \
-    mutually_referential \
-    nested_foreign_keys \
-    null_fk \
-    null_fk_ordering \
-    null_queries \
-    one_to_one \
-    or_lookups \
-    order_with_respect_to \
-    ordering \
-    pagination \
-    prefetch_related \
-    queries \
-    queryset_pickle \
-    raw_query \
-    reverse_lookup \
-    save_delete_hooks \
-    schema \
-    select_for_update \
-    select_related \
-    select_related_onetoone \
-    select_related_regress \
-    serializers \
-    transaction_hooks \
-    transactions \
-    update \
-    update_only_fields
-
-python -m coverage xml --include '*mssql*' --omit '*virtualenvs*' -o coverage.xml
-
diff --git a/testapp/migrations/0020_autofield_to_bigautofield.py b/testapp/migrations/0020_autofield_to_bigautofield.py
new file mode 100644
index 0000000..c21472b
--- /dev/null
+++ b/testapp/migrations/0020_autofield_to_bigautofield.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.13 on 2022-05-04 01:36
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('testapp', '0019_customer_name_customer_address'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='author',
+            name='id',
+            field=models.BigAutoField(primary_key=True, serialize=False),
+        ),
+    ]
diff --git a/testapp/migrations/0021_multiple_autofield_to_bigauto.py b/testapp/migrations/0021_multiple_autofield_to_bigauto.py
new file mode 100644
index 0000000..e7a26b3
--- /dev/null
+++ b/testapp/migrations/0021_multiple_autofield_to_bigauto.py
@@ -0,0 +1,28 @@
+# Generated by Django 3.2.13 on 2022-05-04 01:37
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('testapp', '0020_autofield_to_bigautofield'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='author',
+            name='id',
+            field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
+        ),
+        migrations.AlterField(
+            model_name='editor',
+            name='id',
+            field=models.BigAutoField(primary_key=True, serialize=False),
+        ),
+        migrations.AlterField(
+            model_name='post',
+            name='id',
+            field=models.BigAutoField(primary_key=True, serialize=False),
+        ),
+    ]
diff --git a/testapp/migrations/0022_timezone.py b/testapp/migrations/0022_timezone.py
new file mode 100644
index 0000000..67e3b90
--- /dev/null
+++ b/testapp/migrations/0022_timezone.py
@@ -0,0 +1,21 @@
+# Generated by Django 4.0.4 on 2022-06-07 15:37
+
+from django.db import migrations, models
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('testapp', '0021_multiple_autofield_to_bigauto'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='TimeZone',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('date', models.DateTimeField(default=django.utils.timezone.now)),
+            ],
+        ),
+    ]
diff --git a/testapp/models.py b/testapp/models.py
index 382c349..9ce8546 100644
--- a/testapp/models.py
+++ b/testapp/models.py
@@ -9,16 +9,22 @@ from django.db import models
 from django.db.models import Q
 from django.utils import timezone
 
+# We are using this Mixin to test casting of BigAuto and Auto fields 
+class BigAutoFieldMixin(models.Model):
+    id = models.BigAutoField(primary_key=True)
+
+    class Meta:
+        abstract = True
 
 class Author(models.Model):
     name = models.CharField(max_length=100)
 
 
-class Editor(models.Model):
+class Editor(BigAutoFieldMixin, models.Model):
     name = models.CharField(max_length=100)
 
 
-class Post(models.Model):
+class Post(BigAutoFieldMixin, models.Model):
     title = models.CharField('title', max_length=255)
     author = models.ForeignKey(Author, models.CASCADE)
     # Optional secondary author
@@ -213,3 +219,6 @@ class Customer_address(models.Model):
     Customer_address = models.CharField(max_length=100)
     class Meta:
         ordering = ['Customer_address']
+
+class TimeZone(models.Model):
+    date = models.DateTimeField(default=timezone.now)
diff --git a/testapp/settings.py b/testapp/settings.py
index cc24fe6..9622e02 100644
--- a/testapp/settings.py
+++ b/testapp/settings.py
@@ -124,14 +124,12 @@ EXCLUDED_TESTS = [
     'migrations.test_operations.OperationTests.test_add_constraint_percent_escaping',
     'migrations.test_operations.OperationTests.test_alter_field_pk',
     'migrations.test_operations.OperationTests.test_alter_field_reloads_state_on_fk_with_to_field_target_changes',
-    'migrations.test_operations.OperationTests.test_autofield_foreignfield_growth',
     'schema.tests.SchemaTests.test_alter_auto_field_to_char_field',
     'schema.tests.SchemaTests.test_alter_auto_field_to_integer_field',
     'schema.tests.SchemaTests.test_alter_implicit_id_to_explicit',
     'schema.tests.SchemaTests.test_alter_int_pk_to_autofield_pk',
     'schema.tests.SchemaTests.test_alter_int_pk_to_bigautofield_pk',
     'schema.tests.SchemaTests.test_alter_pk_with_self_referential_field',
-    'schema.tests.SchemaTests.test_no_db_constraint_added_during_primary_key_change',
     'schema.tests.SchemaTests.test_remove_field_check_does_not_remove_meta_constraints',
     'schema.tests.SchemaTests.test_remove_field_unique_does_not_remove_meta_constraints',
     'schema.tests.SchemaTests.test_text_field_with_db_index',
@@ -139,7 +137,6 @@ EXCLUDED_TESTS = [
     'schema.tests.SchemaTests.test_unique_together_with_fk_with_existing_index',
     'aggregation.tests.AggregateTestCase.test_count_star',
     'aggregation_regress.tests.AggregationTests.test_values_list_annotation_args_ordering',
-    'datatypes.tests.DataTypesTestCase.test_error_on_timezone',
     'db_functions.math.test_degrees.DegreesTests.test_integer',
     'db_functions.math.test_power.PowerTests.test_integer',
     'db_functions.math.test_radians.RadiansTests.test_integer',
@@ -162,7 +159,6 @@ EXCLUDED_TESTS = [
     'schema.tests.SchemaTests.test_unique_no_unnecessary_fk_drops',
     'select_for_update.tests.SelectForUpdateTests.test_for_update_after_from',
     'backends.tests.LastExecutedQueryTest.test_last_executed_query',
-    'db_functions.datetime.test_now.NowTests.test_basic',
     'db_functions.datetime.test_extract_trunc.DateFunctionTests.test_extract_year_exact_lookup',
     'db_functions.datetime.test_extract_trunc.DateFunctionTests.test_extract_year_greaterthan_lookup',
     'db_functions.datetime.test_extract_trunc.DateFunctionTests.test_extract_year_lessthan_lookup',
@@ -179,34 +175,12 @@ EXCLUDED_TESTS = [
     'aggregation.tests.AggregateTestCase.test_aggregation_subquery_annotation_exists',
     'aggregation.tests.AggregateTestCase.test_aggregation_subquery_annotation_values_collision',
     'db_functions.datetime.test_extract_trunc.DateFunctionWithTimeZoneTests.test_extract_func_with_timezone',
-    'db_functions.text.test_md5.MD5Tests.test_basic',
-    'db_functions.text.test_md5.MD5Tests.test_transform',
-    'db_functions.text.test_sha1.SHA1Tests.test_basic',
-    'db_functions.text.test_sha1.SHA1Tests.test_transform',
-    'db_functions.text.test_sha224.SHA224Tests.test_basic',
-    'db_functions.text.test_sha224.SHA224Tests.test_transform',
-    'db_functions.text.test_sha256.SHA256Tests.test_basic',
-    'db_functions.text.test_sha256.SHA256Tests.test_transform',
-    'db_functions.text.test_sha384.SHA384Tests.test_basic',
-    'db_functions.text.test_sha384.SHA384Tests.test_transform',
-    'db_functions.text.test_sha512.SHA512Tests.test_basic',
-    'db_functions.text.test_sha512.SHA512Tests.test_transform',
-    'expressions.tests.BasicExpressionsTests.test_case_in_filter_if_boolean_output_field',
-    'expressions.tests.BasicExpressionsTests.test_subquery_in_filter',
     'expressions.tests.FTimeDeltaTests.test_date_subquery_subtraction',
     'expressions.tests.FTimeDeltaTests.test_datetime_subquery_subtraction',
     'expressions.tests.FTimeDeltaTests.test_time_subquery_subtraction',
-    'expressions.tests.BasicExpressionsTests.test_filtering_on_q_that_is_boolean',
     'migrations.test_operations.OperationTests.test_alter_field_reloads_state_on_fk_with_to_field_target_type_change',
-    'migrations.test_operations.OperationTests.test_autofield__bigautofield_foreignfield_growth',
-    'migrations.test_operations.OperationTests.test_smallfield_autofield_foreignfield_growth',
-    'migrations.test_operations.OperationTests.test_smallfield_bigautofield_foreignfield_growth',
-    'schema.tests.SchemaTests.test_alter_auto_field_quoted_db_column',
-    'schema.tests.SchemaTests.test_alter_autofield_pk_to_bigautofield_pk_sequence_owner',
-    'schema.tests.SchemaTests.test_alter_autofield_pk_to_smallautofield_pk_sequence_owner',
-    'schema.tests.SchemaTests.test_alter_primary_key_quoted_db_table',
     'schema.tests.SchemaTests.test_alter_smallint_pk_to_smallautofield_pk',
-
+    
     'annotations.tests.NonAggregateAnnotationTestCase.test_combined_expression_annotation_with_aggregation',
     'db_functions.comparison.test_cast.CastTests.test_cast_to_integer',
     'db_functions.datetime.test_extract_trunc.DateFunctionTests.test_extract_func',
@@ -279,7 +253,33 @@ EXCLUDED_TESTS = [
     'backends.tests.BackendTestCase.test_queries_logger',
     'migrations.test_operations.OperationTests.test_alter_field_pk_mti_fk',
     'migrations.test_operations.OperationTests.test_run_sql_add_missing_semicolon_on_collect_sql',
-    'migrations.test_operations.OperationTests.test_alter_field_pk_mti_and_fk_to_base'
+    'migrations.test_operations.OperationTests.test_alter_field_pk_mti_and_fk_to_base',
+
+    # Hashing
+    # UTF-8 support was added in SQL Server 2019
+    'db_functions.text.test_md5.MD5Tests.test_basic',
+    'db_functions.text.test_md5.MD5Tests.test_transform',
+    'db_functions.text.test_sha1.SHA1Tests.test_basic',
+    'db_functions.text.test_sha1.SHA1Tests.test_transform',
+    'db_functions.text.test_sha256.SHA256Tests.test_basic',
+    'db_functions.text.test_sha256.SHA256Tests.test_transform',
+    'db_functions.text.test_sha512.SHA512Tests.test_basic',
+    'db_functions.text.test_sha512.SHA512Tests.test_transform',
+    # SQL Server doesn't support SHA224 or SHA387
+    'db_functions.text.test_sha224.SHA224Tests.test_basic',
+    'db_functions.text.test_sha224.SHA224Tests.test_transform',
+    'db_functions.text.test_sha384.SHA384Tests.test_basic',
+    'db_functions.text.test_sha384.SHA384Tests.test_transform',
+
+    # Timezone
+    'timezones.tests.NewDatabaseTests.test_cursor_explicit_time_zone',
+    # Skipped next tests because pyodbc drops timezone https://github.com/mkleehammer/pyodbc/issues/810
+    'timezones.tests.LegacyDatabaseTests.test_cursor_execute_accepts_naive_datetime',
+    'timezones.tests.LegacyDatabaseTests.test_cursor_execute_returns_naive_datetime',
+    'timezones.tests.NewDatabaseTests.test_cursor_execute_accepts_naive_datetime',
+    'timezones.tests.NewDatabaseTests.test_cursor_execute_returns_naive_datetime',
+    'timezones.tests.NewDatabaseTests.test_cursor_execute_accepts_aware_datetime',
+    'timezones.tests.NewDatabaseTests.test_cursor_execute_returns_aware_datetime',
 ]
 
 REGEX_TESTS = [
diff --git a/testapp/tests/__init__.py b/testapp/tests/__init__.py
index e69de29..f3736c5 100644
--- a/testapp/tests/__init__.py
+++ b/testapp/tests/__init__.py
@@ -0,0 +1,17 @@
+import django.db
+
+
+def get_constraints(table_name):
+    connection = django.db.connections[django.db.DEFAULT_DB_ALIAS]
+    return connection.introspection.get_constraints(
+        connection.cursor(),
+        table_name=table_name,
+    )
+
+
+def get_constraint_names_where(table_name, **kwargs):
+    return [
+        name
+        for name, details in get_constraints(table_name=table_name).items()
+        if all(details[k] == v for k, v in kwargs.items())
+    ]
diff --git a/testapp/tests/test_constraints.py b/testapp/tests/test_constraints.py
index 1dda8a4..4e6f208 100644
--- a/testapp/tests/test_constraints.py
+++ b/testapp/tests/test_constraints.py
@@ -1,12 +1,15 @@
 # Copyright (c) Microsoft Corporation.
 # Licensed under the BSD license.
+import logging
 
+import django.db.utils
 from django.db import connections, migrations, models
 from django.db.migrations.state import ProjectState
 from django.db.utils import IntegrityError
 from django.test import TestCase, TransactionTestCase, skipUnlessDBFeature
 
 from mssql.base import DatabaseWrapper
+from . import get_constraint_names_where
 from ..models import (
     Author,
     Editor,
@@ -17,6 +20,8 @@ from ..models import (
     TestRenameManyToManyFieldModel,
 )
 
+logger = logging.getLogger('mssql.tests')
+
 
 @skipUnlessDBFeature('supports_nullable_unique_constraints')
 class TestNullableUniqueColumn(TestCase):
@@ -83,6 +88,82 @@ class TestPartiallyNullableUniqueTogether(TestCase):
             TestNullableUniqueTogetherModel.objects.create(a='aaa', b='bbb', c='ccc')
 
 
+class TestHandleOldStyleUniqueTogether(TransactionTestCase):
+    """
+    Regression test for https://github.com/microsoft/mssql-django/issues/137
+
+    Start with a unique_together which was created by an older version of this backend code, which implemented
+    it with a table CONSTRAINT instead of a filtered UNIQUE INDEX like the current code does.
+    e.g. django-mssql-backend < v2.6.0 or (before that) all versions of django-pyodbc-azure
+
+    Then alter the type of a column (e.g. max_length of CharField) which is part of that unique_together and
+    check that the (old-style) CONSTRAINT is dropped before (& a new-style UNIQUE INDEX created afterwards).
+    """
+    def test_drop_old_unique_together_constraint(self):
+        class TestMigrationA(migrations.Migration):
+            initial = True
+
+            operations = [
+                migrations.CreateModel(
+                    name='TestHandleOldStyleUniqueTogether',
+                    fields=[
+                        ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                        ('foo', models.CharField(max_length=50)),
+                        ('bar', models.CharField(max_length=50)),
+                    ],
+                ),
+                # Create the unique_together so that Django knows it exists, however we will deliberately drop
+                # it (filtered unique INDEX) below & manually replace with the old implementation (CONSTRAINT)
+                migrations.AlterUniqueTogether(
+                    name='testhandleoldstyleuniquetogether',
+                    unique_together={('foo', 'bar')}
+                ),
+            ]
+
+        class TestMigrationB(migrations.Migration):
+            operations = [
+                # Alter the type of the field to trigger the _alter_field code which drops/recreats indexes/constraints
+                migrations.AlterField(
+                    model_name='testhandleoldstyleuniquetogether',
+                    name='foo',
+                    field=models.CharField(max_length=99),
+                )
+            ]
+
+        migration_a = TestMigrationA(name='test_drop_old_unique_together_constraint_a', app_label='testapp')
+        migration_b = TestMigrationB(name='test_drop_old_unique_together_constraint_b', app_label='testapp')
+
+        connection = connections['default']
+
+        # Setup
+        with connection.schema_editor(atomic=True) as editor:
+            project_state = migration_a.apply(ProjectState(), editor)
+
+        # Manually replace the unique_together-enforcing INDEX with the old implementation using a CONSTRAINT instead
+        # to simulate the state of a database which had been migrated using an older version of this backend
+        table_name = 'testapp_testhandleoldstyleuniquetogether'
+        unique_index_names = get_constraint_names_where(table_name=table_name, index=True, unique=True)
+        assert len(unique_index_names) == 1
+        unique_together_name = unique_index_names[0]
+        logger.debug('Replacing UNIQUE INDEX %s with a CONSTRAINT of the same name', unique_together_name)
+        with connection.schema_editor(atomic=True) as editor:
+            # Out with the new
+            editor.execute('DROP INDEX [%s] ON [%s]' % (unique_together_name, table_name))
+            # In with the old, so that we end up in the state that an old database might be in
+            editor.execute('ALTER TABLE [%s] ADD CONSTRAINT [%s] UNIQUE ([foo], [bar])' % (table_name, unique_together_name))
+
+        # Test by running AlterField
+        with connection.schema_editor(atomic=True) as editor:
+            # If this doesn't explode then all is well. Without the bugfix, the CONSTRAINT wasn't dropped before,
+            # so then re-instating the unique_together using an INDEX of the same name (after altering the field)
+            # would fail due to the presence of a CONSTRAINT (really still an index under the hood) with that name.
+            try:
+                migration_b.apply(project_state, editor)
+            except django.db.utils.DatabaseError as e:
+                logger.exception('Failed to AlterField:')
+                self.fail('Check for regression of issue #137, AlterField failed with exception: %s' % e)
+
+
 class TestRenameManyToManyField(TestCase):
     def test_uniqueness_still_enforced_afterwards(self):
         # Issue https://github.com/microsoft/mssql-django/issues/86
@@ -139,7 +220,7 @@ class TestUniqueConstraints(TransactionTestCase):
                     ),
                 ]
 
-            migration = TestMigration('testapp', 'test_unsupportable_unique_constraint')
+            migration = TestMigration(name='test_unsupportable_unique_constraint', app_label='testapp')
 
             with connection.schema_editor(atomic=True) as editor:
                 with self.assertRaisesRegex(
diff --git a/testapp/tests/test_fields.py b/testapp/tests/test_fields.py
index 7b85d5e..7f708e8 100644
--- a/testapp/tests/test_fields.py
+++ b/testapp/tests/test_fields.py
@@ -20,3 +20,17 @@ class TestOrderBy(TestCase):
         names = Customer_name.objects.select_for_update().all()
         addresses = Customer_address.objects.filter(Customer_address='123 Main St', Customer_name__in=names)
         self.assertEqual(len(addresses), 1)
+
+    def test_random_order_by(self):
+        # https://code.djangoproject.com/ticket/33531
+        Customer_name.objects.bulk_create([
+            Customer_name(Customer_name='Jack'),
+            Customer_name(Customer_name='Jane'),
+            Customer_name(Customer_name='John'),
+        ])
+        names = []
+        # iterate 20 times to make sure we don't get the same result
+        for _ in range(20):
+            names.append(list(Customer_name.objects.order_by('?')))
+
+        self.assertNotEqual(names.count(names[0]), 20)
diff --git a/testapp/tests/test_indexes.py b/testapp/tests/test_indexes.py
index 9237e73..40100f6 100644
--- a/testapp/tests/test_indexes.py
+++ b/testapp/tests/test_indexes.py
@@ -8,6 +8,7 @@ from django.db.models import UniqueConstraint
 from django.db.utils import DEFAULT_DB_ALIAS, ConnectionHandler, ProgrammingError
 from django.test import TestCase
 
+from . import get_constraints
 from ..models import (
     TestIndexesRetainedRenamed,
     Choice,
@@ -26,14 +27,6 @@ else:
 logger = logging.getLogger('mssql.tests')
 
 
-def _get_constraints(table_name):
-    connection = django.db.connections[django.db.DEFAULT_DB_ALIAS]
-    return connection.introspection.get_constraints(
-        connection.cursor(),
-        table_name=table_name,
-    )
-
-
 class TestIndexesRetained(TestCase):
     """
     Issue https://github.com/microsoft/mssql-django/issues/14
@@ -46,7 +39,7 @@ class TestIndexesRetained(TestCase):
         super().setUpClass()
         # Pre-fetch which indexes exist for the relevant test model
         # now that all the test migrations have run
-        cls.constraints = _get_constraints(table_name=TestIndexesRetainedRenamed._meta.db_table)
+        cls.constraints = get_constraints(table_name=TestIndexesRetainedRenamed._meta.db_table)
         cls.indexes = {k: v for k, v in cls.constraints.items() if v['index'] is True}
 
     def _assert_index_exists(self, columns):
@@ -97,7 +90,7 @@ class TestCorrectIndexes(TestCase):
             if not model_cls._meta.managed:
                 # Models where the table is not managed by Django migrations are irrelevant
                 continue
-            model_constraints = _get_constraints(table_name=model_cls._meta.db_table)
+            model_constraints = get_constraints(table_name=model_cls._meta.db_table)
             # Check correct indexes are in place for all fields in model
             for field in model_cls._meta.get_fields():
                 if not hasattr(field, 'column'):
diff --git a/testapp/tests/test_timezones.py b/testapp/tests/test_timezones.py
new file mode 100644
index 0000000..a4c6104
--- /dev/null
+++ b/testapp/tests/test_timezones.py
@@ -0,0 +1,23 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the BSD license.
+
+import datetime
+
+from django.test import TestCase
+
+from ..models import TimeZone
+
+class TestDateTimeField(TestCase):
+
+    def test_iso_week_day(self):
+        days = {
+            1: TimeZone.objects.create(date=datetime.datetime(2022, 5, 16)),
+            2: TimeZone.objects.create(date=datetime.datetime(2022, 5, 17)),
+            3: TimeZone.objects.create(date=datetime.datetime(2022, 5, 18)),
+            4: TimeZone.objects.create(date=datetime.datetime(2022, 5, 19)),
+            5: TimeZone.objects.create(date=datetime.datetime(2022, 5, 20)),
+            6: TimeZone.objects.create(date=datetime.datetime(2022, 5, 21)),
+            7: TimeZone.objects.create(date=datetime.datetime(2022, 5, 22)),
+        }
+        for k, v in days.items():
+            self.assertSequenceEqual(TimeZone.objects.filter(date__iso_week_day=k), [v])
diff --git a/tox.ini b/tox.ini
deleted file mode 100644
index 5dfb674..0000000
--- a/tox.ini
+++ /dev/null
@@ -1,27 +0,0 @@
-[tox]
-envlist =
-       {py36,py37}-django22,
-       {py36,py37,py38,py39}-django30,
-       {py36,py37,py38,py39}-django31,
-       {py36,py37,py38,py39}-django32,
-       {py38, py39, py310}-django40
-
-[testenv]
-allowlist_externals =
-    /bin/bash
-    /usr/bin/bash
-    C:\Program Files\Git\bin\bash.EXE
-
-commands =
-    python manage.py test --noinput
-    bash test.sh
-
-deps =
-    coverage==5.5
-    unittest-xml-reporting
-
-    django22: django==2.2.*
-    django30: django>=3.0,<3.1
-    django31: django>=3.1,<3.2
-    django32: django==3.2.*
-    django40: django>=4.0a1,<4.1