from django.db import models from django import forms from django.core import exceptions import math class PositiveBigIntegerField(models.BigIntegerField): description = "Positive Big integer" def formfield(self, **kwargs): defaults = {'min_value': 0, 'max_value': models.BigIntegerField.MAX_BIGINT * 2 - 1} defaults.update(kwargs) return super(PositiveBigIntegerField, self).formfield(**defaults) def db_type(self, connection): if 'mysql' in connection.__class__.__module__: return 'bigint UNSIGNED' return super(PositiveBigIntegerField, self).db_type(connection) class PositiveBigAutoField(models.AutoField): description = "Unsigned Big Integer" empty_strings_allowed = False MAX_BIGINT = 9223372036854775807 def db_type(self, connection): if 'mysql' in connection.__class__.__module__: return 'bigint UNSIGNED AUTO_INCREMENT' return super(PositiveBigAutoField, self).db_type(connection) default_error_messages = { 'invalid': "'%(value)s' value must be an integer.", } def get_prep_value(self, value): if value is None: return None return int(value) def get_prep_lookup(self, lookup_type, value): if ((lookup_type == 'gte' or lookup_type == 'lt') and isinstance(value, float)): value = math.ceil(value) return super(PositiveBigAutoField, self).get_prep_lookup(lookup_type, value) def to_python(self, value): if value is None: return value try: return int(value) except (TypeError, ValueError): raise exceptions.ValidationError( self.error_messages['invalid'], code='invalid', params={'value': value}, ) def formfield(self, **kwargs): defaults = {'min_value': 0, 'max_value': PositiveBigAutoField.MAX_BIGINT * 2 - 1, 'form_class': forms.IntegerField } defaults.update(kwargs) return super(PositiveBigAutoField, self).formfield(**defaults) class PositiveBigAutoForeignKey(models.ForeignKey): """A special foriegn key field for positive big auto fields""" def db_type(self, connection): # The database column type of a ForeignKey is the column type # of the field to which it points. An exception is if the ForeignKey # points to an AutoField/PositiveIntegerField/PositiveSmallIntegerField, # in which case the column type is simply that of an IntegerField. # If the database needs similar types for key fields however, the only # thing we can do is making AutoField an IntegerField. rel_field = self.related_field if isinstance(rel_field, PositiveBigAutoField): return PositiveBigIntegerField().db_type(connection=connection) return rel_field.db_type(connection=connection) try: # If we are using south, we need some rules to use these fields from south.modelsinspector import add_introspection_rules add_introspection_rules([], ["^twitter_stream\.fields\.PositiveBigAutoField"]) add_introspection_rules([], ["^twitter_stream\.fields\.PositiveBigIntegerField"]) add_introspection_rules([], ["^twitter_stream\.fields\.PositiveBigAutoForeignKey"]) except ImportError: pass