简体   繁体   中英

Filter objects over 0000-00-00 date in Django

I want to simply get objects which contract_time set to "0000-00-00" I've tried:

users = MonitoringUsers.objects.filter(contract_time = "0000-00-00")

But it throws Validation Error: year is out of range

I connect to the different system, I cannot change how this objects are stored.

Python can't convert this string to correct date object so you have to use queryset's extra method:

users = MonitoringUsers.objects.extra(where=["contract_time='0000-00-00'"])

To prevent errors while loading such records you have to defer this field:

users = MonitoringUsers.objects.defer('contract_time').all()

You can iterate this queryset as usual but if you will try to access user.contract_time for "0000-00-00" record you will get the same ValueError . To solve this issue add a simple property to MonitoringUsers :

class MonitoringUsers(models.Model):
    ...
    @property
    def contract_time_or_none(self):
        try:
            return self.contract_time
        except ValueError:
            return None

Your template in this case will look like:

<ul>
{% for user in users %}
    <li>
        {{ user }} -
        {{ user.contract_time_or_none|default:"END of contract" }}
    </li>
{% endif %}
</ul>

But you should understand that loading of deferred field means additional db hit for each instance of MonitoringUsers . If you want to avoid this then you have to convert date-to-string at SQL level. I don't know which SQL server you use so here is example for SQLite:

users = MonitoringUsers.objects.defer('contract_time').extra(
           select={'contract_time_str':
                   'CASE WHEN contract_time="0000-00-00" '
                         'THEN "END of contract" '
                         'ELSE strftime("%Y-%m-%d", contract_time) '
                   'END'})

With this solution you can output contract_time_str as is:

<ul>
{% for user in users %}
    <li>
        {{ user }} - {{ user.contract_time_str }}
    </li>
{% endif %}
</ul>

Of course in this scenario there will be no additional queries to database.

We used to have a table in MySQL with 0000-00-00 in date fields. Our work around wasn't quite as complicated as @catavaran's answer.

Like in the question, we got a ValidationError when trying MonitoringUsers.objects.filter(contract_time="0000-00-00") .

So to filter these users, we could use extra() as @catavaran suggests.

When accessing contract_time for a record with 0000-00-00 , we found that the Django ORM returned None

>>> users = MonitoringUsers.objects.extra(where=["contract_time='0000-00-00'"])
>>> for user in users:
...     print repr(user.contract_time) 
None

Therefore, in the template, we could simply use:

{{ user.contract_time|default:"END of contract" }}

and we didn't have to do a defer() or add a select argument to extra.

This is simply what we found. Different MySQL versions/configurations may vary. Eventually, we were able to convert all the 0000-00-00 s to nulls.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM