class TestFieldTypes(TestCase):
    fixtures = ['test_model']

    @skipIf(not array_available(), "ArrayField is available in Django 1.8+")
    def test_array(self):
        res = bulk_update(TestModel, [{'id': 1, 'array_field': [1]},
                                      {'id': 2, 'array_field': [2]},
                                      {'id': 3, 'array_field': [3]},
                                      {'id': 4, 'array_field': []}])
        self.assertEqual(4, res)
        for pk, name, array_field in TestModel.objects.all().order_by('id').values_list('id', 'name', 'array_field'):
            if pk in {1, 2, 3}:
                self.assertListEqual([pk], array_field)
            elif pk == 4:
                self.assertListEqual([], array_field)
            else:
                self.assertIsNone(array_field)
            self.assertEqual('test%d' % pk, name)

    @skipIf(not jsonb_available(), "JSONB type is available in Postgres 9.4+ and django 1.9+ only")
    def test_jsonb(self):
        res = bulk_update(TestModel, [{'id': 1, 'json_field': {'test': '1'}},
                                      {'id': 2, 'json_field': {'test': '2'}},
                                      {'id': 3, 'json_field': {'test': '3'}},
                                      {'id': 4, 'json_field': {}},
                                      {'id': 5, 'json_field': {'single': "'", "multi": '"'}}])
        self.assertEqual(5, res)
        for pk, name, json_field in TestModel.objects.all().order_by('id').values_list('id', 'name', 'json_field'):
            if pk in {1, 2, 3}:
                self.assertDictEqual({'test': str(pk)}, json_field)
            elif pk == 4:
                self.assertDictEqual({}, json_field)
            elif pk == 5:
                self.assertDictEqual({'single': "'", "multi": '"'}, json_field)
            else:
                self.assertIsNone(json_field)
            self.assertEqual('test%d' % pk, name)

    @skipIf(not hstore_available(), "HStoreField is available in Django 1.8+")
    def test_hstore(self):
        res = bulk_update(TestModel, [{'id': 1, 'hstore_field': {'test': '1'}},
                                      {'id': 2, 'hstore_field': {'test': '2'}},
                                      {'id': 3, 'hstore_field': {'test': '3'}},
                                      {'id': 4, 'hstore_field': {}},
                                      {'id': 5, 'hstore_field': {'single': "'", "multi": '"'}}])
        self.assertEqual(5, res)
        for item in TestModel.objects.all().order_by('id'):
            if item.pk in {1, 2, 3}:
                self.assertDictEqual({'test': str(item.pk)}, item.hstore_field)
            elif item.pk == 4:
                self.assertDictEqual({}, item.hstore_field)
            elif item.pk == 5:
                self.assertDictEqual({'single': "'", "multi": '"'}, item.hstore_field)
            else:
                self.assertIsNone(item.hstore_field)
            self.assertEqual('test%d' % item.pk, item.name)
Exemplo n.º 2
0

class Meta:
    unique_together = ['id', 'name']


# Not all fields are available in different django and postgres versions
model_attrs = {
    'name': models.CharField(max_length=50, null=True, blank=True, default=''),
    'int_field': models.IntegerField(null=True, blank=True),
    'objects': BulkUpdateManager(),
    'Meta': Meta,
    '__module__': __name__
}

if array_available():
    from django.contrib.postgres.fields import ArrayField
    model_attrs['array_field'] = ArrayField(
        models.IntegerField(null=True, blank=True))
    model_attrs['big_array_field'] = ArrayField(models.BigIntegerField(),
                                                default=list)

if hstore_available():
    from django.contrib.postgres.fields import HStoreField
    model_attrs['hstore_field'] = HStoreField(null=True, blank=True)

if jsonb_available():
    JSONField = import_pg_field_or_dummy('JSONField', jsonb_available)
    model_attrs['json_field'] = JSONField(null=True, blank=True)

TestModel = type('TestModel', (models.Model, ), model_attrs)
class TestSetFunctions(TestCase):
    fixtures = ['test_model']

    def test_incr(self):
        res = bulk_update_or_create(TestModel, [{
            'id': 1,
            'int_field': 1
        }, {
            'id': 5,
            'int_field': 5
        }, {
            'id': 11,
            'int_field': 11
        }], set_functions={'int_field': '+'})
        self.assertEqual(3, res)
        for pk, name, int_field in TestModel.objects.all().order_by('id').values_list('id', 'name', 'int_field'):
            if pk in {1, 5}:
                self.assertEqual(2 * pk, int_field)
            else:
                self.assertEqual(pk, int_field)

            if pk != 11:
                self.assertEqual('test%d' % pk, name)
            else:
                self.assertEqual('', name)

    def test_concat_str(self):
        res = bulk_update_or_create(TestModel, [{
            'id': 1,
            'name': 'bulk_update_1'
        }, {
            'id': 5,
            'name': 'bulk_update_5'
        }, {
            'id': 11,
            'name': 'bulk_update_11'
        }], set_functions={'name': '||'})
        self.assertEqual(3, res)
        for pk, name, int_field in TestModel.objects.all().order_by('id').values_list('id', 'name', 'int_field'):
            if pk in {1, 5}:
                self.assertEqual('test%dbulk_update_%d' % (pk, pk), name)
            elif pk == 11:
                self.assertEqual('bulk_update_%d' % pk, name)
            else:
                self.assertEqual('test%d' % pk, name)

            if pk != 11:
                self.assertEqual(pk, int_field)
            else:
                self.assertIsNone(int_field)

    def _test_concat_array(self, iteration, res):
        self.assertEqual(4, res)
        for pk, name, array_field in TestModel.objects.all().order_by('id').values_list('id', 'name', 'array_field'):
            if pk in {1, 2, 11}:
                self.assertListEqual([pk] * iteration, array_field)
            elif pk == 4:
                self.assertListEqual([], array_field)
            else:
                self.assertIsNone(array_field)

            if pk != 11:
                self.assertEqual('test%d' % pk, name)
            else:
                self.assertEqual('', name)

    @skipIf(not array_available(), "ArrayField is available in Django 1.8+")
    def test_concat_array(self):
        for i in range(1, 5):
            res = bulk_update_or_create(TestModel, [{'id': 1, 'array_field': [1]},
                                                    {'id': 2, 'array_field': [2]},
                                                    {'id': 11, 'array_field': [11]},
                                                    {'id': 4, 'array_field': []}], set_functions={'array_field': '||'})
            self._test_concat_array(i, res)

    @skipIf(not array_available(), "ArrayField is available in Django 1.8+")
    def test_concat_empty(self):
        res = bulk_update_or_create(TestModel, [{'id': 11, 'big_array_field': [2147483649]}],
                                    set_functions={'big_array_field': '||'})
        self.assertEqual(1, res)
        self.assertListEqual([2147483649], TestModel.objects.get(pk=11).big_array_field)

    def _test_union_array(self, iteration, res):
        self.assertEqual(4, res)
        for pk, name, array_field in TestModel.objects.all().order_by('id').values_list('id', 'name', 'array_field'):
            if pk == 1:
                self.assertListEqual([pk], array_field)
            elif pk in {2, 11}:
                # Union doesn't save order, let's sort result
                array_field.sort()
                self.assertListEqual(list(range(1, iteration + 1)), array_field)
            elif pk == 4:
                self.assertListEqual([], array_field)
            else:
                self.assertIsNone(array_field)

            if pk != 11:
                self.assertEqual('test%d' % pk, name)
            else:
                self.assertEqual('', name)

    @skipIf(not array_available(), "ArrayField is available in Django 1.8+")
    def test_union_array(self):
        for i in range(1, 5):
            res = bulk_update_or_create(TestModel, [{'id': 1, 'array_field': [1]},
                                                    {'id': 2, 'array_field': [i]},
                                                    {'id': 11, 'array_field': [i]},
                                                    {'id': 4, 'array_field': []}], set_functions={'array_field': 'union'})
            self._test_union_array(i, res)

    def _test_concat_dict(self, iteration, res, field_name, val_as_str=False):
        self.assertEqual(4, res)
        for pk, name, dict_field in TestModel.objects.all().order_by('id').values_list('id', 'name', field_name):
            if pk in {1, 2, 11}:
                # Note that JSON standard uses only strings as keys. So json.dumps will convert it
                expected = {str(i): str(pk) if val_as_str else pk for i in range(1, iteration + 1)}
                self.assertDictEqual(expected, dict_field)
            elif pk == 4:
                self.assertDictEqual({}, dict_field)
            else:
                self.assertIsNone(dict_field)

            if pk != 11:
                self.assertEqual('test%d' % pk, name)
            else:
                self.assertEqual('', name)

    @skipIf(not hstore_available(), "HStoreField is available in Django 1.8+")
    def test_concat_hstore(self):
        for i in range(1, 5):
            res = bulk_update_or_create(TestModel, [{'id': 1, 'hstore_field': {i: 1}},
                                                    {'id': 2, 'hstore_field': {i: 2}},
                                                    {'id': 11, 'hstore_field': {i: 11}},
                                                    {'id': 4, 'hstore_field': {}}],
                                        set_functions={'hstore_field': '||'})
            self._test_concat_dict(i, res, 'hstore_field', val_as_str=True)

    @skipIf(not jsonb_available(), "JSONB type is available in Postgres 9.4+ and django 1.9+ only")
    def test_concat_jsonb(self):
        for i in range(1, 5):
            res = bulk_update_or_create(TestModel, [{'id': 1, 'json_field': {i: 1}},
                                                    {'id': 2, 'json_field': {i: 2}},
                                                    {'id': 11, 'json_field': {i: 11}},
                                                    {'id': 4, 'json_field': {}}], set_functions={'json_field': '||'})
            self._test_concat_dict(i, res, 'json_field')

    def test_eq_not_null(self):
        res = bulk_update_or_create(TestModel, [{
            'id': 1,
            'name': 'bulk_update_1'
        }, {
            'id': 5,
            'name': None
        }, {
            'id': 11,
            'name': None
        }], set_functions={'name': 'eq_not_null'})
        self.assertEqual(3, res)

        # 9 from fixture + 1 created
        self.assertEqual(10, TestModel.objects.all().count())

        for pk, name, int_field in TestModel.objects.all().order_by('id').values_list('id', 'name', 'int_field'):
            if pk in {1}:
                self.assertEqual('bulk_update_%d' % pk, name)
            elif pk == 11:
                # Default name, not None, look https://github.com/M1hacka/django-pg-bulk-update/issues/2
                self.assertEqual('', name)
            else:
                self.assertEqual('test%d' % pk, name)

            if pk == 11:
                self.assertIsNone(int_field)
            else:
                self.assertEqual(pk, int_field)

    @skipIf(not array_available(), "ArrayField is available in Django 1.8+")
    def test_array_remove(self):
        def _test_array_remove(kwargs):
            res = bulk_update_or_create(TestModel, [{'id': 1, 'array_field': 1},
                                                    {'id': 2, 'array_field': 2},
                                                    {'id': 13, 'array_field': 13}],
                                        set_functions={'array_field': 'array_remove'}, **kwargs)
            self.assertEqual(3, res)

            for pk, array_field in TestModel.objects.filter(id__in=[1, 2, 13]).values_list('pk', 'array_field'):
                if pk == 1:
                    self.assertEqual([2], array_field)
                elif pk == 2:
                    self.assertEqual([1], array_field)
                elif pk == 13:
                    self.assertEqual(None, array_field)

        TestModel.objects.all().update(array_field=[1, 2])
        _test_array_remove({'key_is_unique': False})  # Force 3-step query

        TestModel.objects.filter(id=13).delete()
        TestModel.objects.all().update(array_field=[1, 2])
        _test_array_remove({'key_is_unique': True})
Exemplo n.º 4
0
        'PASSWORD': '******',
        'HOST': '127.0.0.1',
        'PORT': '5432'
    }
}

LOGGING = {
    'version': 1,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
        },
    },
    'loggers': {
        'django-pg-bulk-update': {
            'handlers': ['console'],
            'level': 'DEBUG'
        }
    }
}

# DATABASES should be defined before this call
from django_pg_bulk_update.compatibility import jsonb_available, array_available, hstore_available

INSTALLED_APPS = []

if hstore_available() or jsonb_available() or array_available():
    INSTALLED_APPS.append("django.contrib.postgres")

INSTALLED_APPS.extend(["src", "tests"])
class TestSetFunctions(TestCase):
    fixtures = ['test_model']

    def test_incr(self):
        res = bulk_update(TestModel, [{
            'id': 1,
            'int_field': 1
        }, {
            'id': 5,
            'int_field': 5
        }, {
            'id': 8,
            'int_field': 8
        }], set_functions={'int_field': '+'})
        self.assertEqual(3, res)
        for pk, name, int_field in TestModel.objects.all().order_by('id').values_list('id', 'name', 'int_field'):
            if pk in {1, 5, 8}:
                self.assertEqual(2 * pk, int_field)
            else:
                self.assertEqual(pk, int_field)
            self.assertEqual('test%d' % pk, name)

    def test_concat_str(self):
        res = bulk_update(TestModel, [{
            'id': 1,
            'name': 'bulk_update_1'
        }, {
            'id': 5,
            'name': 'bulk_update_5'
        }, {
            'id': 8,
            'name': 'bulk_update_8'
        }], set_functions={'name': '||'})
        self.assertEqual(3, res)
        for pk, name, int_field in TestModel.objects.all().order_by('id').values_list('id', 'name', 'int_field'):
            if pk in {1, 5, 8}:
                self.assertEqual('test%dbulk_update_%d' % (pk, pk), name)
            else:
                self.assertEqual('test%d' % pk, name)
            self.assertEqual(pk, int_field)

    def _test_concat_array(self, iteration, res):
        self.assertEqual(4, res)
        for pk, name, array_field in TestModel.objects.all().order_by('id').values_list('id', 'name', 'array_field'):
            if pk in {1, 2, 3}:
                self.assertListEqual([pk] * iteration, array_field)
            elif pk == 4:
                self.assertListEqual([], array_field)
            else:
                self.assertIsNone(array_field)
            self.assertEqual('test%d' % pk, name)

    def _test_union_array(self, iteration, res):
        self.assertEqual(4, res)
        for pk, name, array_field in TestModel.objects.all().order_by('id').values_list('id', 'name', 'array_field'):
            if pk in {1, 2}:
                self.assertListEqual([pk], array_field)
            elif pk == 3:
                # Order can be different here, so we sort the result
                array_field.sort()
                self.assertListEqual(list(range(1, iteration + 1)), array_field)
            elif pk == 4:
                self.assertListEqual([], array_field)
            else:
                self.assertIsNone(array_field)
            self.assertEqual('test%d' % pk, name)

    @skipIf(not array_available(), "ArrayField is available in Django 1.8+")
    def test_concat_array(self):
        for i in range(1, 5):
            res = bulk_update(TestModel, [{'id': 1, 'array_field': [1]},
                                          {'id': 2, 'array_field': [2]},
                                          {'id': 3, 'array_field': [3]},
                                          {'id': 4, 'array_field': []}], set_functions={'array_field': '||'})
            self._test_concat_array(i, res)

    @skipIf(not array_available(), "ArrayField is available in Django 1.8+")
    def test_union_array(self):
        for i in range(1, 5):
            res = bulk_update(TestModel, [{'id': 1, 'array_field': [1]},
                                          {'id': 2, 'array_field': [2]},
                                          {'id': 3, 'array_field': [i]},
                                          {'id': 4, 'array_field': []}], set_functions={'array_field': 'union'})
            self._test_union_array(i, res)

    def _test_concat_dict(self, iteration, res, field_name, val_as_str=False):
        self.assertEqual(4, res)
        for pk, name, dict_field in TestModel.objects.all().order_by('id').values_list('id', 'name', field_name):
            if pk in {1, 2, 3}:
                # Note that JSON standard uses only strings as keys. So json.dumps will convert it
                expected = {str(i): str(pk) if val_as_str else pk for i in range(1, iteration + 1)}
                self.assertDictEqual(expected, dict_field)
            elif pk == 4:
                self.assertDictEqual({}, dict_field)
            else:
                self.assertIsNone(dict_field)
            self.assertEqual('test%d' % pk, name)

    @skipIf(not hstore_available(), "HStoreField is available in Django 1.8+")
    def test_concat_hstore(self):
        for i in range(1, 5):
            res = bulk_update(TestModel, [{'id': 1, 'hstore_field': {i: 1}},
                                          {'id': 2, 'hstore_field': {i: 2}},
                                          {'id': 3, 'hstore_field': {i: 3}},
                                          {'id': 4, 'hstore_field': {}}], set_functions={'hstore_field': '||'})
            self._test_concat_dict(i, res, 'hstore_field', val_as_str=True)

    @skipIf(not jsonb_available(), "JSONB type is available in Postgres 9.4+ and django 1.9+ only")
    def test_concat_jsonb(self):
        for i in range(1, 5):
            res = bulk_update(TestModel, [{'id': 1, 'json_field': {i: 1}},
                                          {'id': 2, 'json_field': {i: 2}},
                                          {'id': 3, 'json_field': {i: 3}},
                                          {'id': 4, 'json_field': {}}], set_functions={'json_field': '||'})
            self._test_concat_dict(i, res, 'json_field')

    def test_eq_not_null(self):
        # Test, that NULL value in db will be  NULL after update
        TestModel.objects.filter(pk=3).update(int_field=None)

        res = bulk_update(TestModel, [{'id': 1, 'int_field': 2},
                                      {'id': 2, 'int_field': 3},
                                      {'id': 3, 'int_field': None},
                                      {'id': 4, 'int_field': None}], set_functions={'int_field': 'eq_not_null'})
        self.assertEqual(4, res)
        for pk, name, int_field in TestModel.objects.all().order_by('id').values_list('id', 'name', 'int_field'):
            if pk in {1, 2}:
                self.assertEqual(pk, int_field - 1)
            elif pk == 3:
                self.assertIsNone(int_field)
            elif pk == 4:
                self.assertEqual(pk, int_field)
            else:
                self.assertEqual('test%d' % pk, name)
            self.assertEqual('test%d' % pk, name)

    @skipIf(not array_available(), "ArrayField is available in Django 1.8+")
    def test_array_remove(self):
        TestModel.objects.all().update(array_field=[1, 2, 2])

        res = bulk_update(TestModel, [{'id': 1, 'array_field': 1},
                                      {'id': 2, 'array_field': 2},
                                      {'id': 3, 'array_field': 3}],
                          set_functions={'array_field': 'array_remove'})

        self.assertEqual(3, res)

        for pk, array_field in TestModel.objects.all().order_by('id').values_list('id', 'array_field'):
            if pk == 1:
                self.assertListEqual([2, 2], array_field)
            elif pk == 2:
                self.assertListEqual([1], array_field)
            elif pk == 3:
                self.assertListEqual([1, 2, 2], array_field)