File: lookup_channel.py

package info (click to toggle)
django-ajax-selects 1.7.0-5
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, sid, trixie
  • size: 356 kB
  • sloc: python: 924; javascript: 191; makefile: 4
file content (146 lines) | stat: -rw-r--r-- 5,011 bytes parent folder | download | duplicates (3)
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
from django.core.exceptions import PermissionDenied
from django.utils.encoding import force_text
from django.utils.html import escape


class LookupChannel(object):

    """
    Subclass this, setting the model and implementing methods to taste.

    Attributes::

        model (Model): The Django Model that this lookup channel will search for.
        plugin_options (dict): Options passed to jQuery UI plugin that are specific to this channel.
        min_length (int): Minimum number of characters user types before a search is initiated.

            This is passed to the jQuery plugin_options.
            It is used in jQuery's UI when filtering results from its own cache.

            It is also used in the django view to prevent expensive database queries.
            Large datasets can choke if they search too often with small queries.
            Better to demand at least 2 or 3 characters.
    """

    model = None
    plugin_options = {}
    min_length = 1

    def get_query(self, q, request):
        """
        Return a QuerySet searching for the query string `q`.

        Note that you may return any iterable so you can return a list or even
        use yield and turn this method into a generator.

        Args:
            q (str, unicode): The query string to search for.
            request (Request): This can be used to customize the search by User
                or to use additional GET variables.

        Returns:
            (QuerySet, list, generator): iterable of related_models
        """
        kwargs = {"%s__icontains" % self.search_field: q}
        return self.model.objects.filter(**kwargs).order_by(self.search_field)

    def get_result(self, obj):
        """
        The text result of autocompleting the entered query.

        For a partial string that the user typed in, each matched result is
        here converted to the fully completed text.

        This is currently displayed only for a moment in the text field after
        the user has selected the item.
        Then the item is displayed in the item_display deck and the text field
        is cleared.

        Args:
            obj (Model):
        Returns:
            str: The object as string
        """
        return escape(force_text(obj))

    def format_match(self, obj):
        """
        (HTML) Format item for displaying in the dropdown.

        Args:
            obj (Model):
        Returns:
            str: formatted string, may contain HTML.
        """
        return escape(force_text(obj))

    def format_item_display(self, obj):
        """
        (HTML) format item for displaying item in the selected deck area.

        Args:
            obj (Model):
        Returns:
            str: formatted string, may contain HTML.
        """
        return escape(force_text(obj))

    def get_objects(self, ids):
        """
        This is used to retrieve the currently selected objects for either ManyToMany or ForeignKey.

        Args:
            ids (list): list of primary keys
        Returns:
            list: list of Model objects
        """
        # Inherited models have a OneToOneField (rather than eg AutoField)
        if getattr(self.model._meta.pk, "remote_field", False):
            # Use the type of the field being referenced (2.0+)
            pk_type = self.model._meta.pk.remote_field.field.to_python
        elif getattr(self.model._meta.pk, "rel", False):
            # Use the type of the field being referenced
            pk_type = self.model._meta.pk.rel.field.to_python
        else:
            pk_type = self.model._meta.pk.to_python

        # Return objects in the same order as passed in here
        ids = [pk_type(pk) for pk in ids]
        things = self.model.objects.in_bulk(ids)
        return [things[aid] for aid in ids if aid in things]

    def can_add(self, user, other_model):
        """
        Check if the user has permission to add a ForeignKey or M2M model.

        This enables the green popup + on the widget.
        Default implentation is the standard django permission check.

        Args:
            user (User)
            other_model (Model): the ForeignKey or M2M model to check if the User can add.
        Returns:
            bool
        """
        from django.contrib.contenttypes.models import ContentType
        ctype = ContentType.objects.get_for_model(other_model)
        return user.has_perm("%s.add_%s" % (ctype.app_label, ctype.model))

    def check_auth(self, request):
        """
        By default only request.user.is_staff have access.

        This ensures that nobody can get your data by simply knowing the lookup URL.

        This is called from the ajax_lookup view.

        Public facing forms (outside of the Admin) should implement this to
        allow non-staff to use this LookupChannel.

        Args:
            request (Request)
        Raises:
            PermissionDenied
        """
        if not request.user.is_staff:
            raise PermissionDenied