TicketQuery Wiki Macro

The TicketQuery macro lets you display ticket information anywhere that accepts WikiFormatting. The query language used by the [[TicketQuery]] macro is described in the TracQuery page.

Usage

[[TicketQuery]]

Wiki macro listing tickets that match certain criteria.

This macro accepts a comma-separated list of keyed parameters, in the form "key=value".

If the key is the name of a field, the value must use the syntax of a filter specifier as defined in TracQuery#QueryLanguage. Note that this is not the same as the simplified URL syntax used for query: links starting with a ? character. Commas (,) can be included in field values by escaping them with a backslash (\).

Groups of field constraints to be OR-ed together can be separated by a literal or argument.

In addition to filters, several other named parameters can be used to control how the results are presented. All of them are optional.

The format parameter determines how the list of tickets is presented:

  • list -- the default presentation is to list the ticket ID next to the summary, with each ticket on a separate line.
  • compact -- the tickets are presented as a comma-separated list of ticket IDs.
  • count -- only the count of matching tickets is displayed
  • rawcount -- only the count of matching tickets is displayed, not even with a link to the corresponding query (since 1.1.1)
  • table -- a view similar to the custom query view (but without the controls)
  • progress -- a view similar to the milestone progress bars

The max parameter can be used to limit the number of tickets shown (defaults to 0, i.e. no maximum).

The order parameter sets the field used for ordering tickets (defaults to id).

The desc parameter indicates whether the order of the tickets should be reversed (defaults to false).

The group parameter sets the field used for grouping tickets (defaults to not being set).

The groupdesc parameter indicates whether the natural display order of the groups should be reversed (defaults to false).

The verbose parameter can be set to a true value in order to get the description for the listed tickets. For table format only. deprecated in favor of the rows parameter

The rows parameter can be used to specify which field(s) should be viewed as a row, e.g. rows=description|summary

The col parameter can be used to specify which fields should be viewed as columns. For table format only.

For compatibility with Trac 0.10, if there's a last positional parameter given to the macro, it will be used to specify the format. Also, using "&" as a field separator still works (except for order) but is deprecated.

Examples

Example Result Macro
Number of Triage tickets: 661 [[TicketQuery(status=new&milestone=,count)]]
Number of new tickets: 661 [[TicketQuery(status=new,count)]]
Number of reopened tickets: 0 [[TicketQuery(status=reopened,count)]]
Number of assigned tickets: 394 [[TicketQuery(status=assigned,count)]]
Number of invalid tickets: 5197 [[TicketQuery(status=closed,resolution=invalid,count)]]
Number of worksforme tickets: 1073 [[TicketQuery(status=closed,resolution=worksforme,count)]]
Number of duplicate tickets: 4337 [[TicketQuery(status=closed,resolution=duplicate,count)]]
Number of wontfix tickets: 4146 [[TicketQuery(status=closed,resolution=wontfix,count)]]
Number of fixed tickets: 18633 [[TicketQuery(status=closed,resolution=fixed,count)]]
Number of untriaged tickets (milestone unset): 1055 [[TicketQuery(status!=closed,milestone=,count)]]
Total number of tickets: 35420 [[TicketQuery(count)]]
Number of tickets reported or owned by current user: 1488 [[TicketQuery(reporter=$USER,or,owner=$USER,count)]]
Number of tickets created this month: 38 [[TicketQuery(created=thismonth..,count)]]
Number of closed Firefox tickets: 8 [[TicketQuery(status=closed,keywords~=firefox,count)]]
Number of closed Opera tickets: 24 [[TicketQuery(status=closed,keywords~=opera,count)]]
Number of closed tickets affecting Firefox and Opera: 0 [[TicketQuery(status=closed,keywords~=firefox opera,count)]]
Number of closed tickets affecting Firefox or Opera: 32 [[TicketQuery(status=closed,keywords~=firefox|opera,count)]]
Number of tickets that affect Firefox or are closed and affect Opera: 32 [[TicketQuery(status=closed,keywords~=opera,or,keywords~=firefox,count)]]
Number of closed Firefox tickets that don't affect Opera: 0 [[TicketQuery(status=closed,keywords~=firefox -opera,count)]]
Last 3 modified tickets: #36201, #36200, #36186 [[TicketQuery(max=3,order=modified,desc=1,compact)]]

Details of ticket #1:

[[TicketQuery(id=1,col=id|owner|reporter,rows=summary,table)]]

Ticket Owner Reporter
#1 Jacob Adrian Holovaty
Summary Create architecture for anonymous sessions

Format: list

[[TicketQuery(version=0.6|0.7&resolution=duplicate)]]

This is displayed as:

No results

[[TicketQuery(id=123)]]

This is displayed as:

#123
Typo in the model_api/#field-types

Format: compact

[[TicketQuery(version=0.6|0.7&resolution=duplicate, compact)]]

This is displayed as:

No results

Format: count

[[TicketQuery(version=0.6|0.7&resolution=duplicate, count)]]

This is displayed as:

0

Format: progress

[[TicketQuery(milestone=0.12.8&group=type,format=progress)]]

This is displayed as:

Uncategorized

2000 / 2000

Bug

10175 / 10532

New feature

3711 / 4108

Cleanup/optimization

5263 / 5563

Format: table

You can choose the columns displayed in the table format (format=table) using col=<field>. You can specify multiple fields and the order they are displayed in by placing pipes (|) between the columns:

[[TicketQuery(max=3,status=closed,order=id,desc=1,format=table,col=resolution|summary|owner|reporter)]]

This is displayed as:

Full rows

In table format you can specify full rows using rows=<field>:

[[TicketQuery(max=3,status=closed,order=id,desc=1,format=table,col=resolution|summary|owner|reporter,rows=description)]]

This is displayed as:

Results (1 - 3 of 34365)

1 2 3 4 5 6 7 8 9 10 11
Ticket Resolution Summary Owner Reporter
#36200 fixed Mention that RemoteUserMiddleware should be replaced when using custom header middleware with RemoteUserBackend Joonas Häkkinen Joonas Häkkinen
Description

REMOTE_USER authentication docs at https://docs.djangoproject.com/en/5.1/howto//auth-remote-user/ cover using a custom middleware to read the username from HTTP headers. However, it does not specify that the custom middleware should replace RemoteUserMiddleware rather than be appended to MIDDLEWARE.

This is essentially a small omission and might be clear to experienced Django users. However, at least for me, an experienced web developer but totally new to Django, this was surprising and the resulting CSRF validation failure made me suspect something completely different for two full days. Thus I though that a small mention about replacing instead of appending RemoteUserMiddleware with the custom one would be a reasonable addition to the docs.

I will be opening a PR for this shortly unless you think this is not worth including in the docs.

#36199 duplicate Redundant migrations generated with Coalesce Index in GinIndex with SearchVector Ian Bicket
Description

Title: Redundant Migration Generation for Coalesce Index with TextField Output in Django 5.1 (PostgreSQL)

Django Version: 5.1

Database Backend: PostgreSQL (version 15)

Python Version: 3.12

Operating System: Observed on MacOS Sequoia, but likely to generalize

Description: When running makemigrations on a Django 5.1 project using a PostgreSQL backend, the migration autodetector redundantly generates a migration that removes and then recreates a GinIndex with a SearchVector over a Coalesced field where the output_field parameter is set to TextField(). Furthermore, makemigrations can be run an arbitrary number of times, each execution re-creating what is essentially the same migration, removing and recreating the index.

Steps to Reproduce:

Create a Django model with an index using Coalesce and explicitly set output_field=TextField(), such as in the example below:

from django.contrib.postgres.indexes import GinIndex
from django.contrib.postgres.search import SearchVector
from django.db.models import Value
from django.db.models.functions import Coalesce

# ... model definition, etc.

class SummaryItem(models.Model):
    class Meta:
        indexes = [
                GinIndex(
                    SearchVector(
                        Coalesce(
                            'item_text_by_user',
                            'item_text',
                            Value(''),
                            output_field=TextField()
                    ),
                    'source',
                    config='english',
                ),
                name='summary_item_search_index',
            )
        ]

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    deleted_at = models.DateTimeField(null=True)
    item_type = models.CharField(max_length=16, choices=ItemType)
    item_text = models.TextField()
    item_text_by_user = models.TextField(null=True)

Run makemigrations and apply the migration.

Run makemigrations again without making any changes.

Observe that a new migration is generated that removes and recreates the same Coalesce index.

Expected Behavior: Once the initial migration has been applied, subsequent runs of makemigrations should not generate redundant migrations unless there is an actual schema change.

Actual Behavior: Each execution of makemigrations generates an unnecessary migration removing and recreating the Coalesce index.

Potential Cause: The migration autodetector may be treating the TextField instances in the existing and new application state as distinct, even though they are equivalent. This causes Django to incorrectly assume a change has occurred and generate a new migration. It was observed while breaking down the deconstructed indices from the autodetector's from_state and to_state (see here: https://github.com/django/django/blob/0bf412111be686b6b23e00863f5d449d63557dbf/django/db/migrations/autodetector.py#L1347) that the deconstructed new and old indexes were identified as different because the creation_counter attributes of the TextField() passed to output_field in the indexes (which in turn occurred in the ' in the two respective states were different, which is part of how the __eq__ operator is defined for objects inheriting from Field, and therefore the index in the old and new states were identified as separate, even though there was no change in the definition of the index.

Workaround: As a temporary workaround, removing output_field=TextField() and the Value() (which defaults to a CharField rendering the output_field necessary) parameter from Coalesce will prevent redundant migrations if Django can infer the correct type automatically. However, this might not be suitable in all cases.

#36197 fixed Model.relatedmanager.count() always 0 for custom ManyToMany relationships with through model with to_fields Simon Charette mfontana-elem
Description

While upgrading my codebase to Django 5.1 I started noticing some tests were failing. At the end of the day, the reason was that, given an object's manager for a ManyToMany relation I was getting object.relatedmanager.count() != len(object.relatedmanager.all()). The LHS was always zero.

To illustrate my setup, I have the following models:

from django.db import models


class Course(models.Model):
    uuid = models.UUIDField(unique=True, editable=False, verbose_name="UUID")
    title = models.CharField(max_length=200)

class Student(models.Model):
    uuid = models.UUIDField(unique=True, editable=False, verbose_name="UUID")
    name = models.CharField(max_length=100)
    courses = models.ManyToManyField(Course, through="CourseStudent", related_name='students')

class CourseStudent(models.Model):
    course = models.ForeignKey(Course, to_field="uuid", on_delete=models.RESTRICT, null=False)
    student = models.ForeignKey(Student, to_field="uuid", on_delete=models.RESTRICT, null=False)

And the problem is that it course.students.count() generates the following SQL (both for SQLite and Postgres): SELECT COUNT(*) AS "__count" FROM "courses_coursestudent" WHERE "courses_coursestudent"."course_id" = '00000000000000000000000000000001', which is of course wrong.

Under the exact same setup, Django 5.0 generates: SELECT COUNT(*) AS "__count" FROM "courses_student" INNER JOIN "courses_coursestudent" ON ("courses_student"."uuid" = "courses_coursestudent"."student_id") WHERE "courses_coursestudent"."course_id" = '0817704cdf2d478dba5d629b8997f73b'.

I attach a .zip containing a Dockerfile and a src directory to easily reproduce this behavior.

Best regards,

1 2 3 4 5 6 7 8 9 10 11


See also: TracQuery, TracTickets, TracReports

Last modified 13 months ago Last modified on Jan 24, 2024, 9:58:09 AM
Note: See TracWiki for help on using the wiki.
Back to Top