1
1
"""Queryset for the redirects app."""
2
2
3
3
from django .db import models
4
+ from django .db .models import Value , CharField , IntegerField , Q , F , ExpressionWrapper
5
+ from django .db .models .functions import Substr , Length
4
6
5
7
from readthedocs .core .utils .extend import SettingsOverrideObject
6
8
@@ -25,7 +27,79 @@ def api(self, user=None, detail=True):
25
27
return queryset
26
28
27
29
def get_redirect_path_with_status (self , path , full_path = None , language = None , version_slug = None ):
28
- for redirect in self .select_related ('project' ):
30
+ # add extra fields with the ``path`` and ``full_path`` to perform a
31
+ # filter at db level instead with Python
32
+ queryset = self .annotate (
33
+ path = Value (
34
+ path ,
35
+ output_field = CharField (),
36
+ ),
37
+ full_path = Value (
38
+ full_path ,
39
+ output_field = CharField (),
40
+ ),
41
+
42
+ from_url_length = ExpressionWrapper (
43
+ Length ('from_url' ),
44
+ output_field = IntegerField (),
45
+ ),
46
+
47
+ # 1-indexed
48
+ from_url_without_rest = Substr (
49
+ 'from_url' ,
50
+ 1 ,
51
+ F ('from_url_length' ) - 5 , # Strip "$rest"
52
+ output_field = CharField (),
53
+ ),
54
+
55
+ # 1-indexed
56
+ full_path_without_rest = Substr (
57
+ 'full_path' ,
58
+ 1 ,
59
+ F ('from_url_length' ) - 5 , # Strip "$rest"
60
+ output_field = CharField (),
61
+ ),
62
+ )
63
+ prefix = Q (
64
+ redirect_type = 'prefix' ,
65
+ path__startswith = F ('from_url' ),
66
+ )
67
+ page = Q (
68
+ redirect_type = 'page' ,
69
+ path__iexact = F ('from_url' ),
70
+ )
71
+ exact = (
72
+ Q (
73
+ redirect_type = 'exact' ,
74
+ from_url__endswith = '$rest' ,
75
+ # This works around a bug in Django doing a substr and an endswith,
76
+ # so instead we do 2 substrs and an exact
77
+ # https://code.djangoproject.com/ticket/29155
78
+ full_path_without_rest = F ('from_url_without_rest' ),
79
+ ) | Q (
80
+ redirect_type = 'exact' ,
81
+ full_path__iexact = F ('from_url' ),
82
+ )
83
+ )
84
+ sphinx_html = (
85
+ Q (
86
+ redirect_type = 'sphinx_html' ,
87
+ path__endswith = '/' ,
88
+ ) | Q (
89
+ redirect_type = 'sphinx_html' ,
90
+ path__endswith = '/index.html' ,
91
+ )
92
+ )
93
+ sphinx_htmldir = Q (
94
+ redirect_type = 'sphinx_htmldir' ,
95
+ path__endswith = '.html' ,
96
+ )
97
+
98
+ # There should be one and only one redirect returned by this query. I
99
+ # can't think in a case where there can be more at this point. I'm
100
+ # leaving the loop just in case for now
101
+ queryset = queryset .filter (prefix | page | exact | sphinx_html | sphinx_htmldir )
102
+ for redirect in queryset .select_related ('project' ):
29
103
new_path = redirect .get_redirect_path (
30
104
path = path ,
31
105
language = language ,
0 commit comments