Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nestable blueprints #593

Closed
nightkr opened this issue Sep 13, 2012 · 33 comments · Fixed by #3923
Closed

Nestable blueprints #593

nightkr opened this issue Sep 13, 2012 · 33 comments · Fixed by #3923
Milestone

Comments

@nightkr
Copy link

nightkr commented Sep 13, 2012

I'd like to be able to register "sub-blueprints" using Blueprint.register_blueprint(*args, **kwargs). This would register the nested blueprints with an app when the "parent" is registered with it. All parameters are preserved, other than url_prefix, which is handled similarly to in add_url_rule. A naíve implementation could look like this:

class Blueprint(object):
    ...

    def register_blueprint(self, blueprint, **options):
        def deferred(state):
            url_prefix = options.get('url_prefix')
            if url_prefix is None:
                url_prefix = blueprint.url_prefix
            if 'url_prefix' in options:
                del options['url_prefix']

            state.app.register_blueprint(blueprint, url_prefix, **options)
        self.record(deferred)
@smartboyathome
Copy link

I agree, this is something I'd like to see as well. I think the best use case would be a modular admin panel, where it is kept in a blueprint and plugins create sub-blueprints that are added to that blueprint.

@nightkr

This comment has been minimized.

@mitsuhiko
Copy link
Contributor

Not sure what the semantics are for this. If someone wants this, please come up with a design for it :)

@smartboyathome
Copy link

I think that dontcare4free's NestableBlueprint provides a good rough design. All that would really be needed is the ability to register blueprints onto an already existing blueprint. I don't think this would really change any code in Blueprint.register() either since, from looking at that and BlueprintSetupState, the same function names are shared between Blueprint and Flask. Looks like it'd be mostly a duplication of register_blueprint from Flask to Blueprint.

Here is how I'd envision Blueprint.register_blueprint would work:

Each blueprint would have its own self.blueprints dict. When register_blueprint is called, it would check whether there is a blueprint with that name already in its internal dict. The logic on what to depending on that result do is duplicated between Flask.register_blueprint and Blueprint.register_blueprint. However, the ID of the child blueprint would have the id of the parent blueprint appended to it, with a '.' between the two. For example, if the parent blueprint was named 'config' and the child blueprint was named 'users', then ultimately its ID would be 'config.users'. Any paths in the child would be prefixed by the parent blueprint's url_prefix as well as the child's.

This is equally as rough as the dontcare4free's design above, and I haven't looked thoroughly through the code to see everywhere that this could affect, but it is possible to do.

@esbullington
Copy link

I'm also hoping for this. Right now, I'm implementing a RESTful API alongside a traditional web application and am ending up writing a lot of non-DRY code because I can't nest blueprints, particularly wrt API error handling (i.e., I have to override Flask's build-in error handling for each API blueprint in the same way). In fact, I'm thinking of trying out dontcare4free's NestableBlueprints subclass to see if it would help solve my problem. I'd love it if this were built in.

@homeworkprod
Copy link
Contributor

I could use something like this as well.

Not sure about the design, though – will test-drive it in my application.

@ghost
Copy link

ghost commented Aug 16, 2013

How strange - flask.Flask and flask.Blueprint are getting more and more similar.
Maybe they should inherit from a common class?

@untitaker
Copy link
Contributor

@Gioi I already suggested something along these lines once in IRC and got some arguments against it (by @mitsuhiko ? i don't remember) but i can't recall the points that were made against it. I also suck at grepping the logs.

@nicksloan
Copy link

Personally, I like the idea of inheriting from a common class. I like to build my blueprints as fairly isolated from their siblings. It would be kind of nice if a blueprint was just a flask app itself. Perhaps there could be a distinction between blueprints that have to be bolted onto a parent, and blueprints/apps that can be run standalone. There is a lot of overlap between the two. I think bringing them closer together could really allow for some powerful uses.

@homeworkprod
Copy link
Contributor

To elaborate on my personal use case:

A specific application contains about two dozen blueprints. Most are coupled with a single entity (one pair for users, news posts, etc.) and contain the usual view methods (create, update, delete, etc.).

However, some blueprints and entities belong together (news posts and images, gallery albums and images, etc.), and that's why I would like to group them together, but preferably without sacrificing the namespace-free view methods (as opposed to switching to create_post, create_image, etc. and combining them in the same blueprint Python module).

So what I'm actually looking for is a way to group blueprints; but there is no need to (infinitely) nest them.

@ghost
Copy link

ghost commented Sep 3, 2013

@homeworkprod AFAIU, nestable blueprints would be a solution to your problem, right?

@danielchatfield
Copy link

@mitsuhiko Is this going anywhere? This seems like a great idea.

@homeworkprod
Copy link
Contributor

@Gioi: Well, they would be a solution, but I'm worried that this would go too far.

The initial post speaks of "sub-blueprints", and I think one additional layer would suffice (versus "nested", which, to me, sounds like multiple layers, and people could really go wild with that).

But then again I might see a problem here that doesn't exist.

@ThiefMaster
Copy link
Member

Wouldn't a proper implementation of "sub-blueprints" pretty much automatically support infinite nesting of blueprints, too, anyway?

@homeworkprod
Copy link
Contributor

Some questions to examine the current proposal:

  • Would this mean we'd have endpoints like admin.news.posts.tags.update?
  • Based on the .view shortcuts, how would one relatively reference a sub-blueprint in an url_for call in a (first-level) blueprint's template?
  • Would ..view in a sub-blueprint point to the parent's view function?

@ThiefMaster: Well, I'd say it could get more complex, as it is usually the case with generalized implementations. But basically, yes, likely.

@nightkr
Copy link
Author

nightkr commented Sep 3, 2013

@homeworkprod

  • Would this mean we'd have endpoints like admin.news.posts.tags.update?

The implementation in the OP doesn't handle this, but I think that makes the most sense.

@nightkr
Copy link
Author

nightkr commented Mar 24, 2014

#750 a duplicate of this?

@Turbo87
Copy link
Contributor

Turbo87 commented Mar 24, 2014

@teozkr #750 is/was a pull request, while this is an issue without commits attached

@nightkr
Copy link
Author

nightkr commented Mar 24, 2014

@Turbo87 true, but @kennethreitz pretty much declined it with a back-to-the-drawing-board message.

@employ
Copy link

employ commented Apr 16, 2014

This would be extremely useful when designing applications, especially with a scenario like this (ala shopify):

  • company.shopify.com
  • company.shopify.com/admin

Where the admin panel blueprint would be nest-able within the company page.

@danielchatfield
Copy link

I thought long and hard about this and whilst I absolutely agree that this would be very very nice to have it would require a rewrite of significant parts of flask (the way blueprints currently work under the hood is not very conducive of this) and I'm not sure it could be done in a completely backwards-compatible way.

@razzius
Copy link

razzius commented May 17, 2014

@danielchatfield: I don't see why it couldn't be backwards-compatible — as Blueprint.register_blueprint is a new method, the old API would be unchanged and things could shift around under the hood as much as they needed to.

Currently, it seems that the best way to implement @employ's Shopify example with /admin consisting of multiple blueprints would be to make admin a normal Python module, implement the blueprints by importing from that module, and make the Flask app register the blueprints directly. My issue with this is that you'd have to re-enter the same "/admin" url prefix for all of its blueprints, which isn't DRY.

@DasIch
Copy link
Contributor

DasIch commented Jul 27, 2014

This issue has now been open for almost two years. In that time frame there has not been a single serious attempt at creating a properly designed solution, which would include documentation and considering the impact a discussion on the implications of this change and potential problems.

Worse the discussion that has occured in this issue shows that fundamentally blueprints are probably a bad idea and better solutions for composing views might need to be found.

So for all those who intend to add another +1 comment, take this under consideration and post something useful, if you want to see something happen or I'm just going to unsubscribe because quite frankly I don't care at all about how many +1s this gets apart from that it's filling my inbox. A sentiment I which I'm probably not alone with.

@untitaker
Copy link
Contributor

A way to replace blueprints should probably start with finding out (or summarizing) what is actually wrong with blueprints. They don't seem like a very elegant solution to me, but i am not sure what problems should be solved by a replacement.

I suggest deleting all 👍 comments for the sake of readability, and also to lock this issue and create a new one about a possible replacement or the future of blueprints.

@iloahz
Copy link

iloahz commented Sep 6, 2014

Why I need this

Say I wanna setup a admin page under /admin, and have a view for test initially, then I added a monitor view. As the test views accumulate, someday I decided to put all of them under admin/test, for unity, this operation is also done to monitor, so the desired file structure is like:

/app
    /admin
        /monitor
            __init__.py
            monitor.py
        /test
            __init__.py
            test.py
        __init__.py
        admin.py

What I expect to do now is:

from .test import test_bp
from .monitor import monitor_bp


admin_bp.register_blueprint(test_bp, url_preix='/test')
admin_bp.register_blueprint(monitor_bp, url_preix='/monitor')

rather than:

import test
import monitor


@admin_bp.add_url_rule('/test/test1', 'test1_view', test.test1_view)
@admin_bp.add_url_rule('/test/test2', 'test2_view', test.test2_view)
@admin_bp.add_url_rule('/monitor/page1', 'page1_view', monitor.page1_view)
@admin_bp.add_url_rule('/monitor/page2', 'page2_view', monitor.page2_view)

Just something like urlpattern's include in Django. As the app grows, it would definitely be a complicated sub-site under some endpoint, what one want to do of course, is to reduce the dependency.

@char101
Copy link

char101 commented Sep 17, 2014

A modification which returns the blueprint

    def add_blueprint(self, name, import_name, **kwargs):
        url_subprefix = kwargs.pop('url_subprefix', None)
        if url_subprefix:
            kwargs.setdefault('url_prefix', self.url_prefix + url_subprefix)
        bp = self.__class__(self.name + '.' + name, import_name, **kwargs)

        def later(state):
            state.app.register_blueprint(bp)
        self.record(later)

        return bp

@Jaza
Copy link

Jaza commented Feb 6, 2015

An alternative to sub-blueprints, is to break a one-file Blueprint into multiple py files, to import the secondary files in the main Blueprint file, and to then loop through the imported routes and call bp.add_url_rule() on them.

I created a Gist with the code for doing this:

https://gist.github.com/Jaza/61f879f577bc9d06029e

This doesn't provide sub-prefixing for the secondary routes, the way that nestable blueprints would (although in my use case I didn't want them anyway). Although, one could do auto-sub-prefixing in the for r in routes loop, with some simple concatenation. It's mainly just for splitting up a Blueprint that's grown too big to have all the routes in one py file.

@chadgit

This comment has been minimized.

@ThiefMaster

This comment has been minimized.

@sharifzadesina

This comment has been minimized.

@davidism
Copy link
Member

Hi everyone, I appreciate that you're trying to help, but please stop posting variations of code that adds a prefix to all routes. The goal of nestable blueprints in Flask is to be fully nestable, including request.blueprint, url_for, before_request, and all other parts of the request lifecycle. This is not impossible, but it's much more than just a prefix.

If this feature is important to you, please consider #3215 as a good place to start contributing.

@bekab95
Copy link

bekab95 commented Aug 2, 2020

Hi everyone, I appreciate that you're trying to help, but please stop posting variations of code that adds a prefix to all routes. The goal of nestable blueprints in Flask is to be fully nestable, including request.blueprint, url_for, before_request, and all other parts of the request lifecycle. This is not impossible, but it's much more than just a prefix.

If this feature is important to you, please consider #3215 as a good place to start contributing.

@davidism does this PR #3709 close the issue ?

@ThiefMaster
Copy link
Member

No, AFAICT this is just refactoring to reduce similar/duplicate code between Flask and Blueprint.

@greyli greyli linked a pull request Feb 25, 2021 that will close this issue
6 tasks
@Abdur-rahmaanJ Abdur-rahmaanJ mentioned this issue Feb 25, 2021
6 tasks
@davidism davidism added this to the 2.0.0 milestone Apr 14, 2021
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 29, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.