1 | # vim: set ts=8 sw=4 sts=4 et ai:
|
---|
2 | """
|
---|
3 | Add natural keys to contrib.auth.User and Group models.
|
---|
4 | https://code.djangoproject.com/ticket/13914
|
---|
5 |
|
---|
6 | In this case, only to the Group model we won't create duplicate groups
|
---|
7 | without having to hardcode the Group PKs.
|
---|
8 |
|
---|
9 | In project.fixtures.initial_data.xml we define which groups have which
|
---|
10 | permissions. But the extension modules will require additional groups
|
---|
11 | to be created. They cannot safely choose a PK without stomping on
|
---|
12 | different extension PKs.
|
---|
13 |
|
---|
14 | The fix: remove the PK from the fixtures.
|
---|
15 |
|
---|
16 | Ticket #13914 has been closed, the fix has been committed as
|
---|
17 | 954e3b4ad37b572cdc46baf3e35c712f4049efea in branch 1.7b4.
|
---|
18 | """
|
---|
19 | from django import VERSION
|
---|
20 |
|
---|
21 | if VERSION < (1, 7):
|
---|
22 | from django.contrib.auth import models as auth_models
|
---|
23 | from django.db import models as db_models
|
---|
24 |
|
---|
25 | class GroupManagerWithNaturalKey(db_models.Manager):
|
---|
26 | def get_by_natural_key(self, name):
|
---|
27 | return self.get(name=name)
|
---|
28 | #auth_models.Group.objects = GroupManagerWithNaturalKey()
|
---|
29 | auth_models.Group._default_manager = GroupManagerWithNaturalKey()
|
---|
30 | auth_models.Group._default_manager.model = auth_models.Group
|
---|
31 |
|
---|
32 | def group_natural_key(self):
|
---|
33 | return (self.name,)
|
---|
34 | auth_models.Group.natural_key = group_natural_key
|
---|
35 |
|
---|
36 | # But wait, there is more. We also need to patch the xml
|
---|
37 | # Deserializer to accept the natural key as PK.
|
---|
38 | from django.core.serializers import base, xml_serializer
|
---|
39 | from django.db import models
|
---|
40 |
|
---|
41 | # This function is stolen from Django 1.7.
|
---|
42 | def build_instance(Model, data, db):
|
---|
43 | """
|
---|
44 | Build a model instance.
|
---|
45 |
|
---|
46 | If the model instance doesn't have a primary key and the model supports
|
---|
47 | natural keys, try to retrieve it from the database.
|
---|
48 | """
|
---|
49 | obj = Model(**data)
|
---|
50 | if (obj.pk is None and hasattr(Model, 'natural_key') and
|
---|
51 | hasattr(Model._default_manager, 'get_by_natural_key')):
|
---|
52 | natural_key = obj.natural_key()
|
---|
53 | try:
|
---|
54 | obj.pk = Model._default_manager.db_manager(db).get_by_natural_key(*natural_key).pk
|
---|
55 | except Model.DoesNotExist:
|
---|
56 | pass
|
---|
57 | return obj
|
---|
58 |
|
---|
59 | # This function is unchanged from
|
---|
60 | # django.core.serializers.xml_serializer.Deserializer
|
---|
61 | # apart from the build_instance code at the bottom.
|
---|
62 | def deserializer_handle_object(self, node):
|
---|
63 | """
|
---|
64 | Convert an <object> node to a DeserializedObject.
|
---|
65 | """
|
---|
66 | # Look up the model using the model loading mechanism. If this fails,
|
---|
67 | # bail.
|
---|
68 | Model = self._get_model_from_node(node, "model")
|
---|
69 |
|
---|
70 | # Start building a data dictionary from the object.
|
---|
71 | # If the node is missing the pk set it to None
|
---|
72 | if node.hasAttribute("pk"):
|
---|
73 | pk = node.getAttribute("pk")
|
---|
74 | else:
|
---|
75 | pk = None
|
---|
76 |
|
---|
77 | data = {Model._meta.pk.attname: Model._meta.pk.to_python(pk)}
|
---|
78 |
|
---|
79 | # Also start building a dict of m2m data (this is saved as
|
---|
80 | # {m2m_accessor_attribute : [list_of_related_objects]})
|
---|
81 | m2m_data = {}
|
---|
82 |
|
---|
83 | # Deseralize each field.
|
---|
84 | for field_node in node.getElementsByTagName("field"):
|
---|
85 | # If the field is missing the name attribute, bail (are you
|
---|
86 | # sensing a pattern here?)
|
---|
87 | field_name = field_node.getAttribute("name")
|
---|
88 | if not field_name:
|
---|
89 | raise base.DeserializationError("<field> node is missing the 'name' attribute")
|
---|
90 |
|
---|
91 | # Get the field from the Model. This will raise a
|
---|
92 | # FieldDoesNotExist if, well, the field doesn't exist, which will
|
---|
93 | # be propagated correctly.
|
---|
94 | field = Model._meta.get_field(field_name)
|
---|
95 |
|
---|
96 | # As is usually the case, relation fields get the special treatment.
|
---|
97 | if field.rel and isinstance(field.rel, models.ManyToManyRel):
|
---|
98 | m2m_data[field.name] = self._handle_m2m_field_node(field_node, field)
|
---|
99 | elif field.rel and isinstance(field.rel, models.ManyToOneRel):
|
---|
100 | data[field.attname] = self._handle_fk_field_node(field_node, field)
|
---|
101 | else:
|
---|
102 | if field_node.getElementsByTagName('None'):
|
---|
103 | value = None
|
---|
104 | else:
|
---|
105 | value = field.to_python(xml_serializer.getInnerText(field_node).strip())
|
---|
106 | data[field.name] = value
|
---|
107 |
|
---|
108 | # Original.
|
---|
109 | # # Return a DeserializedObject so that the m2m data has a place to live.
|
---|
110 | # return base.DeserializedObject(Model(**data), m2m_data)
|
---|
111 |
|
---|
112 | # New.
|
---|
113 | obj = build_instance(Model, data, self.db)
|
---|
114 | # Return a DeserializedObject so that the m2m data has a place to live.
|
---|
115 | return base.DeserializedObject(obj, m2m_data)
|
---|
116 |
|
---|
117 | # Overwrite the old method with the new.
|
---|
118 | xml_serializer.Deserializer._handle_object = deserializer_handle_object
|
---|