Ticket #7052: t7052-rc2.diff
File t7052-rc2.diff, 60.5 KB (added by , 15 years ago) |
---|
-
django/contrib/auth/models.py
diff -r e0fb67da6521 django/contrib/auth/models.py
a b 47 47 class SiteProfileNotAvailable(Exception): 48 48 pass 49 49 50 class PermissionManager(models.Manager): 51 def get_by_natural_key(self, codename, app_label, model): 52 return self.get( 53 codename=codename, 54 content_type=ContentType.objects.get_by_natural_key(app_label, model) 55 ) 56 50 57 class Permission(models.Model): 51 58 """The permissions system provides a way to assign permissions to specific users and groups of users. 52 59 … … 63 70 name = models.CharField(_('name'), max_length=50) 64 71 content_type = models.ForeignKey(ContentType) 65 72 codename = models.CharField(_('codename'), max_length=100) 73 objects = PermissionManager() 66 74 67 75 class Meta: 68 76 verbose_name = _('permission') … … 76 84 unicode(self.content_type), 77 85 unicode(self.name)) 78 86 87 def natural_key(self): 88 return (self.codename,) + self.content_type.natural_key() 89 natural_key.dependencies = ['contenttypes.contenttype'] 90 79 91 class Group(models.Model): 80 92 """Groups are a generic way of categorizing users to apply permissions, or some other label, to those users. A user can belong to any number of groups. 81 93 -
django/contrib/contenttypes/models.py
diff -r e0fb67da6521 django/contrib/contenttypes/models.py
a b 8 8 # This cache is shared by all the get_for_* methods. 9 9 _cache = {} 10 10 11 def get_by_natural_key(self, app_label, model): 12 try: 13 ct = self.__class__._cache[(app_label, model)] 14 except KeyError: 15 ct = self.get(app_label=app_label, model=model) 16 return ct 17 11 18 def get_for_model(self, model): 12 19 """ 13 20 Returns the ContentType object for a given model, creating the … … 93 100 so code that calls this method should catch it. 94 101 """ 95 102 return self.model_class()._default_manager.get(**kwargs) 103 104 def natural_key(self): 105 return (self.app_label, self.model) -
django/core/management/commands/dumpdata.py
diff -r e0fb67da6521 django/core/management/commands/dumpdata.py
a b 13 13 help='Specifies the indent level to use when pretty-printing output'), 14 14 make_option('-e', '--exclude', dest='exclude',action='append', default=[], 15 15 help='App to exclude (use multiple --exclude to exclude multiple apps).'), 16 make_option('-n', '--natural', action='store_true', dest='use_natural_keys', default=False, 17 help='Use natural keys if they are available.'), 16 18 ) 17 19 help = 'Output the contents of the database as a fixture of the given format.' 18 20 args = '[appname ...]' … … 24 26 indent = options.get('indent',None) 25 27 exclude = options.get('exclude',[]) 26 28 show_traceback = options.get('traceback', False) 29 use_natural_keys = options.get('use_natural_keys', False) 27 30 28 31 excluded_apps = [get_app(app_label) for app_label in exclude] 29 32 … … 67 70 except KeyError: 68 71 raise CommandError("Unknown serialization format: %s" % format) 69 72 73 # Now collate the objects to be serialized. 70 74 objects = [] 71 for app, model_list in app_list.items(): 72 if model_list is None: 73 model_list = get_models(app) 74 75 for model in model_list: 76 if not model._meta.proxy: 77 objects.extend(model._default_manager.all()) 75 for model in sort_dependencies(app_list.items()): 76 if not model._meta.proxy: 77 objects.extend(model._default_manager.all()) 78 78 79 79 try: 80 return serializers.serialize(format, objects, indent=indent) 80 return serializers.serialize(format, objects, indent=indent, 81 use_natural_keys=use_natural_keys) 81 82 except Exception, e: 82 83 if show_traceback: 83 84 raise 84 85 raise CommandError("Unable to serialize database: %s" % e) 86 87 def sort_dependencies(app_list): 88 """Sort a list of app,modellist pairs into a single list of models. 89 90 The single list of models is sorted so that any model with a natural key 91 is serialized before a normal model, and any model with a natural key 92 dependency has it's dependencies serialized first. 93 """ 94 from django.db.models import get_model, get_models 95 # Process the list of models, and get the list of dependencies 96 model_dependencies = [] 97 models = set() 98 for app, model_list in app_list: 99 if model_list is None: 100 model_list = get_models(app) 101 102 for model in model_list: 103 models.add(model) 104 # Add any explicitly defined dependencies 105 if hasattr(model, 'natural_key'): 106 deps = getattr(model.natural_key, 'dependencies', []) 107 if deps: 108 deps = [get_model(*d.split('.')) for d in deps] 109 else: 110 deps = [] 111 112 # Now add a dependency for any FK or M2M relation with 113 # a model that defines a natural key 114 for field in model._meta.fields: 115 if hasattr(field.rel, 'to'): 116 rel_model = field.rel.to 117 if hasattr(rel_model, 'natural_key'): 118 deps.append(rel_model) 119 for field in model._meta.many_to_many: 120 rel_model = field.rel.to 121 if hasattr(rel_model, 'natural_key'): 122 deps.append(rel_model) 123 model_dependencies.append((model, deps)) 124 125 model_dependencies.reverse() 126 # Now sort the models to ensure that dependencies are met. This 127 # is done by repeatedly iterating over the input list of models. 128 # If all the dependencies of a given model are in the final list, 129 # that model is promoted to the end of the final list. This process 130 # continues until the input list is empty, or we do a full iteration 131 # over the input models without promoting a model to the final list. 132 # If we do a full iteration without a promotion, that means there are 133 # circular dependencies in the list. 134 model_list = [] 135 while model_dependencies: 136 skipped = [] 137 changed = False 138 while model_dependencies: 139 model, deps = model_dependencies.pop() 140 if all((d not in models or d in model_list) for d in deps): 141 # If all of the models in the dependency list are either already 142 # on the final model list, or not on the original serialization list, 143 # then we've found another model with all it's dependencies satisfied. 144 model_list.append(model) 145 changed = True 146 else: 147 skipped.append((model, deps)) 148 if not changed: 149 raise CommandError("Can't resolve dependencies for %s in serialized app list." % 150 ', '.join('%s.%s' % (model._meta.app_label, model._meta.object_name) 151 for model, deps in sorted(skipped, key=lambda obj: obj[0].__name__)) 152 ) 153 model_dependencies = skipped 154 155 return model_list -
django/core/serializers/base.py
diff -r e0fb67da6521 django/core/serializers/base.py
a b 33 33 34 34 self.stream = options.get("stream", StringIO()) 35 35 self.selected_fields = options.get("fields") 36 self.use_natural_keys = options.get("use_natural_keys", False) 36 37 37 38 self.start_serialization() 38 39 for obj in queryset: -
django/core/serializers/json.py
diff -r e0fb67da6521 django/core/serializers/json.py
a b 24 24 def end_serialization(self): 25 25 self.options.pop('stream', None) 26 26 self.options.pop('fields', None) 27 self.options.pop('use_natural_keys', None) 27 28 simplejson.dump(self.objects, self.stream, cls=DjangoJSONEncoder, **self.options) 28 29 29 30 def getvalue(self): -
django/core/serializers/python.py
diff -r e0fb67da6521 django/core/serializers/python.py
a b 47 47 def handle_fk_field(self, obj, field): 48 48 related = getattr(obj, field.name) 49 49 if related is not None: 50 if field.rel.field_name == related._meta.pk.name: 51 # Related to remote object via primary key 52 related = related._get_pk_val() 50 if self.use_natural_keys and hasattr(related, 'natural_key'): 51 related = related.natural_key() 53 52 else: 54 # Related to remote object via other field 55 related = getattr(related, field.rel.field_name) 56 self._current[field.name] = smart_unicode(related, strings_only=True) 53 if field.rel.field_name == related._meta.pk.name: 54 # Related to remote object via primary key 55 related = related._get_pk_val() 56 else: 57 # Related to remote object via other field 58 related = smart_unicode(getattr(related, field.rel.field_name), strings_only=True) 59 self._current[field.name] = related 57 60 58 61 def handle_m2m_field(self, obj, field): 59 62 if field.rel.through._meta.auto_created: 60 self._current[field.name] = [smart_unicode(related._get_pk_val(), strings_only=True) 63 if self.use_natural_keys and hasattr(field.rel.to, 'natural_key'): 64 m2m_value = lambda value: value.natural_key() 65 else: 66 m2m_value = lambda value: smart_unicode(value._get_pk_val(), strings_only=True) 67 self._current[field.name] = [m2m_value(related) 61 68 for related in getattr(obj, field.name).iterator()] 62 69 63 70 def getvalue(self): … … 86 93 87 94 # Handle M2M relations 88 95 if field.rel and isinstance(field.rel, models.ManyToManyRel): 89 m2m_convert = field.rel.to._meta.pk.to_python 90 m2m_data[field.name] = [m2m_convert(smart_unicode(pk)) for pk in field_value] 96 if hasattr(field.rel.to._default_manager, 'get_by_natural_key'): 97 def m2m_convert(value): 98 if hasattr(value, '__iter__'): 99 return field.rel.to._default_manager.get_by_natural_key(*value).pk 100 else: 101 return smart_unicode(field.rel.to._meta.pk.to_python(value)) 102 else: 103 m2m_convert = lambda v: smart_unicode(field.rel.to._meta.pk.to_python(v)) 104 m2m_data[field.name] = [m2m_convert(pk) for pk in field_value] 91 105 92 106 # Handle FK fields 93 107 elif field.rel and isinstance(field.rel, models.ManyToOneRel): 94 108 if field_value is not None: 95 data[field.attname] = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value) 109 if hasattr(field.rel.to._default_manager, 'get_by_natural_key'): 110 if hasattr(field_value, '__iter__'): 111 obj = field.rel.to._default_manager.get_by_natural_key(*field_value) 112 value = getattr(obj, field.rel.field_name) 113 else: 114 value = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value) 115 data[field.attname] = value 116 else: 117 data[field.attname] = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value) 96 118 else: 97 119 data[field.attname] = None 98 120 -
django/core/serializers/pyyaml.py
diff -r e0fb67da6521 django/core/serializers/pyyaml.py
a b 26 26 """ 27 27 Convert a queryset to YAML. 28 28 """ 29 29 30 30 internal_use_only = False 31 31 32 32 def handle_field(self, obj, field): 33 33 # A nasty special case: base YAML doesn't support serialization of time 34 34 # types (as opposed to dates or datetimes, which it does support). Since … … 40 40 self._current[field.name] = str(getattr(obj, field.name)) 41 41 else: 42 42 super(Serializer, self).handle_field(obj, field) 43 43 44 44 def end_serialization(self): 45 45 self.options.pop('stream', None) 46 46 self.options.pop('fields', None) 47 self.options.pop('use_natural_keys', None) 47 48 yaml.dump(self.objects, self.stream, Dumper=DjangoSafeDumper, **self.options) 48 49 49 50 def getvalue(self): -
django/core/serializers/xml_serializer.py
diff -r e0fb67da6521 django/core/serializers/xml_serializer.py
a b 81 81 self._start_relational_field(field) 82 82 related = getattr(obj, field.name) 83 83 if related is not None: 84 if field.rel.field_name == related._meta.pk.name: 85 # Related to remote object via primary key 86 related = related._get_pk_val() 84 if self.use_natural_keys and hasattr(related, 'natural_key'): 85 # If related object has a natural key, use it 86 related = related.natural_key() 87 # Iterable natural keys are rolled out as subelements 88 for key_value in related: 89 self.xml.startElement("natural", {}) 90 self.xml.characters(smart_unicode(key_value)) 91 self.xml.endElement("natural") 87 92 else: 88 # Related to remote object via other field 89 related = getattr(related, field.rel.field_name) 90 self.xml.characters(smart_unicode(related)) 93 if field.rel.field_name == related._meta.pk.name: 94 # Related to remote object via primary key 95 related = related._get_pk_val() 96 else: 97 # Related to remote object via other field 98 related = getattr(related, field.rel.field_name) 99 self.xml.characters(smart_unicode(related)) 91 100 else: 92 101 self.xml.addQuickElement("None") 93 102 self.xml.endElement("field") … … 100 109 """ 101 110 if field.rel.through._meta.auto_created: 102 111 self._start_relational_field(field) 112 if self.use_natural_keys and hasattr(field.rel.to, 'natural_key'): 113 # If the objects in the m2m have a natural key, use it 114 def handle_m2m(value): 115 natural = value.natural_key() 116 # Iterable natural keys are rolled out as subelements 117 self.xml.startElement("object", {}) 118 for key_value in natural: 119 self.xml.startElement("natural", {}) 120 self.xml.characters(smart_unicode(key_value)) 121 self.xml.endElement("natural") 122 self.xml.endElement("object") 123 else: 124 def handle_m2m(value): 125 self.xml.addQuickElement("object", attrs={ 126 'pk' : smart_unicode(value._get_pk_val()) 127 }) 103 128 for relobj in getattr(obj, field.name).iterator(): 104 self.xml.addQuickElement("object", attrs={"pk" : smart_unicode(relobj._get_pk_val())}) 129 handle_m2m(relobj) 130 105 131 self.xml.endElement("field") 106 132 107 133 def _start_relational_field(self, field): … … 187 213 if node.getElementsByTagName('None'): 188 214 return None 189 215 else: 190 return field.rel.to._meta.get_field(field.rel.field_name).to_python( 191 getInnerText(node).strip()) 216 if hasattr(field.rel.to._default_manager, 'get_by_natural_key'): 217 keys = node.getElementsByTagName('natural') 218 if keys: 219 # If there are 'natural' subelements, it must be a natural key 220 field_value = [getInnerText(k).strip() for k in keys] 221 obj = field.rel.to._default_manager.get_by_natural_key(*field_value) 222 obj_pk = getattr(obj, field.rel.field_name) 223 else: 224 # Otherwise, treat like a normal PK 225 field_value = getInnerText(node).strip() 226 obj_pk = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value) 227 return obj_pk 228 else: 229 field_value = getInnerText(node).strip() 230 return field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value) 192 231 193 232 def _handle_m2m_field_node(self, node, field): 194 233 """ 195 234 Handle a <field> node for a ManyToManyField. 196 235 """ 197 return [field.rel.to._meta.pk.to_python( 198 c.getAttribute("pk")) 199 for c in node.getElementsByTagName("object")] 236 if hasattr(field.rel.to._default_manager, 'get_by_natural_key'): 237 def m2m_convert(n): 238 keys = n.getElementsByTagName('natural') 239 if keys: 240 # If there are 'natural' subelements, it must be a natural key 241 field_value = [getInnerText(k).strip() for k in keys] 242 obj_pk = field.rel.to._default_manager.get_by_natural_key(*field_value).pk 243 else: 244 # Otherwise, treat like a normal PK value. 245 obj_pk = field.rel.to._meta.pk.to_python(n.getAttribute('pk')) 246 return obj_pk 247 else: 248 m2m_convert = lambda n: field.rel.to._meta.pk.to_python(n.getAttribute('pk')) 249 return [m2m_convert(c) for c in node.getElementsByTagName("object")] 200 250 201 251 def _get_model_from_node(self, node, attr): 202 252 """ -
docs/ref/django-admin.txt
diff -r e0fb67da6521 docs/ref/django-admin.txt
a b 234 234 rather than the entire application. You can also mix application names and 235 235 model names. 236 236 237 .. django-admin-option:: --natural 238 239 .. versionadded:: 1.2 240 241 Use :ref:`natural keys <topics-serialization-natural-keys>` to represent 242 any foreign key and many-to-many relationship with a model that provides 243 a natural key definition. 244 237 245 flush 238 246 ----- 239 247 -
docs/releases/1.2.txt
diff -r e0fb67da6521 docs/releases/1.2.txt
a b 130 130 :meth:`~django.core.mail.get_connection()` call:: 131 131 132 132 connection = get_connection('django.core.mail.backends.smtp', hostname='localhost', port=1234) 133 133 134 134 User Messages API 135 135 ----------------- 136 136 137 The API for storing messages in the user ``Message`` model (via 137 The API for storing messages in the user ``Message`` model (via 138 138 ``user.message_set.create``) is now deprecated and will be removed in Django 139 139 1.4 according to the standard :ref:`release process <internals-release-process>`. 140 140 … … 147 147 from django.contrib import messages 148 148 messages.add_message(request, messages.INFO, 'a message') 149 149 150 Additionally, if you make use of the method, you need to replace the 150 Additionally, if you make use of the method, you need to replace the 151 151 following:: 152 152 153 153 for message in user.get_and_delete_messages(): 154 154 ... 155 155 156 156 with:: 157 157 158 158 from django.contrib import messages 159 159 for message in messages.get_messages(request): 160 160 ... 161 162 For more information, see the full 163 :ref:`messages documentation <ref-contrib-messages>`. You should begin to 161 162 For more information, see the full 163 :ref:`messages documentation <ref-contrib-messages>`. You should begin to 164 164 update your code to use the new API immediately. 165 165 166 166 What's new in Django 1.2 … … 239 239 class="highlight" 240 240 {% endif %} 241 241 >{{ message }}</div> 242 243 Natural keys in fixtures 244 ------------------------ 245 246 Fixtures can refer to remote objects using :ref:`natural keys`. This 247 lookup scheme is an alternative to the normal primary-key based object 248 references in a fixture, improving readability, and resolving problems 249 referring to objects whose primary key value may not be predictable or 250 known. 251 -
docs/topics/serialization.txt
diff -r e0fb67da6521 docs/topics/serialization.txt
a b 154 154 .. _PyYAML: http://www.pyyaml.org/ 155 155 156 156 Notes for specific serialization formats 157 ---------------------------------------- 157 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 158 158 159 159 json 160 ~~~~ 160 ^^^^ 161 161 162 162 If you're using UTF-8 (or any other non-ASCII encoding) data with the JSON 163 163 serializer, you must pass ``ensure_ascii=False`` as a parameter to the … … 191 191 192 192 .. _special encoder: http://svn.red-bean.com/bob/simplejson/tags/simplejson-1.7/docs/index.html 193 193 194 .. _topics-serialization-natural-keys: 195 196 Natural keys 197 ------------ 198 199 The default serialization strategy for foreign keys and many-to-many 200 relations is to serialize the value of the primary key(s) of the 201 objects in the relation. This strategy works well for most types of 202 object, but it can cause difficulty in some circumstances. 203 204 Consider the case of a list of objects that have foreign key on 205 :class:`ContentType`. If you're going to serialize an object that 206 refers to a content type, you need to have a way to refer to that 207 content type. Content Types are automatically created by Django as 208 part of the database synchronization process, so you don't need to 209 include content types in a fixture or other serialized data. As a 210 result, the primary key of any given content type isn't easy to 211 predict - it will depend on how and when :djadmin:`syncdb` was 212 executed to create the content types. 213 214 There is also the matter of convenience. An integer id isn't always 215 the most convenient way to refer to an object; sometimes, a 216 more natural reference would be helpful. 217 218 Deserialization of natural keys 219 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 220 221 It is for these reasons that Django provides `natural keys`. A natural 222 key is a tuple of values that can be used to uniquely identify an 223 object instance without using the primary key value. 224 225 Consider the following two models:: 226 227 from django.db import models 228 229 class Person(models.Model): 230 first_name = models.CharField(max_length=100) 231 last_name = models.CharField(max_length=100) 232 233 birthdate = models.DateField() 234 235 class Book(models.Model): 236 name = models.CharField(max_length=100) 237 author = models.ForeignKey(Person) 238 239 Ordinarily, serialized data for ``Book`` would use an integer to refer to 240 the author. For example, in JSON, a Book might be serialized as:: 241 242 ... 243 { 244 "pk": 1, 245 "model": "store.book", 246 "fields": { 247 "name": "Mostly Harmless", 248 "author": 42 249 } 250 } 251 ... 252 253 This isn't a particularly natural way to refer to an author. It 254 requires that you know the primary key value for the author; it also 255 requires that this primary key value is stable and predictable. 256 257 However, if we add natural key handling to Person, the fixture becomes 258 much more humane. To add natural key handling, you define a default 259 Manager for Person with a ``get_by_natural_key()`` method. In the case 260 of a Person, a good natural key might be the pair of first and last 261 name:: 262 263 from django.db import models 264 265 class PersonManager(models.Manager): 266 def get_by_natural_key(self, first_name, last_name): 267 return self.filter(first_name=first_name, last_name=last_name) 268 269 class Person(models.Model): 270 objects = PersonManager() 271 272 first_name = models.CharField(max_length=100) 273 last_name = models.CharField(max_length=100) 274 275 birthdate = models.DateField() 276 277 Now books can use that natural key to refer to ``Person`` objects:: 278 279 ... 280 { 281 "pk": 1, 282 "model": "store.book", 283 "fields": { 284 "name": "Mostly Harmless", 285 "author": ["Douglas", "Adams"] 286 } 287 } 288 ... 289 290 When you try to load this serialized data, Django will use the 291 ``get_by_natural_key()`` method to resolve ``["Douglas", "Adams"]`` 292 into the primary key of an actual ``Person`` object. 293 294 Serialization of natural keys 295 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 296 297 So how do you get Django to emit a natural key when serializing an object? 298 Firstly, you need to add another method -- this time to the model itself:: 299 300 class Person(models.Model): 301 objects = PersonManager() 302 303 first_name = models.CharField(max_length=100) 304 last_name = models.CharField(max_length=100) 305 306 birthdate = models.DateField() 307 308 def natural_key(self): 309 return (self.first_name, self.last_name) 310 311 Then, when you call ``serializers.serialize()``, you provide a 312 ``use_natural_keys=True`` argument:: 313 314 >>> serializers.serialize([book1, book2], format='json', indent=2, use_natural_keys=True) 315 316 When ``use_natural_keys=True`` is specified, Django will use the 317 ``natural_key()`` method to serialize any reference to objects of the 318 type that defines the method. 319 320 If you are using :djadmin:`dumpdata` to generate serialized data, you 321 use the `--natural` command line flag to generate natural keys. 322 323 .. note:: 324 325 You don't need to define both ``natural_key()`` and 326 ``get_by_natural_key()``. If you don't want Django to output 327 natural keys during serialization, but you want to retain the 328 ability to load natural keys, then you can opt to not implement 329 the ``natural_key()`` method. 330 331 Conversely, if (for some strange reason) you want Django to output 332 natural keys during serialization, but *not* be able to load those 333 key values, just don't define the ``get_by_natural_key()`` method. 334 335 Dependencies during serialization 336 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 337 338 Since natural keys rely on database lookups to resolve references, it 339 is important that data exists before it is referenced. You can't make 340 a `forward reference` with natural keys - the data you are referencing 341 must exist before you include a natural key reference to that data. 342 343 To accommodate this limitation, calls to :djadmin:`dumpdata` will 344 serialize any model with a ``natural_key()`` method before it serializes 345 normal key objects. 346 347 However, this may not always be enough. If your natural key refers to 348 another object (by using a foreign key or natural key to another object 349 as part of a natural key), then you need to be able to ensure that 350 the objects on which a natural key depends occur in the serialized data 351 before the natural key requires them. 352 353 To control this ordering, you can define dependencies on your 354 ``natural_key()`` methods. You do this by setting a ``dependencies`` 355 attribute on the ``natural_key()`` method itself. 356 357 For example, consider the ``Permission`` model in ``contrib.auth``. 358 The following is a simplified version of the ``Permission`` model:: 359 360 class Permission(models.Model): 361 name = models.CharField(max_length=50) 362 content_type = models.ForeignKey(ContentType) 363 codename = models.CharField(max_length=100) 364 # ... 365 def natural_key(self): 366 return (self.codename,) + self.content_type.natural_key() 367 368 The natural key for a ``Permission`` is a combination of the codename for the 369 ``Permission``, and the ``ContentType`` to which the ``Permission`` applies. This means 370 that ``ContentType`` must be serialized before ``Permission``. To define this 371 dependency, we add one extra line:: 372 373 class Permission(models.Model): 374 # ... 375 def natural_key(self): 376 return (self.codename,) + self.content_type.natural_key() 377 natural_key.dependencies = ['contenttypes.contenttype'] 378 379 This definition ensures that ``ContentType`` models are serialized before 380 ``Permission`` models. In turn, any object referencing ``Permission`` will 381 be serialized after both ``ContentType`` and ``Permission``. -
new file tests/modeltests/fixtures/fixtures/fixture6.json
diff -r e0fb67da6521 tests/modeltests/fixtures/fixtures/fixture6.json
- + 1 [ 2 { 3 "pk": "1", 4 "model": "fixtures.tag", 5 "fields": { 6 "name": "copyright", 7 "tagged_type": ["fixtures", "article"], 8 "tagged_id": "3" 9 } 10 }, 11 { 12 "pk": "2", 13 "model": "fixtures.tag", 14 "fields": { 15 "name": "law", 16 "tagged_type": ["fixtures", "article"], 17 "tagged_id": "3" 18 } 19 }, 20 { 21 "pk": "1", 22 "model": "fixtures.person", 23 "fields": { 24 "name": "Django Reinhardt" 25 } 26 }, 27 { 28 "pk": "2", 29 "model": "fixtures.person", 30 "fields": { 31 "name": "Stephane Grappelli" 32 } 33 }, 34 { 35 "pk": "3", 36 "model": "fixtures.person", 37 "fields": { 38 "name": "Prince" 39 } 40 } 41 ] -
new file tests/modeltests/fixtures/fixtures/fixture7.xml
diff -r e0fb67da6521 tests/modeltests/fixtures/fixtures/fixture7.xml
- + 1 <?xml version="1.0" encoding="utf-8"?> 2 <django-objects version="1.0"> 3 <object pk="2" model="fixtures.tag"> 4 <field type="CharField" name="name">legal</field> 5 <field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"> 6 <natural>fixtures</natural> 7 <natural>article</natural> 8 </field> 9 <field type="PositiveIntegerField" name="tagged_id">3</field> 10 </object> 11 <object pk="3" model="fixtures.tag"> 12 <field type="CharField" name="name">django</field> 13 <field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"> 14 <natural>fixtures</natural> 15 <natural>article</natural> 16 </field> 17 <field type="PositiveIntegerField" name="tagged_id">4</field> 18 </object> 19 <object pk="4" model="fixtures.tag"> 20 <field type="CharField" name="name">world domination</field> 21 <field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"> 22 <natural>fixtures</natural> 23 <natural>article</natural> 24 </field> 25 <field type="PositiveIntegerField" name="tagged_id">4</field> 26 </object> 27 </django-objects> -
new file tests/modeltests/fixtures/fixtures/fixture8.json
diff -r e0fb67da6521 tests/modeltests/fixtures/fixtures/fixture8.json
- + 1 [ 2 { 3 "pk": "1", 4 "model": "fixtures.visa", 5 "fields": { 6 "person": ["Django Reinhardt"], 7 "permissions": [ 8 ["add_user", "auth", "user"], 9 ["change_user", "auth", "user"], 10 ["delete_user", "auth", "user"] 11 ] 12 } 13 }, 14 { 15 "pk": "2", 16 "model": "fixtures.visa", 17 "fields": { 18 "person": ["Stephane Grappelli"], 19 "permissions": [ 20 ["add_user", "auth", "user"] 21 ] 22 } 23 }, 24 { 25 "pk": "3", 26 "model": "fixtures.visa", 27 "fields": { 28 "person": ["Prince"], 29 "permissions": [] 30 } 31 } 32 ] -
new file tests/modeltests/fixtures/fixtures/fixture9.xml
diff -r e0fb67da6521 tests/modeltests/fixtures/fixtures/fixture9.xml
- + 1 <?xml version="1.0" encoding="utf-8"?> 2 <django-objects version="1.0"> 3 <object pk="2" model="fixtures.visa"> 4 <field type="CharField" name="person"> 5 <natural>Stephane Grappelli</natural> 6 </field> 7 <field to="auth.permission" name="permissions" rel="ManyToManyRel"> 8 <object> 9 <natural>add_user</natural> 10 <natural>auth</natural> 11 <natural>user</natural> 12 </object> 13 <object> 14 <natural>delete_user</natural> 15 <natural>auth</natural> 16 <natural>user</natural> 17 </object> 18 </field> 19 </object> 20 <object pk="3" model="fixtures.person"> 21 <field type="CharField" name="name"> 22 <natural>Artist formerly known as "Prince"</natural> 23 </field> 24 </object> 25 <object pk="3" model="fixtures.visa"> 26 <field type="CharField" name="person"> 27 <natural>Artist formerly known as "Prince"</natural> 28 </field> 29 <field to="auth.permission" name="permissions" rel="ManyToManyRel"> 30 <object> 31 <natural>change_user</natural> 32 <natural>auth</natural> 33 <natural>user</natural> 34 </object> 35 </field> 36 </object> 37 <object pk="1" model="fixtures.book"> 38 <field type="CharField" name="name">Music for all ages</field> 39 <field to="fixtures.person" name="authors" rel="ManyToManyRel"> 40 <object> 41 <natural>Django Reinhardt</natural> 42 </object> 43 <object> 44 <natural>Artist formerly known as "Prince"</natural> 45 </object> 46 </field> 47 </object> 48 </django-objects> -
tests/modeltests/fixtures/models.py
diff -r e0fb67da6521 tests/modeltests/fixtures/models.py
a b 8 8 ``FIXTURE_DIRS`` setting. 9 9 """ 10 10 11 from django.contrib.auth.models import Permission 12 from django.contrib.contenttypes import generic 13 from django.contrib.contenttypes.models import ContentType 11 14 from django.db import models 12 15 from django.conf import settings 13 16 … … 31 34 class Meta: 32 35 ordering = ('-pub_date', 'headline') 33 36 37 class Blog(models.Model): 38 name = models.CharField(max_length=100) 39 featured = models.ForeignKey(Article, related_name='fixtures_featured_set') 40 articles = models.ManyToManyField(Article, blank=True, 41 related_name='fixtures_articles_set') 42 43 def __unicode__(self): 44 return self.name 45 46 47 class Tag(models.Model): 48 name = models.CharField(max_length=100) 49 tagged_type = models.ForeignKey(ContentType, related_name="fixtures_tag_set") 50 tagged_id = models.PositiveIntegerField(default=0) 51 tagged = generic.GenericForeignKey(ct_field='tagged_type', 52 fk_field='tagged_id') 53 54 def __unicode__(self): 55 return '<%s: %s> tagged "%s"' % (self.tagged.__class__.__name__, 56 self.tagged, self.name) 57 58 class PersonManager(models.Manager): 59 def get_by_natural_key(self, name): 60 return self.get(name=name) 61 62 class Person(models.Model): 63 objects = PersonManager() 64 name = models.CharField(max_length=100) 65 def __unicode__(self): 66 return self.name 67 68 class Meta: 69 ordering = ('name',) 70 71 def natural_key(self): 72 return (self.name,) 73 74 class Visa(models.Model): 75 person = models.ForeignKey(Person) 76 permissions = models.ManyToManyField(Permission, blank=True) 77 78 def __unicode__(self): 79 return '%s %s' % (self.person.name, 80 ', '.join(p.name for p in self.permissions.all())) 81 82 class Book(models.Model): 83 name = models.CharField(max_length=100) 84 authors = models.ManyToManyField(Person) 85 86 def __unicode__(self): 87 return '%s by %s' % (self.name, 88 ' and '.join(a.name for a in self.authors.all())) 89 90 class Meta: 91 ordering = ('name',) 92 34 93 __test__ = {'API_TESTS': """ 35 94 >>> from django.core import management 36 95 >>> from django.db.models import get_app … … 90 149 >>> Article.objects.all() 91 150 [<Article: XML identified as leading cause of cancer>, <Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker on TV is great!>, <Article: Python program becomes self aware>] 92 151 152 # Load fixture 6, JSON file with dynamic ContentType fields. Testing ManyToOne. 153 >>> management.call_command('loaddata', 'fixture6.json', verbosity=0) 154 >>> Tag.objects.all() 155 [<Tag: <Article: Copyright is fine the way it is> tagged "copyright">, <Tag: <Article: Copyright is fine the way it is> tagged "law">] 156 157 # Load fixture 7, XML file with dynamic ContentType fields. Testing ManyToOne. 158 >>> management.call_command('loaddata', 'fixture7.xml', verbosity=0) 159 >>> Tag.objects.all() 160 [<Tag: <Article: Copyright is fine the way it is> tagged "copyright">, <Tag: <Article: Copyright is fine the way it is> tagged "legal">, <Tag: <Article: Django conquers world!> tagged "django">, <Tag: <Article: Django conquers world!> tagged "world domination">] 161 162 # Load fixture 8, JSON file with dynamic Permission fields. Testing ManyToMany. 163 >>> management.call_command('loaddata', 'fixture8.json', verbosity=0) 164 >>> Visa.objects.all() 165 [<Visa: Django Reinhardt Can add user, Can change user, Can delete user>, <Visa: Stephane Grappelli Can add user>, <Visa: Prince >] 166 167 # Load fixture 9, XML file with dynamic Permission fields. Testing ManyToMany. 168 >>> management.call_command('loaddata', 'fixture9.xml', verbosity=0) 169 >>> Visa.objects.all() 170 [<Visa: Django Reinhardt Can add user, Can change user, Can delete user>, <Visa: Stephane Grappelli Can add user, Can delete user>, <Visa: Artist formerly known as "Prince" Can change user>] 171 172 >>> Book.objects.all() 173 [<Book: Music for all ages by Artist formerly known as "Prince" and Django Reinhardt>] 174 93 175 # Load a fixture that doesn't exist 94 176 >>> management.call_command('loaddata', 'unknown.json', verbosity=0) 95 177 96 178 # object list is unaffected 97 179 >>> Article.objects.all() 98 180 [<Article: XML identified as leading cause of cancer>, <Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker on TV is great!>, <Article: Python program becomes self aware>] 181 182 # By default, you get raw keys on dumpdata 183 >>> management.call_command('dumpdata', 'fixtures.book', format='json') 184 [{"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [3, 1]}}] 185 186 # But you can get natural keys if you ask for them and they are available 187 >>> management.call_command('dumpdata', 'fixtures.book', format='json', use_natural_keys=True) 188 [{"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [["Artist formerly known as \\"Prince\\""], ["Django Reinhardt"]]}}] 189 190 # Dump the current contents of the database as a JSON fixture 191 >>> management.call_command('dumpdata', 'fixtures', format='json', use_natural_keys=True) 192 [{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 5, "model": "fixtures.article", "fields": {"headline": "XML identified as leading cause of cancer", "pub_date": "2006-06-16 16:00:00"}}, {"pk": 4, "model": "fixtures.article", "fields": {"headline": "Django conquers world!", "pub_date": "2006-06-16 15:00:00"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Copyright is fine the way it is", "pub_date": "2006-06-16 14:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker on TV is great!", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "copyright", "tagged_id": 3}}, {"pk": 2, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "legal", "tagged_id": 3}}, {"pk": 3, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "django", "tagged_id": 4}}, {"pk": 4, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "world domination", "tagged_id": 4}}, {"pk": 3, "model": "fixtures.person", "fields": {"name": "Artist formerly known as \\"Prince\\""}}, {"pk": 1, "model": "fixtures.person", "fields": {"name": "Django Reinhardt"}}, {"pk": 2, "model": "fixtures.person", "fields": {"name": "Stephane Grappelli"}}, {"pk": 1, "model": "fixtures.visa", "fields": {"person": ["Django Reinhardt"], "permissions": [["add_user", "auth", "user"], ["change_user", "auth", "user"], ["delete_user", "auth", "user"]]}}, {"pk": 2, "model": "fixtures.visa", "fields": {"person": ["Stephane Grappelli"], "permissions": [["add_user", "auth", "user"], ["delete_user", "auth", "user"]]}}, {"pk": 3, "model": "fixtures.visa", "fields": {"person": ["Artist formerly known as \\"Prince\\""], "permissions": [["change_user", "auth", "user"]]}}, {"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [["Artist formerly known as \\"Prince\\""], ["Django Reinhardt"]]}}] 193 194 # Dump the current contents of the database as an XML fixture 195 >>> management.call_command('dumpdata', 'fixtures', format='xml', use_natural_keys=True) 196 <?xml version="1.0" encoding="utf-8"?> 197 <django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</field></object><object pk="5" model="fixtures.article"><field type="CharField" name="headline">XML identified as leading cause of cancer</field><field type="DateTimeField" name="pub_date">2006-06-16 16:00:00</field></object><object pk="4" model="fixtures.article"><field type="CharField" name="headline">Django conquers world!</field><field type="DateTimeField" name="pub_date">2006-06-16 15:00:00</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Copyright is fine the way it is</field><field type="DateTimeField" name="pub_date">2006-06-16 14:00:00</field></object><object pk="2" model="fixtures.article"><field type="CharField" name="headline">Poker on TV is great!</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.article"><field type="CharField" name="headline">Python program becomes self aware</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">legal</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="3" model="fixtures.tag"><field type="CharField" name="name">django</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="4" model="fixtures.tag"><field type="CharField" name="name">world domination</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Artist formerly known as "Prince"</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object><object pk="1" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Django Reinhardt</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="2" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Stephane Grappelli</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="3" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Artist formerly known as "Prince"</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="1" model="fixtures.book"><field type="CharField" name="name">Music for all ages</field><field to="fixtures.person" name="authors" rel="ManyToManyRel"><object><natural>Artist formerly known as "Prince"</natural></object><object><natural>Django Reinhardt</natural></object></field></object></django-objects> 198 99 199 """} 100 200 101 201 # Database flushing does not work on MySQL with the default storage engine … … 159 259 >>> management.call_command('loaddata', 'fixture5', verbosity=0) # doctest: +ELLIPSIS 160 260 Multiple fixtures named 'fixture5' in '...fixtures'. Aborting. 161 261 262 >>> management.call_command('flush', verbosity=0, interactive=False) 263 264 # Load back in fixture 1, we need the articles from it 265 >>> management.call_command('loaddata', 'fixture1', verbosity=0) 266 267 # Try to load fixture 6 using format discovery 268 >>> management.call_command('loaddata', 'fixture6', verbosity=0) 269 >>> Tag.objects.all() 270 [<Tag: <Article: Time to reform copyright> tagged "copyright">, <Tag: <Article: Time to reform copyright> tagged "law">] 271 272 # Dump the current contents of the database as a JSON fixture 273 >>> management.call_command('dumpdata', 'fixtures', format='json', use_natural_keys=True) 274 [{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "copyright", "tagged_id": 3}}, {"pk": 2, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "law", "tagged_id": 3}}, {"pk": 1, "model": "fixtures.person", "fields": {"name": "Django Reinhardt"}}, {"pk": 3, "model": "fixtures.person", "fields": {"name": "Prince"}}, {"pk": 2, "model": "fixtures.person", "fields": {"name": "Stephane Grappelli"}}] 275 276 # Dump the current contents of the database as an XML fixture 277 >>> management.call_command('dumpdata', 'fixtures', format='xml', use_natural_keys=True) 278 <?xml version="1.0" encoding="utf-8"?> 279 <django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Time to reform copyright</field><field type="DateTimeField" name="pub_date">2006-06-16 13:00:00</field></object><object pk="2" model="fixtures.article"><field type="CharField" name="headline">Poker has no place on ESPN</field><field type="DateTimeField" name="pub_date">2006-06-16 12:00:00</field></object><object pk="1" model="fixtures.article"><field type="CharField" name="headline">Python program becomes self aware</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">law</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Prince</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object></django-objects> 280 162 281 """ 163 282 164 283 from django.test import TestCase -
new file tests/regressiontests/fixtures_regress/fixtures/forward_ref_lookup.json
diff -r e0fb67da6521 tests/regressiontests/fixtures_regress/fixtures/forward_ref_lookup.json
- + 1 [ 2 { 3 "pk": "4", 4 "model": "fixtures_regress.person", 5 "fields": { 6 "name": "Neal Stephenson" 7 } 8 }, 9 { 10 "pk": "2", 11 "model": "fixtures_regress.store", 12 "fields": { 13 "name": "Amazon" 14 } 15 }, 16 { 17 "pk": "3", 18 "model": "fixtures_regress.store", 19 "fields": { 20 "name": "Borders" 21 } 22 }, 23 { 24 "pk": 1, 25 "model": "fixtures_regress.book", 26 "fields": { 27 "name": "Cryptonomicon", 28 "author": ["Neal Stephenson"], 29 "stores": [["Amazon"], ["Borders"]] 30 } 31 } 32 ] 33 No newline at end of file -
new file tests/regressiontests/fixtures_regress/fixtures/non_natural_1.json
diff -r e0fb67da6521 tests/regressiontests/fixtures_regress/fixtures/non_natural_1.json
- + 1 [ 2 { 3 "pk": 12, 4 "model": "fixtures_regress.person", 5 "fields": { 6 "name": "Greg Egan" 7 } 8 }, 9 { 10 "pk": 11, 11 "model": "fixtures_regress.store", 12 "fields": { 13 "name": "Angus and Robertson" 14 } 15 }, 16 { 17 "pk": 10, 18 "model": "fixtures_regress.book", 19 "fields": { 20 "name": "Permutation City", 21 "author": 12, 22 "stores": [11] 23 } 24 } 25 ] 26 No newline at end of file -
new file tests/regressiontests/fixtures_regress/fixtures/non_natural_2.xml
diff -r e0fb67da6521 tests/regressiontests/fixtures_regress/fixtures/non_natural_2.xml
- + 1 <?xml version="1.0" encoding="utf-8"?> 2 <django-objects version="1.0"> 3 <object pk="22" model="fixtures_regress.person"> 4 <field type="CharField" name="name">Orson Scott Card</field> 5 </object> 6 <object pk="21" model="fixtures_regress.store"> 7 <field type="CharField" name="name">Collins Bookstore</field> 8 </object> 9 <object pk="20" model="fixtures_regress.book"> 10 <field type="CharField" name="name">Ender's Game</field> 11 <field to="fixtures_regress.person" name="author" rel="ManyToOneRel">22</field> 12 <field to="fixtures_regress.store" name="stores" rel="ManyToManyRel"> 13 <object pk="21"/> 14 </field> 15 </object> 16 </django-objects> 17 No newline at end of file -
tests/regressiontests/fixtures_regress/models.py
diff -r e0fb67da6521 tests/regressiontests/fixtures_regress/models.py
a b 13 13 specimens = models.Manager() 14 14 15 15 def __unicode__(self): 16 return self. common_name16 return self.name 17 17 18 18 def animal_pre_save_check(signal, sender, instance, **kwargs): 19 19 "A signal that is used to check the type of data loaded from fixtures" … … 69 69 class Widget(models.Model): 70 70 name = models.CharField(max_length=255) 71 71 72 class Meta: 73 ordering = ('name',) 74 75 def __unicode__(self): 76 return self.name 77 72 78 class WidgetProxy(Widget): 73 79 class Meta: 74 80 proxy = True 75 81 82 # Check for forward references in FKs and M2Ms with natural keys 83 84 class TestManager(models.Manager): 85 def get_by_natural_key(self, key): 86 return self.get(name=key) 87 88 class Store(models.Model): 89 objects = TestManager() 90 name = models.CharField(max_length=255) 91 92 class Meta: 93 ordering = ('name',) 94 95 def __unicode__(self): 96 return self.name 97 98 def natural_key(self): 99 return (self.name,) 100 101 class Person(models.Model): 102 objects = TestManager() 103 name = models.CharField(max_length=255) 104 105 class Meta: 106 ordering = ('name',) 107 108 def __unicode__(self): 109 return self.name 110 111 # Person doesn't actually have a dependency on store, but we need to define 112 # one to test the behaviour of the dependency resolution algorithm. 113 def natural_key(self): 114 return (self.name,) 115 natural_key.dependencies = ['fixtures_regress.store'] 116 117 class Book(models.Model): 118 name = models.CharField(max_length=255) 119 author = models.ForeignKey(Person) 120 stores = models.ManyToManyField(Store) 121 122 class Meta: 123 ordering = ('name',) 124 125 def __unicode__(self): 126 return u'%s by %s (available at %s)' % ( 127 self.name, 128 self.author.name, 129 ', '.join(s.name for s in self.stores.all()) 130 ) 131 76 132 __test__ = {'API_TESTS':""" 77 133 >>> from django.core import management 78 134 … … 192 248 >>> management.call_command('dumpdata', 'fixtures_regress', format='json') 193 249 [{"pk": 1, "model": "fixtures_regress.widget", "fields": {"name": "grommet"}}] 194 250 251 ############################################### 252 # Check that natural key requirements are taken into account 253 # when serializing models 254 >>> management.call_command('loaddata', 'forward_ref_lookup.json', verbosity=0) 255 256 >>> management.call_command('dumpdata', 'fixtures_regress.book', 'fixtures_regress.person', 'fixtures_regress.store', verbosity=0, use_natural_keys=True) 257 [{"pk": 2, "model": "fixtures_regress.store", "fields": {"name": "Amazon"}}, {"pk": 3, "model": "fixtures_regress.store", "fields": {"name": "Borders"}}, {"pk": 4, "model": "fixtures_regress.person", "fields": {"name": "Neal Stephenson"}}, {"pk": 1, "model": "fixtures_regress.book", "fields": {"stores": [["Amazon"], ["Borders"]], "name": "Cryptonomicon", "author": ["Neal Stephenson"]}}] 258 259 # Now lets check the dependency sorting explicitly 260 261 # First Some models with pathological circular dependencies 262 >>> class Circle1(models.Model): 263 ... name = models.CharField(max_length=255) 264 ... def natural_key(self): 265 ... return self.name 266 ... natural_key.dependencies = ['fixtures_regress.circle2'] 267 268 >>> class Circle2(models.Model): 269 ... name = models.CharField(max_length=255) 270 ... def natural_key(self): 271 ... return self.name 272 ... natural_key.dependencies = ['fixtures_regress.circle1'] 273 274 >>> class Circle3(models.Model): 275 ... name = models.CharField(max_length=255) 276 ... def natural_key(self): 277 ... return self.name 278 ... natural_key.dependencies = ['fixtures_regress.circle3'] 279 280 >>> class Circle4(models.Model): 281 ... name = models.CharField(max_length=255) 282 ... def natural_key(self): 283 ... return self.name 284 ... natural_key.dependencies = ['fixtures_regress.circle5'] 285 286 >>> class Circle5(models.Model): 287 ... name = models.CharField(max_length=255) 288 ... def natural_key(self): 289 ... return self.name 290 ... natural_key.dependencies = ['fixtures_regress.circle6'] 291 292 >>> class Circle6(models.Model): 293 ... name = models.CharField(max_length=255) 294 ... def natural_key(self): 295 ... return self.name 296 ... natural_key.dependencies = ['fixtures_regress.circle4'] 297 298 >>> class ExternalDependency(models.Model): 299 ... name = models.CharField(max_length=255) 300 ... def natural_key(self): 301 ... return self.name 302 ... natural_key.dependencies = ['fixtures_regress.book'] 303 304 # It doesn't matter what order you mention the models 305 # Store *must* be serialized before then Person, and both 306 # must be serialized before Book. 307 >>> from django.core.management.commands.dumpdata import sort_dependencies 308 >>> sort_dependencies([('fixtures_regress', [Book, Person, Store])]) 309 [<class 'regressiontests.fixtures_regress.models.Store'>, <class 'regressiontests.fixtures_regress.models.Person'>, <class 'regressiontests.fixtures_regress.models.Book'>] 310 311 >>> sort_dependencies([('fixtures_regress', [Book, Store, Person])]) 312 [<class 'regressiontests.fixtures_regress.models.Store'>, <class 'regressiontests.fixtures_regress.models.Person'>, <class 'regressiontests.fixtures_regress.models.Book'>] 313 314 >>> sort_dependencies([('fixtures_regress', [Store, Book, Person])]) 315 [<class 'regressiontests.fixtures_regress.models.Store'>, <class 'regressiontests.fixtures_regress.models.Person'>, <class 'regressiontests.fixtures_regress.models.Book'>] 316 317 >>> sort_dependencies([('fixtures_regress', [Store, Person, Book])]) 318 [<class 'regressiontests.fixtures_regress.models.Store'>, <class 'regressiontests.fixtures_regress.models.Person'>, <class 'regressiontests.fixtures_regress.models.Book'>] 319 320 >>> sort_dependencies([('fixtures_regress', [Person, Book, Store])]) 321 [<class 'regressiontests.fixtures_regress.models.Store'>, <class 'regressiontests.fixtures_regress.models.Person'>, <class 'regressiontests.fixtures_regress.models.Book'>] 322 323 >>> sort_dependencies([('fixtures_regress', [Person, Store, Book])]) 324 [<class 'regressiontests.fixtures_regress.models.Store'>, <class 'regressiontests.fixtures_regress.models.Person'>, <class 'regressiontests.fixtures_regress.models.Book'>] 325 326 # A dangling dependency - assume the user knows what they are doing. 327 >>> sort_dependencies([('fixtures_regress', [Person, Circle1, Store, Book])]) 328 [<class 'regressiontests.fixtures_regress.models.Circle1'>, <class 'regressiontests.fixtures_regress.models.Store'>, <class 'regressiontests.fixtures_regress.models.Person'>, <class 'regressiontests.fixtures_regress.models.Book'>] 329 330 # A tight circular dependency 331 >>> sort_dependencies([('fixtures_regress', [Person, Circle2, Circle1, Store, Book])]) 332 Traceback (most recent call last): 333 ... 334 CommandError: Can't resolve dependencies for fixtures_regress.Circle1, fixtures_regress.Circle2 in serialized app list. 335 336 >>> sort_dependencies([('fixtures_regress', [Circle1, Book, Circle2])]) 337 Traceback (most recent call last): 338 ... 339 CommandError: Can't resolve dependencies for fixtures_regress.Circle1, fixtures_regress.Circle2 in serialized app list. 340 341 # A self referential dependency 342 >>> sort_dependencies([('fixtures_regress', [Book, Circle3])]) 343 Traceback (most recent call last): 344 ... 345 CommandError: Can't resolve dependencies for fixtures_regress.Circle3 in serialized app list. 346 347 # A long circular dependency 348 >>> sort_dependencies([('fixtures_regress', [Person, Circle2, Circle1, Circle3, Store, Book])]) 349 Traceback (most recent call last): 350 ... 351 CommandError: Can't resolve dependencies for fixtures_regress.Circle1, fixtures_regress.Circle2, fixtures_regress.Circle3 in serialized app list. 352 353 # A dependency on a normal, non-natural-key model 354 >>> sort_dependencies([('fixtures_regress', [Person, ExternalDependency, Book])]) 355 [<class 'regressiontests.fixtures_regress.models.Person'>, <class 'regressiontests.fixtures_regress.models.Book'>, <class 'regressiontests.fixtures_regress.models.ExternalDependency'>] 356 357 ############################################### 358 # Check that normal primary keys still work 359 # on a model with natural key capabilities 360 361 >>> management.call_command('loaddata', 'non_natural_1.json', verbosity=0) 362 >>> management.call_command('loaddata', 'non_natural_2.xml', verbosity=0) 363 364 >>> Book.objects.all() 365 [<Book: Cryptonomicon by Neal Stephenson (available at Amazon, Borders)>, <Book: Ender's Game by Orson Scott Card (available at Collins Bookstore)>, <Book: Permutation City by Greg Egan (available at Angus and Robertson)>] 366 195 367 """} 368