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

99

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

# Copyright 2009-2015 Luc Saffre 

# License: BSD (see file COPYING for details) 

"""Defines the :class:`DisableDeleteHandler` class. 

""" 

 

from __future__ import unicode_literals 

from past.builtins import cmp 

from builtins import object 

 

import logging 

logger = logging.getLogger(__name__) 

 

from django.conf import settings 

from django.db import models 

 

from .utils import full_model_name as fmn 

 

 

class DisableDeleteHandler(object): 

    """A helper object used to find out whether a known object can be 

    deleted or not. 

 

    Lino installs an instance of this on each model in an attribute 

    `_lino_ddh` during kernel startup. 

 

    .. attribute:: fklist 

 

        A list of tuples `(model, fk)`, one item for each FK field in 

        the application which points to this model. 

 

    .. attribute:: model 

 

        The owning model (i.e. ``m._lino_ddh.model is m`` is True for 

        every model) 

 

    """ 

 

    def __init__(self, model): 

        self.model = model 

        self.fklist = [] 

 

    def add_fk(self, model, fk): 

        # called from kernel during startup. fk_model is None for 

        # fields defined on a parent model. 

 

        for m, fld in self.fklist: 

            if model is m: 

                # avoid duplicate entries caused by MTI children 

                return 

        self.fklist.append((model, fk)) 

 

        def f(a, b): 

            return cmp( 

                fmn(a[0])+'.'+a[1].name, fmn(b[0])+'.'+b[1].name) 

        from functools import cmp_to_key 

        self.fklist.sort(key=cmp_to_key(f)) 

 

    def __str__(self): 

        s = ','.join([m.__name__ + '.' + fk.name for m, fk in self.fklist]) 

        return "<DisableDeleteHandler(%s, %s)>" % (self.model, s) 

 

    def disable_delete_on_object(self, obj, ignore_models=set()): 

        """Return a veto message which explains why this object cannot be 

        deleted.  Return `None` if there is no veto. 

 

        If `ignore_model` (a set of model class objects) is specified, 

        do not check for vetos on ForeignKey fields defined on one of 

        these models. 

 

        """ 

        #logger.info("20101104 called %s.disable_delete(%s)", obj, self) 

        # print "20150831 disable_delete", obj, self 

        for m, fk in self.fklist: 

            if m in ignore_models: 

                # print "20150831 skipping", m, fk 

                continue 

            # if m.__name__.endswith("Partner") and fk.name == 'partner': 

            # print 20150831, m, fk 

            if fk.name in m.allow_cascaded_delete: 

                continue 

            if fk.null and fk.rel.on_delete == models.SET_NULL: 

                continue 

            n = m.objects.filter(**{fk.name: obj}).count() 

            if n: 

                return obj.delete_veto_message(m, n) 

        kernel = settings.SITE.kernel 

        # print "20141208 generic related objects for %s:" % obj 

        for gfk, fk_field, qs in kernel.get_generic_related(obj): 

            if gfk.name in qs.model.allow_cascaded_delete: 

                continue 

            if fk_field.null:  # a nullable GFK is no reason to veto 

                continue 

            n = qs.count() 

            # print "20141208 - %s %s %s" % ( 

            #     gfk.model, gfk.name, qs.query) 

            if n: 

                return obj.delete_veto_message(qs.model, n) 

        return None