Skip to content

Commit

Permalink
lint: black
Browse files Browse the repository at this point in the history
  • Loading branch information
Viicos committed Mar 8, 2024
1 parent c43fa7f commit 7cf8152
Show file tree
Hide file tree
Showing 10 changed files with 285 additions and 164 deletions.
24 changes: 19 additions & 5 deletions regex_redirects/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@
# Admin action for a generic "CSV Export"
# Django snippets: http://djangosnippets.org/snippets/2369/

def export_as_csv_action(description="Export selected objects as CSV file",
fields=None, exclude=None, header=True):

def export_as_csv_action(
description="Export selected objects as CSV file",
fields=None,
exclude=None,
header=True,
):
"""
This function returns an export csv action
'fields' and 'exclude' work like in django ModelForm
'header' is whether or not to output the column names as the first row
"""

def export_as_csv(modeladmin, request, queryset):
"""
Generic csv export admin action.
Expand All @@ -26,14 +32,22 @@ def export_as_csv(modeladmin, request, queryset):
excludeset = set(exclude)
field_names = field_names - excludeset

response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename=%s.csv' % opts.replace('.', '_')
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = "attachment; filename=%s.csv" % opts.replace(
".", "_"
)

writer = csv.writer(response)
if header:
writer.writerow(list(field_names))
for obj in queryset:
writer.writerow([getattr(obj, field).encode('utf-8', 'replace') for field in field_names])
writer.writerow(
[
getattr(obj, field).encode("utf-8", "replace")
for field in field_names
]
)
return response

export_as_csv.short_description = description
return export_as_csv
9 changes: 5 additions & 4 deletions regex_redirects/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
from .actions import export_as_csv_action
from .models import Redirect

FIELD_LIST = ('old_path', 'new_path', 'regular_expression', 'fallback_redirect')
FIELD_LIST = ("old_path", "new_path", "regular_expression", "fallback_redirect")


class RedirectAdmin(admin.ModelAdmin):
list_display = FIELD_LIST
list_filter = ('regular_expression',)
search_fields = ('old_path', 'new_path')
list_filter = ("regular_expression",)
search_fields = ("old_path", "new_path")

actions = [export_as_csv_action("Export to CSV", fields=FIELD_LIST)]

actions = [export_as_csv_action('Export to CSV', fields=FIELD_LIST)]

admin.site.register(Redirect, RedirectAdmin)
4 changes: 2 additions & 2 deletions regex_redirects/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@


class RegexRedirectsConfig(AppConfig):
name = 'regex_redirects'
verbose_name = 'Regex Redirects'
name = "regex_redirects"
verbose_name = "Regex Redirects"
73 changes: 45 additions & 28 deletions regex_redirects/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
except ImportError:
MiddlewareMixin = object # fallback for Django < 1.10

DJANGO_REGEX_REDIRECTS_CACHE_KEY = 'django-regex-redirects-regular'
DJANGO_REGEX_REDIRECTS_CACHE_REGEX_KEY = 'django-regex-redirects-regex'
DJANGO_REGEX_REDIRECTS_CACHE_KEY = "django-regex-redirects-regular"
DJANGO_REGEX_REDIRECTS_CACHE_REGEX_KEY = "django-regex-redirects-regex"
DJANGO_REGEX_REDIRECTS_CACHE_TIMEOUT = 60

"""
Expand All @@ -26,7 +26,7 @@

class RedirectFallbackMiddleware(MiddlewareMixin):
def __init__(self, *args, **kwargs):
if 'django.contrib.sites' not in settings.INSTALLED_APPS:
if "django.contrib.sites" not in settings.INSTALLED_APPS:
raise ImproperlyConfigured(
"You cannot use RedirectFallbackMiddleware when "
"django.contrib.sites is not installed."
Expand All @@ -43,59 +43,76 @@ def process_response(self, request, response):
return response # No need to check for a redirect for non-404 responses.

full_path = request.get_full_path()
http_host = request.META.get('HTTP_HOST', '')
http_host = request.META.get("HTTP_HOST", "")
if http_host:
if request.is_secure():
http_host = 'https://' + http_host
http_host = "https://" + http_host
else:
http_host = 'http://' + http_host
http_host = "http://" + http_host

redirects = cache.get(DJANGO_REGEX_REDIRECTS_CACHE_KEY)
if redirects is None:
redirects = list(Redirect.objects.all().order_by('fallback_redirect').values())
cache.set(DJANGO_REGEX_REDIRECTS_CACHE_KEY, redirects, DJANGO_REGEX_REDIRECTS_CACHE_TIMEOUT)
redirects = list(
Redirect.objects.all().order_by("fallback_redirect").values()
)
cache.set(
DJANGO_REGEX_REDIRECTS_CACHE_KEY,
redirects,
DJANGO_REGEX_REDIRECTS_CACHE_TIMEOUT,
)

for redirect in redirects:
# Attempt a regular match
if redirect['old_path'] == full_path:
self.increment_redirect(redirect['id'])
if redirect['new_path'].startswith('http'):
return http.HttpResponsePermanentRedirect(redirect['new_path'])
if redirect["old_path"] == full_path:
self.increment_redirect(redirect["id"])
if redirect["new_path"].startswith("http"):
return http.HttpResponsePermanentRedirect(redirect["new_path"])
else:
return http.HttpResponsePermanentRedirect(http_host + redirect['new_path'])
return http.HttpResponsePermanentRedirect(
http_host + redirect["new_path"]
)

if settings.APPEND_SLASH and not request.path.endswith('/'):
if settings.APPEND_SLASH and not request.path.endswith("/"):
# Try appending a trailing slash.
path_len = len(request.path)
slashed_full_path = full_path[:path_len] + '/' + full_path[path_len:]
slashed_full_path = full_path[:path_len] + "/" + full_path[path_len:]

if redirect['old_path'] == slashed_full_path:
self.increment_redirect(redirect['id'])
if redirect['new_path'].startswith('http'):
return http.HttpResponsePermanentRedirect(redirect['new_path'])
if redirect["old_path"] == slashed_full_path:
self.increment_redirect(redirect["id"])
if redirect["new_path"].startswith("http"):
return http.HttpResponsePermanentRedirect(redirect["new_path"])
else:
return http.HttpResponsePermanentRedirect(http_host + redirect['new_path'])
return http.HttpResponsePermanentRedirect(
http_host + redirect["new_path"]
)

reg_redirects = cache.get(DJANGO_REGEX_REDIRECTS_CACHE_REGEX_KEY)
if reg_redirects is None:
reg_redirects = list(Redirect.objects.filter(regular_expression=True).order_by('fallback_redirect').values())
cache.set(DJANGO_REGEX_REDIRECTS_CACHE_REGEX_KEY, reg_redirects,
DJANGO_REGEX_REDIRECTS_CACHE_TIMEOUT)
reg_redirects = list(
Redirect.objects.filter(regular_expression=True)
.order_by("fallback_redirect")
.values()
)
cache.set(
DJANGO_REGEX_REDIRECTS_CACHE_REGEX_KEY,
reg_redirects,
DJANGO_REGEX_REDIRECTS_CACHE_TIMEOUT,
)

for redirect in reg_redirects:
try:
old_path = re.compile(redirect['old_path'], re.IGNORECASE)
old_path = re.compile(redirect["old_path"], re.IGNORECASE)
except re.error:
# old_path does not compile into regex, ignore it and move on to the next one
continue

if re.match(redirect['old_path'], full_path):
self.increment_redirect(redirect['id'])
if re.match(redirect["old_path"], full_path):
self.increment_redirect(redirect["id"])
# Convert $1 into \1 (otherwise users would have to enter \1 via the admin
# which would have to be escaped)
new_path = redirect['new_path'].replace('$', '\\')
new_path = redirect["new_path"].replace("$", "\\")
replaced_path = re.sub(old_path, new_path, full_path)
if redirect['new_path'].startswith('http'):
if redirect["new_path"].startswith("http"):
return http.HttpResponsePermanentRedirect(replaced_path)
else:
return http.HttpResponsePermanentRedirect(http_host + replaced_path)
Expand Down
68 changes: 56 additions & 12 deletions regex_redirects/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,68 @@

class Migration(migrations.Migration):

dependencies = [
]
dependencies = []

operations = [
migrations.CreateModel(
name='Redirect',
name="Redirect",
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('old_path', models.CharField(help_text="This should be an absolute path, excluding the domain name. Example: '/events/search/'.", unique=True, max_length=200, verbose_name='redirect from', db_index=True)),
('new_path', models.CharField(help_text="This can be either an absolute path (as above) or a full URL starting with 'http://'.", max_length=200, verbose_name='redirect to', blank=True)),
('regular_expression', models.BooleanField(default=False, help_text='If checked, the redirect-from and redirect-to fields will also be processed using regular expressions when matching incoming requests.<br>Example: <strong>/projects/.* -> /#!/projects</strong> will redirect everyone visiting a page starting with /projects/<br>Example: <strong>/projects/(.*) -> /#!/projects/$1</strong> will turn /projects/myproject into /#!/projects/myproject<br><br>Invalid regular expressions will be ignored.', verbose_name='Match using regular expressions')),
('fallback_redirect', models.BooleanField(default=False, help_text="This redirect is only matched after all other redirects have failed to match.<br>This allows us to define a general 'catch-all' that is only used as a fallback after more specific redirects have been attempted.", verbose_name='Fallback redirect')),
('nr_times_visited', models.IntegerField(default=0, help_text='Is incremented each time a visitor hits this redirect')),
(
"id",
models.AutoField(
verbose_name="ID",
serialize=False,
auto_created=True,
primary_key=True,
),
),
(
"old_path",
models.CharField(
help_text="This should be an absolute path, excluding the domain name. Example: '/events/search/'.",
unique=True,
max_length=200,
verbose_name="redirect from",
db_index=True,
),
),
(
"new_path",
models.CharField(
help_text="This can be either an absolute path (as above) or a full URL starting with 'http://'.",
max_length=200,
verbose_name="redirect to",
blank=True,
),
),
(
"regular_expression",
models.BooleanField(
default=False,
help_text="If checked, the redirect-from and redirect-to fields will also be processed using regular expressions when matching incoming requests.<br>Example: <strong>/projects/.* -> /#!/projects</strong> will redirect everyone visiting a page starting with /projects/<br>Example: <strong>/projects/(.*) -> /#!/projects/$1</strong> will turn /projects/myproject into /#!/projects/myproject<br><br>Invalid regular expressions will be ignored.",
verbose_name="Match using regular expressions",
),
),
(
"fallback_redirect",
models.BooleanField(
default=False,
help_text="This redirect is only matched after all other redirects have failed to match.<br>This allows us to define a general 'catch-all' that is only used as a fallback after more specific redirects have been attempted.",
verbose_name="Fallback redirect",
),
),
(
"nr_times_visited",
models.IntegerField(
default=0,
help_text="Is incremented each time a visitor hits this redirect",
),
),
],
options={
'ordering': ('fallback_redirect', 'regular_expression', 'old_path'),
'verbose_name': 'redirect',
'verbose_name_plural': 'redirects',
"ordering": ("fallback_redirect", "regular_expression", "old_path"),
"verbose_name": "redirect",
"verbose_name_plural": "redirects",
},
),
]
24 changes: 17 additions & 7 deletions regex_redirects/migrations/0002_auto_20151217_1938.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,28 @@
class Migration(migrations.Migration):

dependencies = [
('regex_redirects', '0001_initial'),
("regex_redirects", "0001_initial"),
]

operations = [
migrations.AlterField(
model_name='redirect',
name='new_path',
field=models.CharField(help_text="This can be either an absolute path (as above) or a full URL starting with 'http://'.", max_length=2000, verbose_name='redirect to', blank=True),
model_name="redirect",
name="new_path",
field=models.CharField(
help_text="This can be either an absolute path (as above) or a full URL starting with 'http://'.",
max_length=2000,
verbose_name="redirect to",
blank=True,
),
),
migrations.AlterField(
model_name='redirect',
name='old_path',
field=models.CharField(help_text="This should be an absolute path, excluding the domain name. Example: '/events/search/'.", max_length=512, verbose_name='redirect from', db_index=True),
model_name="redirect",
name="old_path",
field=models.CharField(
help_text="This should be an absolute path, excluding the domain name. Example: '/events/search/'.",
max_length=512,
verbose_name="redirect from",
db_index=True,
),
),
]
4 changes: 2 additions & 2 deletions regex_redirects/migrations/0004_auto_20170512_1349.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
class Migration(migrations.Migration):

dependencies = [
('regex_redirects', '0002_auto_20151217_1938'),
("regex_redirects", "0002_auto_20151217_1938"),
]

operations = [
migrations.AlterUniqueTogether(
name='redirect',
name="redirect",
unique_together=set([]),
),
]
13 changes: 9 additions & 4 deletions regex_redirects/migrations/0005_auto_20210425_1321.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@
class Migration(migrations.Migration):

dependencies = [
('regex_redirects', '0004_auto_20170512_1349'),
("regex_redirects", "0004_auto_20170512_1349"),
]

operations = [
migrations.AlterField(
model_name='redirect',
name='old_path',
field=models.CharField(db_index=True, help_text="This should be an absolute path, excluding the domain name. Example: '/events/search/'.", max_length=512, verbose_name='redirect from'),
model_name="redirect",
name="old_path",
field=models.CharField(
db_index=True,
help_text="This should be an absolute path, excluding the domain name. Example: '/events/search/'.",
max_length=512,
verbose_name="redirect from",
),
),
]
Loading

0 comments on commit 7cf8152

Please sign in to comment.