Create button, hooked up to python function

Hello, everyone!
I want to make a template with a button, hooked up to the custom python function, so that when i clicked it i could access my function with the request and form.

For example,
I have this model:

class Statistic(Model):
    """Модель для просмотра статистики"""

    id: AutoField = AutoField(primary_key=True)
    work_name: CharField = CharField(verbose_name="Название работы")
    user: ForeignKey = ForeignKey(
        User,
        on_delete=models.SET_NULL,
        null=True,
        verbose_name="Создатель",
    )
    department: ForeignKey = ForeignKey(
        Department,
        on_delete=models.SET_NULL,
        null=True,
        verbose_name="Департамент",
    )
    start_date: DateField = DateField(verbose_name="Начальная дата")
    end_date: DateField = DateField(verbose_name="Конечная дата")
    
    @property
    def dates(self) -> tuple[date, date]:
        return (self.start_date, self.end_date)
    
    @property
    def statistics(self) -> list[Model]:
        return StatisticsService.get_filtered_objects(self.dates, self.user, self.department)

    class Meta:
        managed = False
        verbose_name = "Статистика"
        verbose_name_plural = verbose_name
        db_table = "statistic"

The idea here, that user should get to its view-only page in the admin panel, fill the dates, department and user and press generate. After that i would get necessary objects and with the context push them to the template

My current half-attempt

    def changelist_view(self, request, extra_context=None):
        date_from = request.GET.get('date_from')
        date_to = request.GET.get('date_to')

        filtered_objects = StatisticsService.get_filtered_objects(
            (date_from, date_to), request.user, request,department
        )

I haven’t changed the template yet.

Any ideas how to do add custom button?

I understand that i would need to overwrite the ‘submit’ logic and somehow check the value / class of the pressed button, right?
If that’s so, how can i do it?

What I think you’re asking for is two things:

  1. You want to add a button to the change list page of a model
  2. You want this button to call a python function

For the first, is this button intended to perform something like an Admin action? (In other words, you want to select one or more instances of a model and do something with those instances.)

If so, then I would suggest you add this functionality as an admin action and not by adding an additional button.

Or, the other possibility is that you want to do something completely different - something that doesn’t relate to admin-type activities at all.

In this case, you could add your button directly to a custom change list page template that you create. (You can either override the existing template following the naming conventions, or you can create a custom template and use it by defining the change_list_template attribute in your ModelAdmin class.)

This button would be a submit button to a URL of your choice. Your custom code would then exist in a view associated with that URL.

Nope, not the admin function. Only my custom one
The idea is to reload (or not reload with the AJAX style / extend the context) the current (admin) page and just add a few fields, depending on the result of the StatisticsService.get_filtered_objects.
I know that i probably can build a custom view, but in my understanding they’re outside of the admin panel, and i want them to be inside of the admin panel (with the side-panel of models and etc.)

Can i put my arguments into the POST form and URL to this exact admin page, so that it would reload itself and with the render_change_form i could’ve get my objects and do necessary stuff?

First, a side note: You need to change your thinking about this a bit.

All pages you see in your browser are created by views. No views are “inside” or “outside” any “panels”. The views create the panels that you see. The location of the code generating those views can be anywhere.

Code is not “inside” a page.

The browser requests a URL. That URL is associated with a view. That view generates a page. The contents of that page are completely defined by the view.

There is nothing that prevents you from writing a view that renders the admin templates, providing you supply the same context that those templates expect. Those views would create pages that look exacly like the system views.

But beyond that, I’m not sure I’m following what exactly you’re attempting to do here.

Are you just looking to restrict which instances of a model are showing up on the changelist page?

You can override the get_queryset method for this.

Are you looking to change which fields are shown in the change list?

You can override the get_list_display method to do this.

In either case, you could supply whatever parameters to this view as GET parameters on the request.

You can also completely override the changelist_view to have it create whatever kind of page you want.

In all of these cases, since these pages are primarily generated as part of a GET request, you would pass whatever information into these views via query parameters. This means that whatever button you wish to add needs to dynamically create and add these parameters to the URL being requested.

Another Side Note:

Not “admin function”, “admin action” - that’s custom code that you write that integrates into the changelist page to perform a desired action on a select set of model instances.

That sounds like what i need!
My goal is:

  1. User presses on Statistic as in list_view.
  2. Instead of showing the list of models, i show necessary fields which user should fill and then press on a button
  3. After he presses the page reloads and now my custom function is doing something.

the Statistic model is not managed, created to conveniently allow access from admin panel and have some convenient methods; not creating instances in db.

So, if i understand you correctly, my algorithm should be:

  1. User fills necessary info (the model fields)
  2. On the button press i need to pass parameters to the GET request
  3. On the first (and second) opening of a page i need to check whether the request has necessary parameters and do some stuff that i need to
    Right?

If that’s so, then can you please direct me to the documentation where i could read more about how to with the ‘form submitting’ and pushing custom args to the request.

P. S.
Thanks for terminology fix.
It’s always good to learn something new!

After reading this more full description, I strongly suggest you abandon this attempt at integrating this in the admin.

Create a regular view that provides a page with a form, that upon submission of that form, generates and returns the desired data.

You’re creating a lot of unnecessary work trying to do it this way, and what you’re doing is fundamentally contrary to the design intent of the admin. (See the first two paragraphs at The Django admin site | Django documentation | Django)

If you’re not comfortable with creating a standard view, then I suggest you work your way through the Official Django Tutorial again.

I guess i’ll do just that. It does sound much better.
So, i will need to do my algorithm with the filling and reloading the page, just in a custom view. Is that correct?

Then last question:
I need to add the url in the list of models in the app, so it would look like so:

So that when i clicked on it, it would take me to the necessary view.

Yes. Create a form and a template for your view. Have the view render the form on a GET.
On the POST, have the view accept the input, bind the submitted data to the form, process the data in the form, and generate the output.

Probably the easiest way would be to override the app_list.html template, and add this link to it.
See Overriding admin templates for more information about this.
Note that since this template is used for all apps, your modification to show this link should be wrapped in an if tag to only add it to the template for the desired app.