Is it possible to construct reversible url names in urls.py using <str:xx>, regex etc

26 views
Skip to first unread message

Mikkel Kromann

unread,
Jun 21, 2018, 8:19:08 AM6/21/18
to Django users
I have a lot of models (say 30 or 40) which are so similar, that I have handled them using slightly modified class based views.

In urls.py:
urlpatterns = [
    path
('list/<str:mName>/',            ItemListView.as_view()),
    path
('create/<str:mName>/',          ItemCreateView.as_view()),
    path
('update/<str:mName>/<pk>',      ItemUpdateView.as_view()),
    path
('delete/<str:mName>/<pk>',      ItemDeleteView.as_view()),
]

However, I'd really like to give all my urls names so that they can be easily reversed (e.g. success links).
urlpatterns = [
    path
('list/<str:mName>/',            ItemListView.as_view(),      name=mName+'_list'),
]

From what I can see, my model name mName is passed only to the view and apparently not to the name constructor inside urls.py
Also, while I do not entirely understand the reverse naming process, I sense that it might not be too easy to reverse the url name if it is not spelled out directly.

Are there any options for handling this in urls.py?
Or should I make a filter that will transform the model name and operation name to an url and accept that I have to code url logic outside urls.py
Or could I reprogram the path() function to make this possible?
Or should I violate DRY and simply write 4 path lines for each of my 40 models?


cheers + thanks, Mikkel

Melvyn Sopacua

unread,
Jun 21, 2018, 9:12:31 AM6/21/18
to django...@googlegroups.com
On donderdag 21 juni 2018 10:19:08 CEST Mikkel Kromann wrote:

> However, I'd really like to give all my urls names so that they can be
> easily reversed (e.g. success links).
> urlpatterns = [
> path('list/<str:mName>/', ItemListView.as_view(), name=
> mName+'_list'),
> ]
>
> From what I can see, my model name mName is passed only to the view and
> apparently not to the name constructor inside urls.py
> Also, while I do not entirely understand the reverse naming process, I
> sense that it might not be too easy to reverse the url name if it is not
> spelled out directly.
>
> Are there any options for handling this in urls.py?

Take a look at crudlfap's Router class to see how to generate dynamic urls. In
short: let a router who generates views for models generate the urlpatterns.
All django is looking for is an iterable that is named 'urlpatterns' in a urls
module.

https://github.com/yourlabs/crudlfap/blob/master/src/crudlfap/router.py#L274
--
Melvyn Sopacua

Mikkel Kromann

unread,
Jun 23, 2018, 12:30:09 PM6/23/18
to Django users
So, I think I have a neat idea for solving my problem, simply make a function which returns a list of path() returns.

In urls.py
urlpatterns = GetItemPaths() + [
# other paths
]

In models.py:
def GetItemPaths():    paths = [ ]
   
for i in [ 'Model1', 'Model2', 'Model3' ]:
        p
= path( i + '/list/', ItemListView.as_view(model=i), name=i + '_list' )
        paths
.append(p)

   
return paths

However, there seems to be a problem with trying to pass the model name to my CBV ItemListView.
Unless I replace the model=i with e.g. model=Model1 I get the following error:

'str' object has no attribute '_default_manager'

So it certainly seems that using model=i will not pass the model name.
Is this because the argument for model= expects not a string but an object?


thanks, Mikkel

Melvyn Sopacua

unread,
Jun 23, 2018, 1:08:04 PM6/23/18
to django...@googlegroups.com
On zaterdag 23 juni 2018 14:30:08 CEST Mikkel Kromann wrote:

> In models.py:
> def GetItemPaths(): paths = [ ]
> for i in [ 'Model1', 'Model2', 'Model3' ]:
> p = path( i + '/list/', ItemListView.as_view(model=i), name=i +
> '_list' )
> paths.append(p)
>
> return paths
>
> However, there seems to be a problem with trying to pass the model name to
> my CBV ItemListView.
> Unless I replace the model=i with e.g. model=Model1 I get the following
> error:
>
> 'str' object has no attribute '_default_manager'
>
>
> So it certainly seems that using model=i will not pass the model name.
> Is this because the argument for model= expects not a string but an object?

Yes. You can use import_module from django.functional to pass a dotted path.
Or you can use apps.get_model() to get a model class for a app name / model
name combo.

--
Melvyn Sopacua

Mikkel Kromann

unread,
Jun 25, 2018, 6:25:51 PM6/25/18
to Django users
Thank you so much for your help, Melvyn.
With your help I managed to the the code running :)
Here it is, if somebody should find it helpful.

cheers, Mikkel

From somewhere, e.g. models.py
# The list of model names (just add your model names to it)
def GetItemNames():
   
return [ 'Year', 'Vintage', 'Region', 'Location' ]

# The generator of paths
def GetItemPaths():
    paths
= [ ]
   
for i in GetItemNames():
        m
= apps.get_model('items', i)
        paths
.append(path( 'list/'+i+'/' ,         ItemListView.as_view(model=m),      name=i+'_list'   ) )
        paths
.append(path( 'create/'+i+'/',        ItemCreateView.as_view(model=m),    name=i+'_create' ) )
        paths
.append(path( 'update/'+i+'/<pk>/',   ItemUpdateView.as_view(model=m),    name=i+'_update' ) )
        paths
.append(path( 'delete/'+i+'/<pk>/',   ItemDeleteView.as_view(model=m),    name=i+'_delete' ) )

   
return paths

From urls.py:
from . models import GetItemPaths
urlpatterns
= GetItemPaths() + [
    path
('/to/some/other/views/'    otherview.asView(), name='other_name'),
]
Reply all
Reply to author
Forward
0 new messages