6
6
from django .apps import apps # Django >= 1.7
7
7
except ImportError :
8
8
apps = None
9
- from django .db import models
9
+ from django .db import models , router
10
10
from django .db .models .fields .related import RelatedField
11
11
from django .db .models .related import RelatedObject
12
12
from django .conf import settings
13
13
from django .contrib import admin
14
- from django .utils import importlib
14
+ from django .utils import importlib , six
15
+ from django .utils .encoding import python_2_unicode_compatible
15
16
try :
16
17
from django .utils .encoding import smart_text
17
18
except ImportError :
18
- smart_text = unicode
19
- try :
20
- from django .utils .six import text_type
21
- except ImportError :
22
- text_type = unicode
19
+ from django .utils .encoding import smart_unicode as smart_text
23
20
try :
24
21
from django .utils .timezone import now
25
22
except ImportError :
28
25
from django .utils .translation import string_concat
29
26
from .manager import HistoryDescriptor
30
27
31
- try :
32
- basestring
33
- except NameError :
34
- basestring = str # Python 3 has no basestring
35
-
36
- try :
37
- from django .utils .encoding import python_2_unicode_compatible
38
- except ImportError : # django 1.3 compatibility
39
- import sys
40
-
41
- # copy of django function without use of six
42
- def python_2_unicode_compatible (klass ):
43
- """
44
- Decorator defining __unicode__ and __str__ as appropriate for Py2/3
45
-
46
- Usage: define __str__ method and apply this decorator to the class.
47
- """
48
- if sys .version_info [0 ] != 3 :
49
- klass .__unicode__ = klass .__str__
50
- klass .__str__ = lambda self : self .__unicode__ ().encode ('utf-8' )
51
- return klass
52
-
53
-
54
28
registered_models = {}
55
29
56
30
@@ -62,7 +36,7 @@ def __init__(self, verbose_name=None, bases=(models.Model,),
62
36
self .user_set_verbose_name = verbose_name
63
37
self .user_related_name = user_related_name
64
38
try :
65
- if isinstance (bases , basestring ):
39
+ if isinstance (bases , six . string_types ):
66
40
raise TypeError
67
41
self .bases = tuple (bases )
68
42
except TypeError :
@@ -149,7 +123,7 @@ def copy_fields(self, model):
149
123
# Don't allow reverse relations.
150
124
# ForeignKey knows best what datatype to use for the column
151
125
# we'll used that as soon as it's finalized by copying rel.to
152
- field .__class__ = get_custom_fk_class ( type ( field ))
126
+ field .__class__ = CustomForeignKeyField
153
127
field .rel .related_name = '+'
154
128
field .null = True
155
129
field .blank = True
@@ -243,7 +217,13 @@ def get_history_user(self, instance):
243
217
return None
244
218
245
219
246
- class ForeignKeyMixin (object ):
220
+ class CustomForeignKeyField (models .ForeignKey ):
221
+
222
+ def __init__ (self , * args , ** kwargs ):
223
+ super (CustomForeignKeyField , self ).__init__ (* args , ** kwargs )
224
+ self .db_constraint = False
225
+ self .generate_reverse_relation = False
226
+
247
227
def get_attname (self ):
248
228
return self .name
249
229
@@ -253,7 +233,7 @@ def get_one_to_one_field(self, to_field, other):
253
233
# recursive
254
234
temp_field = self .__class__ (to_field .rel .to ._meta .object_name )
255
235
for key , val in to_field .__dict__ .items ():
256
- if (isinstance (key , basestring )
236
+ if (isinstance (key , six . string_types )
257
237
and not key .startswith ('_' )):
258
238
setattr (temp_field , key , val )
259
239
field = self .__class__ .get_field (
@@ -268,7 +248,7 @@ def get_field(self, other, cls):
268
248
if isinstance (to_field , models .OneToOneField ):
269
249
field = self .get_one_to_one_field (to_field , other )
270
250
elif isinstance (to_field , models .AutoField ):
271
- field .__class__ = models . IntegerField
251
+ field .__class__ = convert_auto_field ( to_field )
272
252
else :
273
253
field .__class__ = to_field .__class__
274
254
excluded_prefixes = ("_" , "__" )
@@ -294,7 +274,7 @@ def get_field(self, other, cls):
294
274
"blank" ,
295
275
)
296
276
for key , val in to_field .__dict__ .items ():
297
- if (isinstance (key , basestring )
277
+ if (isinstance (key , six . string_types )
298
278
and not key .startswith (excluded_prefixes )
299
279
and key not in excluded_attributes ):
300
280
setattr (field , key , val )
@@ -303,7 +283,13 @@ def get_field(self, other, cls):
303
283
def do_related_class (self , other , cls ):
304
284
field = self .get_field (other , cls )
305
285
if not hasattr (self , 'related' ):
306
- self .related = RelatedObject (other , cls .instance_type , self )
286
+ try :
287
+ instance_type = cls .instance_type
288
+ except AttributeError : # when model is reconstituted for migration
289
+ if cls .__module__ != "__fake__" : # not from migrations, error
290
+ raise
291
+ else :
292
+ self .related = RelatedObject (other , instance_type , self )
307
293
transform_field (field )
308
294
field .rel = None
309
295
@@ -312,17 +298,12 @@ def contribute_to_class(self, cls, name):
312
298
RelatedField .contribute_to_class (self , cls , name )
313
299
314
300
315
- def get_custom_fk_class (parent_type ):
316
- return type (str ('CustomForeignKey' ), (ForeignKeyMixin , parent_type ), {})
317
-
318
-
319
301
def transform_field (field ):
320
302
"""Customize field appropriately for use in historical model"""
321
303
field .name = field .attname
322
304
if isinstance (field , models .AutoField ):
323
- # The historical model gets its own AutoField, so any
324
- # existing one must be replaced with an IntegerField.
325
- field .__class__ = models .IntegerField
305
+ field .__class__ = convert_auto_field (field )
306
+
326
307
elif isinstance (field , models .FileField ):
327
308
# Don't copy file, just path.
328
309
field .__class__ = models .TextField
@@ -340,6 +321,19 @@ def transform_field(field):
340
321
field .serialize = True
341
322
342
323
324
+ def convert_auto_field (field ):
325
+ """Convert AutoField to a non-incrementing type
326
+
327
+ The historical model gets its own AutoField, so any existing one
328
+ must be replaced with an IntegerField.
329
+ """
330
+ connection = router .db_for_write (field .model )
331
+ if settings .DATABASES [connection ]['ENGINE' ] in ('django_mongodb_engine' ,):
332
+ # Check if AutoField is string for django-non-rel support
333
+ return models .TextField
334
+ return models .IntegerField
335
+
336
+
343
337
class HistoricalObjectDescriptor (object ):
344
338
def __init__ (self , model ):
345
339
self .model = model
0 commit comments