Opened 11 years ago

Closed 11 years ago

#20521 closed New feature (wontfix)

{% local_url %} to extend {% url %} with instrospection of the rendering app

Reported by: Jorge C. Leitão Owned by: nobody
Component: Template system Version: 1.5
Severity: Normal Keywords: tag, url
Cc: Jorge C. Leitão Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

==Summary==

New feature: use app's directory tree to resolve urls on templates.

==Motivation==
Currently the {% url %} tag requires the full path of the view or a url name to reverse the correct url of the view. This is limited due to either url name collisions or app's name collision.
To avoid url collisions, django implements a URL namespace, which has to be applied on each app coded to be reusable. One of the biggest problems of the current implementation is the difficultly in using the URL namespace for practical proposes: it is mainly used and (limited to) the case where the same app is being deployed on the same site more than one time.

==Proposal==

I here propose a new templatetag, {% local_url %} and a shortcut to a render, "local_render", that solves the url resolving issue, and does not collide with the current django state.

It works as follows: the local_render passes the argument 'current_app' to the django's render (shortcuts.render) with the full path of the app (package.package.(...)). The {% local_url %} then resolves the url according to the current_app. It is basically a copy of the tag {% url %}, where the "render" of the node is slightly different:

# if the context does not have current_app (case where current_app is not used), render normally
# if the context has current_app (e.g. here 'main.main1.main2'):

try to render with view_name as main.main1.main2.view_name
if NoReverseMatch, try to render with self.view_name=main.main1.main2.view_name
if NoReverseMatch, try to render with self.view_name=main.main1.view_name
if NoReverseMatch, try to render with self.view_name=main.view_name
if NoReverseMatch, try to render with self.view_name=view_name
if NoReverseMatch, raise NoReverseMatch

where view_name is the argument passed in init to self.view_name.

I.e. this basically implements a resolver that works over the app's path.

==Discussion==

  • Neither the render nor the tag collide with current implementation (as far as I understood, the current_app is never passed as argument to the url resolver in the {% url %} tag).
  • This deprecate the need of URL namespaces for working with templates: the strategy to resolve URL in templates is with this approach according to the directory structure, and apps must have different names inside a directory.
  • This agrees with django's spirit of using app's as reusable and modules that complement other app's.
  • This allows for views "overload": Consider a child app that is installed on top of a parent which has a view (e.g. views.rules) and a template with {% local_url views.rules %}. The child decides to extend the template (for instance for using the same footers or headers. The child can define a view with the same name (rules) which overloads the parent's view: if the render is called from the child, it uses the view, if it is called from the parent, it is the parent's view. This is the same behavior one would obtain for python classes: the child can always overload a parent's method.
  • This idea can be extended to URL resolvers in general, but that requires a major design decision that I don't want to enter for now.

I already have a code which reproduces the behavior I'm suggesting, which I use for a project of mine. I will wait for approach/criticisms to see if it deserves be written in django's standards.

Change History (2)

comment:1 by Jorge C. Leitão, 11 years ago

Cc: Jorge C. Leitão added

comment:2 by Russell Keith-Magee, 11 years ago

Resolution: wontfix
Status: newclosed

I'm going to mark this wontfix, for two reasons:

1) I don't accept that a new template tag for name resolving is a good idea. There should be one, and only one, obvious way to do it. I don't see how a second url tag will improve matters here.

2) Your whole argument is based on the premise that the current URL reversal scheme is "limited due to either url name collisions or app's name collision". I don't accept that this is true in practice. If you name your URLs things like "update", then collisions are probable, but every best practice I've seen either incorporates the app name into the url name (e.g., django-registration uses 'registration_activation_complete' and 'registration_activate') or actively encourages the use of namespaces -- which are in the control of the end user. By way of proof -- Django has been in public use for almost 8 years, and namespacing has been available for almost 4, and this is the first time I've seen anyone make a public complaint that name collisions are a significant problem that needs a solution.

I'm not claiming you don't (or shouldn't) have a problem in your own code. I'm just questioning whether the problem is so widespread that it warrants the introduction of a second parallel URL reversal scheme.

There might be some merit to your idea of using current_app inside the existing url reversal tools; however, the implementation and consequences of that change would need to be elaborated some more.

If this is an idea you're passionate about, I suggest taking it to django-developers for further discussion. We don't discuss new features on tickets because of the lack of visibility of the discussion. In that discussion, I suggest you spend a lot more time explaining the set of conditions that have led to you getting regular URL name collisions.

Note: See TracTickets for help on using tickets.
Back to Top