david / django-portablecontacts (http://portablecontacts.net/)

Support for PortableContacts in Django.

Changed (Δ7.5 KB):

raw changeset »

portablecontacts/serializers/__init__.py (13 lines added, 0 lines removed)

portablecontacts/serializers/python.py (13 lines added, 19 lines removed)

portablecontacts/serializers/xml_serializer.py (87 lines added, 7 lines removed)

portablecontacts/tests.py (93 lines added, 2 lines removed)

portablecontacts_example/manage.py (1 lines added, 1 lines removed)

Up to file-list portablecontacts/serializers/__init__.py:

1
2
convert_classname_to_name = {
3
    'portablecontacts:contactemail': 'emails',
4
    'portablecontacts:contacturl': 'urls',
5
    'portablecontacts:contactphonenumber': 'phoneNumbers',
6
    'portablecontacts:contactim': 'ims',
7
    'portablecontacts:contactphoto': 'photos',
8
    'portablecontacts:contacttag': 'tags',
9
    'portablecontacts:contactrelationship': 'relationships',
10
    'portablecontacts:contactaddress': 'addresses',
11
    'portablecontacts:contactorganization': 'organizations',
12
    'portablecontacts:contactaccount': 'accounts',
13
}

Up to file-list portablecontacts/serializers/python.py:

@@ -4,32 +4,28 @@ from django.utils.encoding import smart_
4
4
from django.core.serializers.python import Serializer as DjangoPythonSerializer
5
5
6
6
from portablecontacts.models import contact_model
7
from portablecontacts.serializers import convert_classname_to_name
7
8
8
convert_classname_to_name = {
9
    'portablecontacts:contactemail': 'emails',
10
    'portablecontacts:contacturl': 'urls',
11
    'portablecontacts:contactphonenumber': 'phoneNumbers',
12
    'portablecontacts:contactim': 'ims',
13
    'portablecontacts:contactphoto': 'photos',
14
    'portablecontacts:contacttag': 'tags',
15
    'portablecontacts:contactrelationship': 'relationships',
16
    'portablecontacts:contactaddress': 'addresses',
17
    'portablecontacts:contactorganization': 'organizations',
18
    'portablecontacts:contactaccount': 'accounts',
19
}
20
9
21
10
class Serializer(DjangoPythonSerializer):
22
11
    """
23
12
    Serializes a QuerySet to basic Python objects.
24
13
    """
14
    def start_object(self, obj):
15
        """
16
        Called as each object is handled.
17
        """
18
        self._current = {}
19
        if isinstance(obj, contact_model):
20
            self._current.update({
21
                "id": smart_unicode(obj._get_pk_val(), strings_only=True),
22
            })
23
25
24
    def end_object(self, obj):
26
25
        """
27
26
        Called after handling all fields for an object.
28
27
        """
29
28
        if isinstance(obj, contact_model):
30
            self._current.update({
31
                "id": smart_unicode(obj._get_pk_val(), strings_only=True),
32
            })
33
29
            for rel_object in obj._meta.get_all_related_objects():
34
30
                objects = rel_object.model.objects.filter(contact=obj)
35
31
                if objects:
@@ -93,10 +89,8 @@ class Serializer(DjangoPythonSerializer)
93
89
            return
94
90
        
95
91
        if field_name == 'type':
96
            type_display = getattr(obj, 'get_%s_display' % field_name)()
97
            self._current_reverse[field_name] = smart_unicode(type_display, strings_only=True)
98
        else:
99
            self._current_reverse[field_name] = smart_unicode(field_content, strings_only=True)
92
            field_content = getattr(obj, 'get_%s_display' % field_name)()
93
        self._current_reverse[field_name] = smart_unicode(field_content, strings_only=True)
100
94
    
101
95
    def handle_fk_field(self, obj, field):
102
96
        """

Up to file-list portablecontacts/serializers/xml_serializer.py:

@@ -3,6 +3,9 @@ from django.core.serializers.xml_seriali
3
3
from django.utils.xmlutils import SimplerXMLGenerator
4
4
from django.utils.encoding import smart_unicode
5
5
6
from portablecontacts.models import contact_model
7
from portablecontacts.serializers import convert_classname_to_name
8
6
9
class Serializer(DjangoXMLSerializer):
7
10
    """
8
11
    Convert a queryset to XML.
@@ -16,6 +19,14 @@ class Serializer(DjangoXMLSerializer):
16
19
        self.xml = SimplerXMLGenerator(self.stream, self.options.get("encoding", settings.DEFAULT_CHARSET))
17
20
        self.xml.startDocument()
18
21
        self.xml.startElement("response", {})
22
        
23
        if not self.options.get('unique', False):
24
            self.indent(1)
25
            self.xml.addQuickElement("startIndex", self.options.get('startIndex', '0'))
26
            self.indent(1)
27
            self.xml.addQuickElement("itemsPerPage", self.options.get('itemsPerPage', '0'))
28
            self.indent(1)
29
            self.xml.addQuickElement("totalResults", self.options.get('totalResults', '0'))
19
30
20
31
    def end_serialization(self):
21
32
        """
@@ -31,11 +42,28 @@ class Serializer(DjangoXMLSerializer):
31
42
        """
32
43
        self.indent(1)
33
44
        self.xml.startElement("entry", {})
45
        self.indent(2)
46
        self.xml.addQuickElement('id', smart_unicode(obj.id))
47
        
48
        self.has_name = False
49
        self.has_displayName = False
34
50
35
51
    def end_object(self, obj):
36
52
        """
37
53
        Called after handling all fields for an object.
38
54
        """
55
        if isinstance(obj, contact_model):
56
            for rel_object in obj._meta.get_all_related_objects():
57
                objects = rel_object.model.objects.filter(contact=obj)
58
                if objects:
59
                    name = convert_classname_to_name[rel_object.name]
60
                    self.handle_reverse_fk_fields(objects, name)
61
                    
62
            #if 'tags' in self._current:
63
            #    self._current['tags'] = [tag['value'] for tag in self._current['tags']]
64
            #if 'relationships' in self._current:
65
            #    self._current['relationships'] = [relationship['value'] \
66
            #                                        for relationship in self._current['relationships']]
39
67
        self.indent(1)
40
68
        self.xml.endElement("entry")
41
69
@@ -46,13 +74,35 @@ class Serializer(DjangoXMLSerializer):
46
74
        """
47
75
        # Get a "string version" of the object's data (this is handled by the
48
76
        # serializer base class).
49
        field_content = getattr(obj, field.name)
50
        if not field_content or field.name in ('published', 'updated'):
51
            return
52
        
53
        self.indent(2)
54
        value = self.get_string_value(obj, field)
55
        self.xml.addQuickElement(field.name, smart_unicode(value))
77
        field_name = field.name
78
        field_content = getattr(obj, field_name)
79
80
        if field_content:
81
            if field_name in ('userName', 'familyName', 'givenName', 
82
                'middleName', 'honorificPrefix', 'honorificSuffix'):
83
                if not self.has_displayName:
84
                    self.indent(2)
85
                    self.xml.addQuickElement('displayName', smart_unicode(getattr(obj, 'displayName'), strings_only=True))
86
                    self.has_displayName = True
87
                if field_name in ('familyName', 'givenName', 'middleName', 
88
                    'honorificPrefix', 'honorificSuffix'):
89
                    if not self.has_name:
90
                        self.indent(2)
91
                        self.xml.startElement("name", {})
92
                        for local_field_name in ('familyName', 'givenName', 'middleName', 
93
                            'honorificPrefix', 'honorificSuffix'):
94
                            local_field_content = smart_unicode(getattr(obj, local_field_name), strings_only=True)
95
                            if local_field_content:
96
                                self.indent(3)
97
                                self.xml.addQuickElement(local_field_name, local_field_content)
98
                        self.indent(2)
99
                        self.xml.endElement("name")
100
                        self.has_name = True
101
            
102
            elif field_name not in ('published', 'updated'):
103
                self.indent(2)
104
                value = self.get_string_value(obj, field)
105
                self.xml.addQuickElement(field_name, value)
56
106
57
107
    def handle_fk_field(self, obj, field):
58
108
        """
@@ -60,3 +110,33 @@ class Serializer(DjangoXMLSerializer):
60
110
        differently from regular fields).
61
111
        """
62
112
        pass
113
114
    def handle_reverse_fk_fields(self, objs, name):
115
        """
116
        Called to handle a models which have a ForeignKey to the current oject.
117
        """
118
        objects = []
119
        self._current_reverse = {}
120
        for obj in objs:
121
            self.indent(2)
122
            self.xml.startElement(name, {})
123
            for field in obj._meta.local_fields:
124
                if field.serialize and field.rel is None \
125
                    and (self.selected_fields is None or field.attname in self.selected_fields):
126
                    field_name = field.name
127
                    field_content = getattr(obj, field_name)
128
                    if not field_content or (field_name=='primary' and field_content==False):
129
                        continue
130
                    
131
                    if field_content == False:
132
                        field_content = 'false'
133
                    elif field_content == True:
134
                        field_content = 'true'
135
136
                    self.indent(3)
137
                    if field_name == 'type':
138
                        field_content = getattr(obj, 'get_%s_display' % field_name)()
139
                    self.xml.addQuickElement(field.name, smart_unicode(field_content, strings_only=True))
140
                    
141
            self.indent(2)
142
            self.xml.endElement(name)

Up to file-list portablecontacts/tests.py:

@@ -371,8 +371,98 @@ data:
371
371
       }
372
372
      }
373
373
374
XML representation::
374
XML representations::
375
375
376
      >>> response = c.get("/portablecontacts/@me/@all/", {'format': 'xml'})
377
      >>> response.status_code
378
      200
379
      >>> print response.content
380
      <?xml version="1.0" encoding="utf-8"?>
381
      <response>
382
       <startIndex>0</startIndex>
383
       <itemsPerPage>0</itemsPerPage>
384
       <totalResults>0</totalResults>
385
       <entry>
386
        <id>1</id>
387
        <displayName>Minimal Contact</displayName>
388
       </entry>
389
       <entry>
390
        <id>2</id>
391
        <displayName>Mork Hashimoto</displayName>
392
        <name>
393
         <familyName>Hashimoto</familyName>
394
         <givenName>Mork</givenName>
395
        </name>
396
        <emails>
397
         <value>mhashimoto-04@plaxo.com</value>
398
         <type>work</type>
399
         <primary>true</primary>
400
        </emails>
401
        <emails>
402
         <value>mhashimoto-04@plaxo.com</value>
403
         <type>home</type>
404
        </emails>
405
        <urls>
406
         <value>http://www.seeyellow.com</value>
407
         <type>work</type>
408
        </urls>
409
        <urls>
410
         <value>http://www.angryalien.com</value>
411
         <type>home</type>
412
        </urls>
413
        <phoneNumbers>
414
         <value>KLONDIKE5</value>
415
         <type>work</type>
416
        </phoneNumbers>
417
        <phoneNumbers>
418
         <value>650-123-4567</value>
419
         <type>mobile</type>
420
        </phoneNumbers>
421
        <ims>
422
         <value>plaxodev8</value>
423
         <type>aim</type>
424
        </ims>
425
        <photos>
426
         <value>http://sample.site.org/photos/12345.jpg</value>
427
         <type>thumbnail</type>
428
        </photos>
429
        <tags>
430
         <value>plaxo guy</value>
431
        </tags>
432
        <relationships>
433
         <value>friend</value>
434
        </relationships>
435
        <addresses>
436
         <formatted>742 Evergreen Terrace
437
      Suite 123
438
      Springfield VT 12345 USA</formatted>
439
         <type>home</type>
440
         <streetAddress>742 Evergreen Terrace
441
      Suite 123</streetAddress>
442
         <locality>Springfield</locality>
443
         <region>VT</region>
444
         <postalCode>12345</postalCode>
445
         <country>USA</country>
446
        </addresses>
447
        <organizations>
448
         <name>Burns Worldwide</name>
449
         <title>Head Bee Guy</title>
450
        </organizations>
451
        <accounts>
452
         <domain>plaxo.com</domain>
453
         <userid>2706</userid>
454
        </accounts>
455
       </entry>
456
       <entry>
457
        <id>3</id>
458
        <displayName>David Larlet</displayName>
459
        <name>
460
         <familyName>Larlet</familyName>
461
         <givenName>David</givenName>
462
        </name>
463
        <preferredUsername>david</preferredUsername>
464
       </entry>
465
      </response>
376
466
      >>> response = c.get("/portablecontacts/@me/@all/1/", {'format': 'xml'})
377
467
      >>> response.status_code
378
468
      200
@@ -380,7 +470,8 @@ XML representation::
380
470
      <?xml version="1.0" encoding="utf-8"?>
381
471
      <response>
382
472
       <entry>
383
        <userName>Minimal Contact</userName>
473
       <id>1</id>
474
        <displayName>Minimal Contact</displayName>
384
475
       </entry>
385
476
      </response>
386
477

Up to file-list portablecontacts_example/manage.py:

1
1
import sys, os
2
sys.path = [os.path.join(os.getcwd(), '../')] + sys.path
2
sys.path = [os.path.join(os.getcwd(), '../'), '../../../lib/django_one'] + sys.path
3
3
4
4
from django.core.management import execute_manager
5
5