Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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

# -*- coding: UTF-8 -*- 

# Copyright 2014 Luc Saffre 

# License: BSD (see file COPYING for details) 

 

""".. management_command:: mergedata 

 

Takes the full name of a python module as argument. It then imports 

this module and expects it to define a function `objects` in its 

global namespace. It calls this function and expects it to yield a 

series of Django instance objects which have not yet been saved. It 

then compares these objects with the "corresponding data" in the 

database and prints a summary to stdout. It then suggests to merge the 

new data into the database. 

 

- It never *deletes* any stored records. 

- All incoming objects either replace an existing (stored) object, or 

  will be added to the database. 

- If an incoming object has a non-empty primary key, then it replaces 

  the corresponding stored object. Otherwise, if the model has 

  `unique` fields, then these cause potential replacement. 

 

Currently the command is only partly implemented, it doesn't yet 

update existing records.  But it detects whether records are new, and 

adds only those. 

 

""" 

 

from __future__ import print_function 

from __future__ import unicode_literals 

 

import logging 

logger = logging.getLogger(__name__) 

 

from optparse import make_option 

 

from django.conf import settings 

from django.core.management.base import BaseCommand, CommandError 

from importlib import import_module 

 

 

class Command(BaseCommand): 

    help = __doc__ 

    args = "action_spec [args ...]" 

 

    option_list = BaseCommand.option_list + ( 

        make_option('--noinput', action='store_false', 

                    dest='interactive', default=True, 

                    help='Do not prompt for input of any kind.'), 

        make_option('--nochange', action='store_true', 

                    dest='nochange', default=False, 

                    help='Dry run. Do not actually change anything.'), 

    ) 

 

    def merge_module(self, name): 

        m = import_module(name) 

 

        for obj in m.objects(): 

            M = obj.__class__ 

            if obj.pk is None: 

                stored = set() 

                for f in obj._meta.fields: 

                    if not f.primary_key and f.unique: 

                        v = getattr(obj, f.name) 

                        fkw = {f.name: v} 

                        try: 

                            o2 = M.objects.get(**fkw) 

                            stored.add(o2) 

                            logger.debug("Found %s with %s=%s", o2, f.name, v) 

                        except M.DoesNotExist: 

                            pass  # ok 

 

                if len(stored) == 0: 

                    obj.full_clean() 

                    if not self.nochange: 

                        obj.save() 

                        tpl = 'New %s %s has been created.' 

                    else: 

                        tpl = 'New %s %s would have been created.' 

                    logger.info(tpl, M._meta.verbose_name, obj) 

                elif len(stored) == 1: 

                    logger.info("Cannot yet update existing %s", obj) 

                else: 

                    logger.info("Found %d existing rows for %s", 

                                len(stored), obj) 

            else: 

                logger.warning("Ignored non-empty primary key %s", obj) 

 

    def handle(self, *args, **options): 

        if len(args) == 0: 

            raise CommandError("I need at least one argument.") 

 

        self.nochange = options['nochange'] 

        self.interactive = options['interactive'] 

 

        for name in args: 

            self.merge_module(name)