Skip to content

customizable identity attribute for ModelView URL generation#922

Open
x23n5902y wants to merge 5 commits intosmithyhq:mainfrom
x23n5902y:modelview-identity
Open

customizable identity attribute for ModelView URL generation#922
x23n5902y wants to merge 5 commits intosmithyhq:mainfrom
x23n5902y:modelview-identity

Conversation

@x23n5902y
Copy link
Copy Markdown

Allow specifying a custom 'identity' attribute in ModelViewMeta for URL generation

  • Enable using a customizable 'identity' attribute instead of default slugified model class name.
  • This allows creating separate ModelView instances for the same model with distinct URLs, improving flexibility and organization.

@x23n5902y
Copy link
Copy Markdown
Author

Hi, @aminalaee
I noticed that your tests are failing, but it seems the errors are coming from files unrelated to my changes. It looks like the issue might not be caused by my code. Could you please take a look? Let me know if I can help!

Thanks!

@mmzeynalli
Copy link
Copy Markdown
Member

@x23n5902y any updates? Or is it still failing because of other files?

@mmzeynalli
Copy link
Copy Markdown
Member

@x23n5902y could you fix this? I fails only on your patch, currently all tests pass

@mmzeynalli mmzeynalli added waiting-for-feedback Waiting feedback/answer/updates from contributor waiting-for-tests Feature is ready, but tests are missing labels Apr 4, 2026
Copy link
Copy Markdown
Member

@mmzeynalli mmzeynalli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@x23n5902y could you also write tests for this? Like two modelviews of the same model, with different identity. Have different actions on each of them, and check they dont "see" each others actions.

Comment thread sqladmin/models.py

cls.pk_columns = get_primary_keys(model)
cls.identity = slugify_class_name(model.__name__)
cls.identity = slugify_class_name(attrs.get("identity", model.__name__))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to do:

attrs.get("identity", slugify_class_name(model.__name__))

@mmzeynalli
Copy link
Copy Markdown
Member

mmzeynalli commented Apr 6, 2026

So, I found the problem. In this patch, 4 tests (same 2 tests in sync and async_view) fail:

FAILED tests/test_views/test_view_async.py::test_list_view_with_relations - assert '<a href="http://testserver/admin/address/details/1">(Address 1)</a>' in '<!DOCTYPE html>\n<html lang="en">\n\n<head>\n  <meta charset="UTF-8">\n  <meta name="viewport" content="width=device-... <script type="text/javascript" sr...
FAILED tests/test_views/test_view_async.py::test_detail_page - assert '<a href="http://testserver/admin/address/details/1">(Address 1)</a>' in '<!DOCTYPE html>\n<html lang="en">\n\n<head>\n  <meta charset="UTF-8">\n  <meta name="viewport" content="width=device-... <script type="text/javascript" sr...

This happens because, in this patch, sqladmin/models.py:ModelView:_build_url_for function has beem changed from

return request.url_for(
            name,
            identity=slugify_class_name(obj.__class__.__name__),
            pk=get_object_identifier(obj),
        )

To

return request.url_for(
            name,
            identity=self.identity,
            pk=get_object_identifier(obj),
        )

Which works for most of the time, EXCEPT when building URL to FK objects, i. e., object from other model (in this case Address), and correspondingly, other identity. We cannot keep it as it was, because if Address model has user-defined identity, this will fail again.

Upon looking for solutions and chatting with AI, I got this:

# application.py:BaseAdmin:add_model_view
self._model_identity_map[view.model] = view.identity

# models.py
def _resolve_identity(self, obj: Any) -> str:
        if isinstance(obj, self.model):
            return self.identity
        return self._admin_ref._model_identity_map.get(
            obj.__class__, slugify_class_name(obj.__class__.__name__)
        )

def _build_url_for(self, name: str, request: Request, obj: Any) -> URL:
        return request.url_for(
            name,
            identity=self._resolve_identity(obj),
            pk=get_object_identifier(obj),
        )

It is kind of an ugly solution, but it works. I am conflicted to fix this: it is really nice to have feature (I have needed it myself), but solution is ugly, and kind of hard to maintain. I will let @aminalaee to decide.

@mmzeynalli mmzeynalli added needs-update There are stuff that needs updated and reviewed again labels Apr 6, 2026
@mmzeynalli mmzeynalli linked an issue Apr 6, 2026 that may be closed by this pull request
2 tasks
@mmzeynalli mmzeynalli added the under-discussion This fix/feature is still under discussion label Apr 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-update There are stuff that needs updated and reviewed again under-discussion This fix/feature is still under discussion waiting-for-feedback Waiting feedback/answer/updates from contributor waiting-for-tests Feature is ready, but tests are missing

Projects

None yet

4 participants