Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add DateTimeField in django without microsecond

I'm writing django application in django 1.8 and mysql 5.7.

Below is the model which I have written:

class People(models.Model):
    name = models.CharField(max_length=20)
    age = models.IntegerField()
    create_time = models.DateTimeField()

    class Meta:
        db_table = "people"

Above model creates the table below:

mysql> desc people;
+-------------+-------------+------+-----+---------+----------------+
| Field       | Type        | Null | Key | Default | Extra          |
+-------------+-------------+------+-----+---------+----------------+
| id          | int(11)     | NO   | PRI | NULL    | auto_increment |
| name        | varchar(20) | NO   |     | NULL    |                |
| age         | int(11)     | NO   |     | NULL    |                | 
| create_time | datetime(6) | NO   |     | NULL    |                |
+-------------+-------------+------+-----+---------+----------------+

Here Django creates datetime field with microsecond

datetime(6)

But I want datetime field without microsecond

datetime

I have another application, which is also using the same database and that datetime field with microsecond is raising an issue for me.

like image 243
Lalitkumar Tarsariya Avatar asked Oct 03 '17 07:10

Lalitkumar Tarsariya


1 Answers

This is really very interesting question. I looked through the source code and here is the reason for setting the datetime with fractional seconds. The following snippet is from the file django/db/backends/mysql/base.py:

class DatabaseWrapper(BaseDatabaseWrapper):
    vendor = 'mysql'
    # This dictionary maps Field objects to their associated MySQL column
    # types, as strings. Column-type strings can contain format strings; they'll
    # be interpolated against the values of Field.__dict__ before being output.
    # If a column type is set to None, it won't be included in the output.
    _data_types = {
        'AutoField': 'integer AUTO_INCREMENT',
        'BinaryField': 'longblob',
        'BooleanField': 'bool',
        'CharField': 'varchar(%(max_length)s)',
        'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
        'DateField': 'date',
        'DateTimeField': 'datetime',
        'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
        'DurationField': 'bigint',
        'FileField': 'varchar(%(max_length)s)',
        'FilePathField': 'varchar(%(max_length)s)',
        'FloatField': 'double precision',
        'IntegerField': 'integer',
        'BigIntegerField': 'bigint',
        'IPAddressField': 'char(15)',
        'GenericIPAddressField': 'char(39)',
        'NullBooleanField': 'bool',
        'OneToOneField': 'integer',
        'PositiveIntegerField': 'integer UNSIGNED',
        'PositiveSmallIntegerField': 'smallint UNSIGNED',
        'SlugField': 'varchar(%(max_length)s)',
        'SmallIntegerField': 'smallint',
        'TextField': 'longtext',
        'TimeField': 'time',
        'UUIDField': 'char(32)',
    }

    @cached_property
    def data_types(self):
        if self.features.supports_microsecond_precision:
            return dict(self._data_types, DateTimeField='datetime(6)', TimeField='time(6)')
        else:
            return self._data_types

    # ... further class methods

In the method data_types the if condition checks the MySQL version. The method supports_microsecond_precision comes from the file django/db/backends/mysql/features.py:

class DatabaseFeatures(BaseDatabaseFeatures):
    # ... properties and methods

    def supports_microsecond_precision(self):                                         
        # See https://github.com/farcepest/MySQLdb1/issues/24 for the reason          
        # about requiring MySQLdb 1.2.5                                               
        return self.connection.mysql_version >= (5, 6, 4) and Database.version_info >= (1, 2, 5)

So when you use MySQL 5.6.4 or higher the field DateTimeField is mapped to datetime(6).

I couldn't find any possibility given by Django to adjust this, so ended up with monkey patching:

from django.db.backends.mysql.base import DatabaseWrapper

DatabaseWrapper.data_types = DatabaseWrapper._data_types

Put the above code where it suits best your needs, be it models.py or __init__.py, or maybe some other file. When running migrations Django will create column datetime and not datetime(6) for DateTimeField, even if you're using MySQL 5.7.

like image 126
cezar Avatar answered Sep 21 '22 03:09

cezar