@@ -67,6 +67,7 @@ def __init__(
6767 #: blueprint.
6868 self .url_prefix = url_prefix
6969
70+ self .name = self .options .get ("name" , blueprint .name )
7071 self .name_prefix = self .options .get ("name_prefix" , "" )
7172
7273 #: A dictionary with URL defaults that is added to each and every
@@ -96,9 +97,10 @@ def add_url_rule(
9697 defaults = self .url_defaults
9798 if "defaults" in options :
9899 defaults = dict (defaults , ** options .pop ("defaults" ))
100+
99101 self .app .add_url_rule (
100102 rule ,
101- f"{ self .name_prefix } { self .blueprint . name } .{ endpoint } " ,
103+ f"{ self .name_prefix } . { self .name } .{ endpoint } " . lstrip ( "." ) ,
102104 view_func ,
103105 defaults = defaults ,
104106 ** options ,
@@ -252,8 +254,16 @@ def register_blueprint(self, blueprint: "Blueprint", **options: t.Any) -> None:
252254 arguments passed to this method will override the defaults set
253255 on the blueprint.
254256
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+
255263 .. versionadded:: 2.0
256264 """
265+ if blueprint is self :
266+ raise ValueError ("Cannot register a blueprint on itself" )
257267 self ._blueprints .append ((blueprint , options ))
258268
259269 def register (self , app : "Flask" , options : dict ) -> None :
@@ -266,23 +276,48 @@ def register(self, app: "Flask", options: dict) -> None:
266276 with.
267277 :param options: Keyword arguments forwarded from
268278 :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.
271294 """
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+ )
285319
320+ app .blueprints [name ] = self
286321 self ._got_registered_once = True
287322 state = self .make_setup_state (app , options , first_registration )
288323
@@ -298,12 +333,11 @@ def register(self, app: "Flask", options: dict) -> None:
298333
299334 def extend (bp_dict , parent_dict ):
300335 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 } "
303337 parent_dict [key ].extend (values )
304338
305339 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 } "
307341 value = defaultdict (
308342 dict ,
309343 {
@@ -337,7 +371,7 @@ def extend(bp_dict, parent_dict):
337371 if cli_resolved_group is None :
338372 app .cli .commands .update (self .cli .commands )
339373 elif cli_resolved_group is _sentinel :
340- self .cli .name = self . name
374+ self .cli .name = name
341375 app .cli .add_command (self .cli )
342376 else :
343377 self .cli .name = cli_resolved_group
@@ -354,10 +388,12 @@ def extend(bp_dict, parent_dict):
354388 bp_options ["url_prefix" ] = (
355389 state .url_prefix .rstrip ("/" ) + "/" + bp_url_prefix .lstrip ("/" )
356390 )
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 :
358394 bp_options ["url_prefix" ] = state .url_prefix
359395
360- bp_options ["name_prefix" ] = options . get ( "name_prefix" , "" ) + self . name + "."
396+ bp_options ["name_prefix" ] = name
361397 blueprint .register (app , bp_options )
362398
363399 def add_url_rule (
0 commit comments