-
Notifications
You must be signed in to change notification settings - Fork 528
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handle Project URL Slug Changes #2889
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work!
There's two minor changes I requested, as well as a more holistic approach to redirecting several URLs.
pontoon/base/admin.py
Outdated
@@ -292,6 +292,10 @@ class TranslationAdmin(admin.ModelAdmin): | |||
raw_id_fields = ("entity",) | |||
|
|||
|
|||
class ProjectSlugHistoryAdmin(admin.ModelAdmin): | |||
raw_id_fields = ("project",) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line is not needed, it would be nice however to show the created_at
field in the form.
pontoon/base/models.py
Outdated
created_at = models.DateTimeField(auto_now_add=True) | ||
|
||
|
||
@receiver(pre_save, sender=Project) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please move the signal to the signals.py
file?
pontoon/projects/views.py
Outdated
# Redirect to the current project slug | ||
return redirect("pontoon.projects.project", slug=slug_history.project.slug) | ||
else: | ||
raise Http404("Project not found.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are quite a few views in which we'd need a check like this. For example, we'd also want to redirect the localization dashboard (/{locale}/{project}), translate view (/{locale}/{project}/all-resources/), as well as several AJAX views spread across multiple urls.py files.
Can we instead introduce a few URL patters that will catch old project slugs as proposed initially?
…ug redirection logic and enhanced docstrings
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit concerned with the proposed order of operations, for two reasons:
- This adds a ProjectSlugHistory lookup to every request, even when the slug is the current one used by a project. This seems rather wasteful.
- What happens if a slug is re-used? I can think of two potentially problematic scenarios:
- If a project's slug is renamed
aa
→bb
→aa
, there's potential redirect loop that may be entered. - If one project's slug is renamed
aa
→bb
, and then a different project starts using theaa
slug, requests for it get redirected to thebb
project.
- If a project's slug is renamed
Both of the above concerns would be resolved if the ProjectSlugHistory lookup were only done after an initial project slug lookup failed. Is there a reason this would not work?
Test cases should also be included for the scenarios mentioned above.
pontoon/base/utils.py
Outdated
# Try to find a project using the current slug | ||
try: | ||
Project.objects.get(slug=slug) | ||
# If found, continue with the original view function | ||
return view_func(request, *args, **kwargs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is better, but still adds a project lookup to each request that's then discarded. Is there a way to do as I suggested earlier, and to include this redirection in the error handling rather than proactively?
In each of the three functions that use this decorator, there's a call to get_object_or_404()
for something like Project.objects.get(slug=slug)
. Could that call be changed to trigger this flow, instead of the current decorator approach?
…unction get_project_or_redirect
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good! Some questions and nitpicks on line comments.
@mathjazz Do you want to look at this again, as its shape has evolved somewhat, or should we just merge & push to staging?
This looks good, nice work! I'm pushing the branch to stage now. The only thing I'm left wondering about is whether we should redirect just the three main URLs (current state), or all. |
@flodolo Could you take this for a spin on stage and provide some feedback here? Essentially you should go to Admin, change slug for a project (or projects) and then test if the app behaves as expected. |
This seems to work correctly, although there's an edge case which is not considered, and to me it feels like a bug. I changed the slug for AMO from Then I went to a different project (AMO Linter), and switched its slug to |
@ayanaar Let's extend slug validation to also prevent the use of historic slugs. |
This should be fine, and already handled by this PR. Historical slugs are not required to be unique, but on access the most recent slug wins. For an example of a different system that works this way, see GitHub repo renames: They'll redirect old URLs until and unless you define a new repo with the same name as a past one had. In my opinion, no further work is required on this PR before finally merging it. |
On Pontoon Call we've decided to move on with the current state of the patch. Nice work, @ayanaar! |
Fixes #2152
This pull request addresses the issue of properly redirecting old project slugs to the current ones after a project's slug has been updated. The previous approach using a decorator was refactored into a utility function for more efficient handling of outdated slugs. Below are the changes made:
ProjectSlugHistory Model: A new model called ProjectSlugHistory was created to keep a history of old slugs for each project. This model has fields for the project (a foreign key to the Project model), the old slug, and the timestamp of the change.
Pre-Save Signal: The signal
create_slug_history
was implemented to detect when a project's slug is updated. When a change is detected, a new ProjectSlugHistory instance is created, effectively saving the old slug.get_project_or_redirect Utility Function: This function attempts to get a project with the given slug. If the project doesn't exist, it checks if the slug is in the ProjectSlugHistory and if so, it redirects to the current project slug URL. If the old slug is not found in the history, it raises an Http404 error.
Utility Function Usage: Several views (e.g., localization, translate, project) have been updated to use the new utility function.
Test Cases: Comprehensive test cases have been added to validate the functionality of the
get_project_or_redirect
utility function. This includes scenarios with old slugs, missing slugs, and nonexistent slugs for the project, translate, and localization views. These tests ensure the correctness of the redirection process.This new approach ensures a smooth user experience when slugs are changed, as old links will continue to function correctly by redirecting to the new location. The newly added tests and updated docstrings also enhance code maintainability and clarity.