Ah, that is but one of the issues - I forgot to check what happened if you were logged in as a normal user rather than a maintainer. Add this too:
diff --git a/patchwork/views/__init__.py b/patchwork/views/__init__.py index d41c4609fe6e..f1acfb3a599e 100644 --- a/patchwork/views/__init__.py +++ b/patchwork/views/__init__.py @@ -280,7 +280,7 @@ def generic_list(request, project, view, view_args=None, filter_settings=None, # but we will need to follow the state and submitter relations for # rendering the list template patches = patches.select_related('state', 'submitter', 'delegate', - 'series') + 'series', 'submitter__user') patches = patches.only('state', 'submitter', 'delegate', 'project', 'series__name', 'name', 'date', 'msgid') Daniel Axtens <d...@axtens.net> writes: >> TODO: The loading of the patch-list page is very slow now because it >> seems that for each call to check if a patch is editable by a user, the >> db is accessed. Changes in the backend need to be made so this is done >> with only done with only one call to the db. > > AFAICT, the issue is that the code does a new db query for every > patch.is_editable() It didn't make things noticable slow for me, but I > do see an explosion in query volume. Anyway, try the following: > > diff --git a/patchwork/models.py b/patchwork/models.py > index 58e4c51e9716..c29f9a988fd5 100644 > --- a/patchwork/models.py > +++ b/patchwork/models.py > @@ -93,10 +93,14 @@ class Project(models.Model): > send_notifications = models.BooleanField(default=False) > use_tags = models.BooleanField(default=True) > > + @cached_property > + def maintainer_users(self): > + return [x.user for x in self.maintainer_project.all().only('user')] > + > def is_editable(self, user): > if not user.is_authenticated: > return False > - return self in user.profile.maintainer_projects.all() > + return user in self.maintainer_users > > @cached_property > def tags(self): > > > FWIW, I still think more than a few maintainers would be surprised that > delegates are editable by normal users... I still think we probably want > to make some guardrails available for projects. Not entirely sure what > that should look like just yet but that's where my head is at. > > Kind regards, > Daniel > >> >> Signed-off-by: Raxel Gutierrez <ra...@google.com> >> --- >> htdocs/README.rst | 6 +++ >> htdocs/js/patch-list.js | 52 ++++++++++++++++++- >> .../patchwork/partials/patch-list.html | 35 +++++++++++-- >> patchwork/views/__init__.py | 6 +++ >> 4 files changed, 94 insertions(+), 5 deletions(-) >> >> diff --git a/htdocs/README.rst b/htdocs/README.rst >> index 6c435124..32550119 100644 >> --- a/htdocs/README.rst >> +++ b/htdocs/README.rst >> @@ -133,6 +133,12 @@ js >> >> Part of Patchwork. >> >> +``patch-list.js.`` >> + Event helpers and other application logic for patch-list.html. These >> + support patch list manipulation (e.g. inline dropdown changes). >> + >> + Part of Patchwork. >> + >> ``selectize.min.js`` >> Selectize is the hybrid of a ``textbox`` and ``<select>`` box. It's jQuery >> based and it has autocomplete and native-feeling keyboard navigation; >> useful >> diff --git a/htdocs/js/patch-list.js b/htdocs/js/patch-list.js >> index 6ae13721..526f5370 100644 >> --- a/htdocs/js/patch-list.js >> +++ b/htdocs/js/patch-list.js >> @@ -1,5 +1,32 @@ >> +import { updateProperty } from "./rest.js"; >> + >> $( document ).ready(function() { >> - $("#patch-list").stickyTableHeaders(); >> + let inlinePropertyDropdowns = $("td > >> select[class^='change-property-']"); >> + $(inlinePropertyDropdowns).each(function() { >> + // Store previous dropdown selection >> + $(this).data("prevProperty", $(this).val()); >> + }); >> + >> + // Change listener for dropdowns that change an individual patch's >> delegate and state properties >> + $(inlinePropertyDropdowns).change((event) => { >> + const property = event.target.getAttribute("value"); >> + const { url, data } = getPatchProperties(event.target, property); >> + const updateMessage = { >> + 'error': "No patches updated", >> + 'success': "1 patch updated", >> + }; >> + updateProperty(url, data, updateMessage).then(isSuccess => { >> + if (!isSuccess) { >> + // Revert to previous selection >> + $(event.target).val($(event.target).data("prevProperty")); >> + } else { >> + // Update to new previous selection >> + $(event.target).data("prevProperty", $(event.target).val()); >> + } >> + }); >> + }); >> + >> + $("#patchlist").stickyTableHeaders(); >> >> $("#check-all").change(function(e) { >> if(this.checked) { >> @@ -9,4 +36,25 @@ $( document ).ready(function() { >> } >> e.preventDefault(); >> }); >> -}); >> \ No newline at end of file >> + >> + /** >> + * Returns the data to make property changes to a patch through PATCH >> request. >> + * @param {Element} propertySelect Property select element modified. >> + * @param {string} property Patch property modified (e.g. "state", >> "delegate") >> + * @return {{property: string, value: string}} >> + * property: Property field to be modified in request. >> + * value: New value for property to be modified to in request. >> + */ >> + function getPatchProperties(propertySelect, property) { >> + const selectedOption = >> propertySelect.options[propertySelect.selectedIndex]; >> + const patchId = >> propertySelect.parentElement.parentElement.dataset.patchId; >> + const propertyValue = (property === "state") ? selectedOption.text >> + : (selectedOption.value === "*") ? null : >> selectedOption.value >> + const data = {}; >> + data[property] = propertyValue; >> + return { >> + "url": `/api/patches/${patchId}/`, >> + "data": data, >> + }; >> + } >> +}); >> diff --git a/patchwork/templates/patchwork/partials/patch-list.html >> b/patchwork/templates/patchwork/partials/patch-list.html >> index aeb26aa8..7d2d2cc9 100644 >> --- a/patchwork/templates/patchwork/partials/patch-list.html >> +++ b/patchwork/templates/patchwork/partials/patch-list.html >> @@ -5,7 +5,7 @@ >> {% load static %} >> >> {% block headers %} >> - <script src="{% static "js/patch-list.js" %}"></script> >> + <script type="module" src="{% static "js/patch-list.js" %}"></script> >> {% endblock %} >> >> {% include "patchwork/partials/filters.html" %} >> @@ -187,8 +187,37 @@ >> <td id="patch-checks:{{patch.id}}" class="text-nowrap">{{ >> patch|patch_checks }}</td> >> <td id="patch-date:{{patch.id}}" class="text-nowrap">{{ >> patch.date|date:"Y-m-d" }}</td> >> <td id="patch-submitter:{{patch.id}}">{{ >> patch.submitter|personify:project }}</td> >> - <td id="patch-delegate:{{patch.id}}">{{ patch.delegate.username }}</td> >> - <td id="patch-state:{{patch.id}}">{{ patch.state }}</td> >> + <td id="patch-delegate:{{patch.id}}"> >> + {% if patch.is_editable %} >> + <select class="change-property-delegate" value="delegate"> >> + {% if not patch.delegate.username %} >> + <option value="*" selected>No delegate</option> >> + {% else %} >> + <option value="*" selected>{{ patch.delegate.username }}</option> >> + {% endif %} >> + {% for maintainer in maintainers %} >> + <option value="{{ maintainer.id }}">{{ maintainer.name }}</option> >> + {% endfor %} >> + </select> >> + {% else %} >> + {{ patch.delegate.username }} >> + {% endif %} >> + </td> >> + <td id="patch-state:{{patch.id}}"> >> + {% if patch.is_editable %} >> + <select class="change-property-state" value="state"> >> + {% for state in states %} >> + {% if state.name == patch.state.name %} >> + <option value="{{ patch.state.ordering }}" selected>{{ >> patch.state }}</option> >> + {% else %} >> + <option value="{{ state.ordering }}">{{ state.name }}</option> >> + {% endif %} >> + {% endfor %} >> + </select> >> + {% else %} >> + {{ patch.state }} >> + {% endif %} >> + </td> >> </tr> >> {% empty %} >> <tr> >> diff --git a/patchwork/views/__init__.py b/patchwork/views/__init__.py >> index 5da8046d..d41c4609 100644 >> --- a/patchwork/views/__init__.py >> +++ b/patchwork/views/__init__.py >> @@ -16,6 +16,7 @@ from patchwork.models import Bundle >> from patchwork.models import BundlePatch >> from patchwork.models import Patch >> from patchwork.models import Project >> +from patchwork.models import State >> from patchwork.models import Check >> from patchwork.paginator import Paginator >> >> @@ -177,6 +178,8 @@ def generic_list(request, project, view, view_args=None, >> filter_settings=None, >> 'project': project, >> 'projects': Project.objects.all(), >> 'filters': filters, >> + 'maintainers': project.maintainer_project.all(), >> + 'states': State.objects.all(), >> } >> >> # pagination >> @@ -287,6 +290,9 @@ def generic_list(request, project, view, view_args=None, >> filter_settings=None, >> Prefetch('check_set', queryset=Check.objects.only( >> 'context', 'user_id', 'patch_id', 'state', 'date'))) >> >> + for patch in patches: >> + patch.is_editable = patch.is_editable(user) >> + >> paginator = Paginator(request, patches) >> >> context.update({ >> -- >> 2.33.0.rc2.250.ged5fa647cd-goog >> >> _______________________________________________ >> Patchwork mailing list >> Patchwork@lists.ozlabs.org >> https://lists.ozlabs.org/listinfo/patchwork _______________________________________________ Patchwork mailing list Patchwork@lists.ozlabs.org https://lists.ozlabs.org/listinfo/patchwork