| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
 | from __future__ import unicode_literals
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.generic import GenericRelation, GenericForeignKey
from django.db import models
from django.test import TestCase
from rest_framework import serializers
try:
    from django.utils.encoding import python_2_unicode_compatible
except ImportError:
    def python_2_unicode_compatible(klass):
        """
        A decorator that defines __unicode__ and __str__ methods under Python 2.
        Under Python 3 it does nothing.
        To support Python 2 and 3 with a single code base, define a __str__ method
        returning text and apply this decorator to the class.
        """
        if '__str__' not in klass.__dict__:
            raise ValueError("@python_2_unicode_compatible cannot be applied "
                             "to %s because it doesn't define __str__()." %
                             klass.__name__)
        klass.__unicode__ = klass.__str__
        klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
        return klass
@python_2_unicode_compatible
class Tag(models.Model):
    """
    Tags have a descriptive slug, and are attached to an arbitrary object.
    """
    tag = models.SlugField()
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    tagged_item = GenericForeignKey('content_type', 'object_id')
    def __str__(self):
        return self.tag
@python_2_unicode_compatible
class Bookmark(models.Model):
    """
    A URL bookmark that may have multiple tags attached.
    """
    url = models.URLField()
    tags = GenericRelation(Tag)
    def __str__(self):
        return 'Bookmark: %s' % self.url
@python_2_unicode_compatible
class Note(models.Model):
    """
    A textual note that may have multiple tags attached.
    """
    text = models.TextField()
    tags = GenericRelation(Tag)
    def __str__(self):
        return 'Note: %s' % self.text
class TestGenericRelations(TestCase):
    def setUp(self):
        self.bookmark = Bookmark.objects.create(url='https://www.djangoproject.com/')
        Tag.objects.create(tagged_item=self.bookmark, tag='django')
        Tag.objects.create(tagged_item=self.bookmark, tag='python')
        self.note = Note.objects.create(text='Remember the milk')
        Tag.objects.create(tagged_item=self.note, tag='reminder')
    def test_generic_relation(self):
        """
        Test a relationship that spans a GenericRelation field.
        IE. A reverse generic relationship.
        """
        class BookmarkSerializer(serializers.ModelSerializer):
            tags = serializers.RelatedField(many=True)
            class Meta:
                model = Bookmark
                exclude = ('id',)
        serializer = BookmarkSerializer(self.bookmark)
        expected = {
            'tags': ['django', 'python'],
            'url': 'https://www.djangoproject.com/'
        }
        self.assertEqual(serializer.data, expected)
    def test_generic_nested_relation(self):
        """
        Test saving a GenericRelation field via a nested serializer.
        """
        class TagSerializer(serializers.ModelSerializer):
            class Meta:
                model = Tag
                exclude = ('content_type', 'object_id')
        class BookmarkSerializer(serializers.ModelSerializer):
            tags = TagSerializer()
            class Meta:
                model = Bookmark
                exclude = ('id',)
        data = {
            'url': 'https://docs.djangoproject.com/',
            'tags': [
                {'tag': 'contenttypes'},
                {'tag': 'genericrelations'},
            ]
        }
        serializer = BookmarkSerializer(data=data)
        self.assertTrue(serializer.is_valid())
        serializer.save()
        self.assertEqual(serializer.object.tags.count(), 2)
    def test_generic_fk(self):
        """
        Test a relationship that spans a GenericForeignKey field.
        IE. A forward generic relationship.
        """
        class TagSerializer(serializers.ModelSerializer):
            tagged_item = serializers.RelatedField()
            class Meta:
                model = Tag
                exclude = ('id', 'content_type', 'object_id')
        serializer = TagSerializer(Tag.objects.all(), many=True)
        expected = [
        {
            'tag': 'django',
            'tagged_item': 'Bookmark: https://www.djangoproject.com/'
        },
        {
            'tag': 'python',
            'tagged_item': 'Bookmark: https://www.djangoproject.com/'
        },
        {
            'tag': 'reminder',
            'tagged_item': 'Note: Remember the milk'
        }
        ]
        self.assertEqual(serializer.data, expected)
 |