@@ -67,6 +67,7 @@ def __init__(
67
67
#: blueprint.
68
68
self .url_prefix = url_prefix
69
69
70
+ self .name = self .options .get ("name" , blueprint .name )
70
71
self .name_prefix = self .options .get ("name_prefix" , "" )
71
72
72
73
#: A dictionary with URL defaults that is added to each and every
@@ -96,9 +97,10 @@ def add_url_rule(
96
97
defaults = self .url_defaults
97
98
if "defaults" in options :
98
99
defaults = dict (defaults , ** options .pop ("defaults" ))
100
+
99
101
self .app .add_url_rule (
100
102
rule ,
101
- f"{ self .name_prefix } { self .blueprint . name } .{ endpoint } " ,
103
+ f"{ self .name_prefix } . { self .name } .{ endpoint } " . lstrip ( "." ) ,
102
104
view_func ,
103
105
defaults = defaults ,
104
106
** options ,
@@ -252,8 +254,16 @@ def register_blueprint(self, blueprint: "Blueprint", **options: t.Any) -> None:
252
254
arguments passed to this method will override the defaults set
253
255
on the blueprint.
254
256
257
+ .. versionchanged:: 2.0.1
258
+ The ``name`` option can be used to change the (pre-dotted)
259
+ name the blueprint is registered with. This allows the same
260
+ blueprint to be registered multiple times with unique names
261
+ for ``url_for``.
262
+
255
263
.. versionadded:: 2.0
256
264
"""
265
+ if blueprint is self :
266
+ raise ValueError ("Cannot register a blueprint on itself" )
257
267
self ._blueprints .append ((blueprint , options ))
258
268
259
269
def register (self , app : "Flask" , options : dict ) -> None :
@@ -266,23 +276,48 @@ def register(self, app: "Flask", options: dict) -> None:
266
276
with.
267
277
:param options: Keyword arguments forwarded from
268
278
:meth:`~Flask.register_blueprint`.
269
- :param first_registration: Whether this is the first time this
270
- blueprint has been registered on the application.
279
+
280
+ .. versionchanged:: 2.0.1
281
+ Nested blueprints are registered with their dotted name.
282
+ This allows different blueprints with the same name to be
283
+ nested at different locations.
284
+
285
+ .. versionchanged:: 2.0.1
286
+ The ``name`` option can be used to change the (pre-dotted)
287
+ name the blueprint is registered with. This allows the same
288
+ blueprint to be registered multiple times with unique names
289
+ for ``url_for``.
290
+
291
+ .. versionchanged:: 2.0.1
292
+ Registering the same blueprint with the same name multiple
293
+ times is deprecated and will become an error in Flask 2.1.
271
294
"""
272
- first_registration = False
273
-
274
- if self .name in app .blueprints :
275
- assert app .blueprints [self .name ] is self , (
276
- "A name collision occurred between blueprints"
277
- f" { self !r} and { app .blueprints [self .name ]!r} ."
278
- f" Both share the same name { self .name !r} ."
279
- f" Blueprints that are created on the fly need unique"
280
- f" names."
281
- )
282
- else :
283
- app .blueprints [self .name ] = self
284
- first_registration = True
295
+ first_registration = not any (bp is self for bp in app .blueprints .values ())
296
+ name_prefix = options .get ("name_prefix" , "" )
297
+ self_name = options .get ("name" , self .name )
298
+ name = f"{ name_prefix } .{ self_name } " .lstrip ("." )
299
+
300
+ if name in app .blueprints :
301
+ existing_at = f" '{ name } '" if self_name != name else ""
302
+
303
+ if app .blueprints [name ] is not self :
304
+ raise ValueError (
305
+ f"The name '{ self_name } ' is already registered for"
306
+ f" a different blueprint{ existing_at } . Use 'name='"
307
+ " to provide a unique name."
308
+ )
309
+ else :
310
+ import warnings
311
+
312
+ warnings .warn (
313
+ f"The name '{ self_name } ' is already registered for"
314
+ f" this blueprint{ existing_at } . Use 'name=' to"
315
+ " provide a unique name. This will become an error"
316
+ " in Flask 2.1." ,
317
+ stacklevel = 4 ,
318
+ )
285
319
320
+ app .blueprints [name ] = self
286
321
self ._got_registered_once = True
287
322
state = self .make_setup_state (app , options , first_registration )
288
323
@@ -298,12 +333,11 @@ def register(self, app: "Flask", options: dict) -> None:
298
333
299
334
def extend (bp_dict , parent_dict ):
300
335
for key , values in bp_dict .items ():
301
- key = self .name if key is None else f"{ self .name } .{ key } "
302
-
336
+ key = name if key is None else f"{ name } .{ key } "
303
337
parent_dict [key ].extend (values )
304
338
305
339
for key , value in self .error_handler_spec .items ():
306
- key = self . name if key is None else f"{ self . name } .{ key } "
340
+ key = name if key is None else f"{ name } .{ key } "
307
341
value = defaultdict (
308
342
dict ,
309
343
{
@@ -337,7 +371,7 @@ def extend(bp_dict, parent_dict):
337
371
if cli_resolved_group is None :
338
372
app .cli .commands .update (self .cli .commands )
339
373
elif cli_resolved_group is _sentinel :
340
- self .cli .name = self . name
374
+ self .cli .name = name
341
375
app .cli .add_command (self .cli )
342
376
else :
343
377
self .cli .name = cli_resolved_group
@@ -354,10 +388,12 @@ def extend(bp_dict, parent_dict):
354
388
bp_options ["url_prefix" ] = (
355
389
state .url_prefix .rstrip ("/" ) + "/" + bp_url_prefix .lstrip ("/" )
356
390
)
357
- else :
391
+ elif bp_url_prefix is not None :
392
+ bp_options ["url_prefix" ] = bp_url_prefix
393
+ elif state .url_prefix is not None :
358
394
bp_options ["url_prefix" ] = state .url_prefix
359
395
360
- bp_options ["name_prefix" ] = options . get ( "name_prefix" , "" ) + self . name + "."
396
+ bp_options ["name_prefix" ] = name
361
397
blueprint .register (app , bp_options )
362
398
363
399
def add_url_rule (
0 commit comments