A Beginner's Introduction to Python Frameworks

11 min read

beginners introduction python frameworks

So, you’ve started to learn Python.

It doesn’t seem too bad; you can code after all, so it’s just a matter of learning the differences in syntax. Perhaps you’re just at the stage of practicing tutorials and reading books.

But it is high time to start collecting hard experience in Python. It’s time to create your first Python project.

What should you start with? With an idea, obviously, but I’m sure that won’t be a problem. You already have several great concepts waiting for some of your spare time.

What’s next? The choice of a framework. And that’s where the real conundrum starts, because the ecosystem of Python frameworks is quite extensive and varied. Almost like a jungle, if you will.

In this article I’m going describe the most common Python frameworks so you can choose the one you want to start with. Be warned that this is a purely subjective comparison, because it mainly results from my own more-or-less successful attempts to use those frameworks in commercial projects.

Update: We’ve recently expanded this post with two more frameworks you might consider for your project: Pyramid and Sanic. Enjoy the new version!


The most popular Python framework is definitely Django. Its characteristic feature is that within a single package there is everything you need to build a web application, from low- to high-end.

Django applications are based on a design pattern similar to MVC, the so-called MVT (Model-View-Template) pattern. Models are defined using Django ORM. SQL databases are mainly used as storage. Django has a built-in admin panel, allowing for easy management of the database content.

With minimal configuration this panel is automatically generated based on the defined models. Views can include both functions and classes. The assignment of URLs to views is done in one place (the urls.py file), so that after reviewing that single file you can learn what URLs are supported. Templates are created using a fairly simple Django Templates system.

Django is praised for strong community support, and for detailed documentation describing the functionality of the framework. This documentation coupled with the fact that after the installation you get a complete environment, the entry threshold is rather low. After going through the official tutorial, you’ll be able to do most of the things needed to build an application.

Unfortunately, Django’s monolithism also has its drawbacks. It’s difficult, though not impossible, to replace one of the built-in elements with another implementation. For example, using some other ORM (e.g. SQLAlchemy) requires abandoning or completely rebuilding such items as the admin panel, authorization, session handling or generating forms.

Because Django is complete but inflexible, it is suitable for standard applications (i.e. the vast majority of software projects). However, if you need to implement some unconventional design, it leads to a struggle with the framework rather than pleasant programming.

Sample model in Django:

        class Company(models.Model):
    name = models.CharField(max_length=255)
    email = models.EmailField(max_length=75, null=True, blank=True)
    website_url = models.URLField(blank=True, null=True)
    city = models.CharField(max_length=100, null=True, blank=True)
    street = models.CharField(max_length=100, null=True, blank=True)
    size = models.IntegerField(null=True, blank=True)
    date_founded = models.CharField(
        help_text='MM/YYYY', null=True, blank=True, max_length=7,
    def urls(self):
        return {
            'view': reverse('view-company', args=(self.pk,)),
            'edit': reverse('edit-company', args=(self.pk,)),
    def __unicode__(self):
        return self.name


Flask is considered a micro-framework. It comes with basic functionality, but also allows you to easily expand it. Therefore, Flask works more as the glue that allows you to join libraries with each other. For example, “pure Flask” does not provide support for any storage, but there are a number of different implementations that you can install and use interchangeably for that purpose (e.g. Flask-SQLAlchemy, Flask-MongoAlchemy, and Flask-Redis). Similarly, the basic template system is Jinja2, but you can use a replacement (e.g. Mako).

The motto of this framework is “one drop at a time”, and this is reflected in its comprehensive documentation. Knowledge of how to build an application is acquired in portions here – after reading a few paragraphs, you will be able to perform basic tasks. You do not have to know the more advanced stuff – you’ll get to learn it only when you need it. Thanks to this, the Flask student can gather knowledge smoothly and avoid boredom, making Flask suitable for learning.

A large number of Flask extensions, unfortunately, are not as well supported as the framework itself. It happens quite often that the plug-ins are no longer being developed or their documentation is not outdated. In such situations, you need to spend some time googling a replacement that offers similar functionality, but is still actively supported. Building your application with packages from different authors, you might have to put quite a bit of sweat into integrating them with each other. You will rarely find ready-made instructions how to do this in the plug-ins’ documentation, but in such situations the flask community and websites such as Stack Overflow may be helpful.

Sample view in Flask:

@documented(prod_id="ID of a product")
def images_get(version, prod_id):
    """Retrieves a list of product images."""
    return [i.serialize() for i in find_product(prod_id).images]


A third noteworthy web framework called Pyramid is rooted in two other products which are no longer developed: Pylons and repoze.bfg. The legacy left by its predecessors caused Pyramid to evolve into a very mature and stable project.

The philosophies of Pyramid and Django differ substantially, even though both were born in the same year (2005). Unlike Django, Pyramid is trivial to customize, allowing you to create features in ways that were not foreseen by the authors of the framework. It does not force the programmer to use framework’s idioms. It’s meant to be a solid scaffolding for complex or highly non-standard projects.

Pyramid strives to be persistence-agnostic. There is no bundled database access module, but a common practice is to combine Pyramid with the powerful, mature SQLAlchemy ORM. Of course, that’s only the most popular way to go. Programmers are free to use whatever tool they find useful, such as using peewee ORM instead, writing raw SQL queries or integrating with a NoSQL database, just to name a few. All options are open, though this approach requires a bit of experience to smoothly add the desired persistence mechanism to the project. The same goes for other components, such as templating.

This summarizes what Pyramid is all about. Modules bundled with it relate to the web layer only. Users are encouraged to freely choose 3rd party packages that will support other aspects of their project.

However, this model causes a noticeable overhead at the beginning of any new project, because you have to spend some time choosing and integrating tools that the team is comfortable with. Still, once you put the effort into making extra decisions in the beginning, you are rewarded with a setup that makes it easy to start a new project and comfortable to develop it further.

Pyramid’s motto is "The Start Small, Finish Big Stay Finished Framework". This makes it an appropriate tool for experienced developers who are not afraid of putting a lot of extra work in the beginning, without shipping any feature within the first few days. Less experienced programmers may feel a bit intimidated.

Sample "Hello world" app in Pyramid:

        from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response

def hello_world(request):
   return Response('Hello, world!')

if __name__ == '__main__':
   with Configurator() as config:
       config.add_route('hello', '/')
       config.add_view(hello_world, route_name='hello')
       app = config.make_wsgi_app()
   server = make_server('', 6543, app)


Sanic differs considerably from the aforementioned frameworks because unlike them, it is based on asyncio - Python’s toolbox for asynchronous programming, bundled with the standard library starting from version 3.4.

In order to develop projects based on Sanic, one has to grasp the ideas behind asyncio first. This involves a lot of theoretical knowledge about coroutines, concurrent programming caveats and carefully reasoning about the data flow in the application.

Once a developer gets the idea and applies Sanic/asyncio to an appropriate problem, the effort pays off. Sanic is especially handy when it comes to coping with long-living connections, such as websockets.

Another use case is writing a “glue-web application”, that can be a mediator between two subsystems with incompatible APIs.

On the other hand, Sanic will not be a good choice for simple CRUD applications that only perform basic database operations. It would just make them more complicated with no visible benefit.

Sanic is meant to be very fast. One of its dependencies is uvloop - an alternative, drop-in replacement for asyncio’s not-so-good built-in event loop. uvloop is a wrapper around libuv. The same engine powers node.js. According to the uvloop documentation, this makes asyncio work 2-4 times faster.

In terms of “what’s in the box”, Sanic does not offer as much as other frameworks. It is a micro-framework, just like Flask. Apart from routing and other basic web-related goodies like utilities for handling cookies and streaming responses, there is not much inside. Sanic imitates Flask, for example by sharing the concept of Blueprints - tiny sub-applications, letting developers split and organize their code in bigger applications.

Sanic is a great choice for projects requiring support for websockets or making a lot of long-lasting external API calls. Please note it requires at least Python 3.5.

Handling websockets in Sanic:

async def time(websocket, path):
   while True:
      now = datetime.datetime.utcnow().isoformat() + 'Z'
      await websocket.send(now)
      await asyncio.sleep(random.random() * 3)


The world of Python frameworks includes many more interesting examples. Each of these frameworks focuses on a different issue, was built for distinct tasks, or has a particular history.

One that comes to mind is Zope2, one of the oldest frameworks which is still used mainly as part of the Plone CMS. Zope3 (later renamed BlueBream) was created as Zope2’s successor. The framework was supposed to allow for easier creation of large applications, but has not won too much popularity, mainly because of the need to master fairly complex concepts (e.g. Zope Component Architecture) very early in the learning process.

Also noteworthy is Google App Engine, which allows you to run applications written in Python, among others. This platform lets you create applications in any framework compatible with WSGI. The SDK for App Engine includes a simple framework called webapp2 and exactly this approach is often used in web applications adapted to this environment.

Another interesting example is Tornado developed by FriendFeed and made available by Facebook. This framework includes libraries supporting asynchronicity, so you can build applications supporting multiple simultaneous connections (e.g. long pooling, WebSocket). Other similar libraries include Pulsar (async), Twisted (callbacks), and Gevent (greenlet). These libraries allow you to build any network applications (e.g. multiplayer games, chat rooms), but but they also perform well at handling HTTP requests.

Developing applications using these frameworks and libraries is more difficult and requires you to explore some more difficult concepts. I’d recommend using them later on in your venture into the Python world.


I hope this short summary of Python frameworks will help you decide which framework you should delve into first.

Now then, it’s time for your move. Read a tutorial, write some practice code and then get started with your first project in Python.

And if you have any more Python-related questions, don't hesitate to leave us a comment or contact us - we'd be happy to answer.

nearshoring ebook

Wojciech Lichota

Head of Service Delivery


Sebastian Buczyński

Senior Python Developer STX Next

More articles about Python