>
you're reading...
General

Comparative Analysis: Drupal versus Django


Preface

I originally wrote this comparative analysis a couple of years ago but being in the Media industry the question comes up a lot regarding what is the right CMS platform to use. In this particular case I was evaluating Drupal versus Django as a replacement for a third proprietary system. Before rejecting that comparison as Drupal and Django are indeed two different animals please review the “Grounds for Comparison” section below.

Introduction

The purpose of this comparative analysis is to provide guidance for determining an online strategy for maintaining and developing websites and web applications while maximizing efficiency and performance, and minimizing cost, maintenance, and support. I have researched and reviewed many content management systems and frameworks but quickly narrowed them down to these two applications based on scalability, enterprise support, cost, platform dependencies, and team experience.

Grounds for Comparison

In order to understand the comparison between these two applications it is important to understand their differences. Drupal is a Content Management System (CMS) whereas Django is a framework. Many references anonymously quote that “Django is a framework with CMS-like tendencies while Drupal is a CMS with framework-like tendencies”. What is the difference? A CMS targets simplification of the user-interface for the end-user in order to input content, manage layouts/views, and apply custom formatting/styling without requiring development intervention. Frameworks on the other hand target the developer – they are independent from the presentation layer and do not incorporate business logic. Frameworks simplify the development process while attempting to enforce best practices and some sort of object model. “So before you protest that I’m comparing apples and oranges, remember that in real life, the lines are blurry, and that apples and oranges can be compared, if what you intend to compare is types of fruit” (Hacker, 1999).

Portability

Drupal is a LAMP-based (Linux, Apache, MySQL, PHP) platform. Any system (including Windows using WAMP or XAMP) today can support the Apache, MySQL, PHP stack. Django runs on Python which is typically served on a *nix environment but can also run under Windows using Python for Windows. As such both systems are platform independent. Both systems are open source and have the ability to run on Windows for development environments (I’ve successfully installed and configured both on a local Windows development environment using Apache and MySQL).

Scalability

Drupal and Django both support a diverse range of clientele – from private web users to global corporations. Drupal powers New York Observer, NowPublic, Popular Science, Economist, AOL Corporate, Dahon Bicycles, San Jose State University, Harvard, MIT, Council on Writing Program Administrators, MTV UK, Sony Music, Warner Brothers Records, Fast Company, Team Sugar, Ubuntu Brainstorm, DrupalSN, Jewcy, Whitehouse.gov, and many more. Django powers some equally renowned clientele such as Lawrence.com, washingtonpost.com, EveryBlock, LJWorld.com, Tabblo, Toronto Life, The Onion and more. Without going into details both platforms are clearly scalable and can support enterprise level corporate websites and applications with ease.

Performance

Without installing both platforms on a suitable environment (my local development machine would not be sufficient for this purpose) I cannot conduct reliable performance benchmarks. What I can do is look at some of the underlying technologies and processes to form an educated opinion:

Database: Both platforms can run on MySQL which supports some big name clients such as Lufthansa, Sabre Holdings, Google, Yahoo!, Travelocity, and Wikipedia – in addition to more than 6 million other customers (Lyons, 2005). Django recommends running their platform on PostgreSQL which is considered the Open Source alternative to Oracle. While PostgreSQL is more feature-packed, MySQL is historically faster (although they’re fairly equal today) and is backed by Sun Microsystems, Hewlett Packard, and a much larger open source community.

Web Server: Both platforms can run through Apache. Django utilizes an additional Apache module called mod_wsgi which allows Apache to pass through processing of the Python language. Django also recommends passing through static media files (CSS, images, Flash, etc.) to a light-weight web server such as NginX or lightHttpd that does not require the overhead or system resources as a full-fledged version of Apache if it is unnecessary. All of these are open source, widely supported, and are industry standards with proven performance and reliability.

Performance based on the technologies that support Drupal and Django are comparatively equal but Drupal, due to its architecture, relies on numerous layers of processing to retrieve data and render a page (VanDyk, 2004) – although this has been improved in Drupal 7 by removing one of these layers. Much of this is to do with the loose architecture (and lack of a true framework) that Django enforces. As such Drupal will likely result in additional processing and therefore degraded performance in comparison to Django. This degradation will be most visible in development since local environments will not be able to leverage the same caching mechanisms available on Production web servers. Drupal does however have an internal caching mechanism as well in order to help increase page rendering performance.

Extensibility

Drupal is highly extensible through the use of modules via a hook-system but it does not contain a native RESTful interface for integrating with third-party applications. There is a REST module that can be added to Drupal but Drupal has officially denounced this plug-in due to the security vulnerabilities that come with it.

Similarly, Django is also extensible through the use of middleware – other Django applications that can be incorporated into your project in order to add functionality. “Middleware is a framework of hooks into Django’s request/response processing. It’s a light low-level “plug-in” system for globally altering Django’s input and/or output.” (Django Project).

Flexibility

Both Drupal and Django are extremely flexible. Both allow the developer to modify the data model and extend the software any way they choose. Django has an added advantage in that again, being Python based, it is very object-oriented and will enforce an MVC model. This is “good practice” and might help enforce good coding practices while preventing future issues caused by poor implementation.

Stability

Operating

Python and PHP are both open-source Production ready programming languages. Both are well-organized with a large number of contributors, on-going development, and provide stable releases for Production environments. We already heavily utilize Apache and MySQL so without going into further detail it’s safe to imply they’re also stable. Django recommends the use of PostgreSQL but does not lack support for MySQL. The Django Project itself also runs on Python but utilizes nginx web server (http://nginx.org/) which is designed to be a lightweight, high-performance web server/reverse proxy server (Wikipedia, 2011). nginx also provides stable releases.

API

Drupal operates with the mindset that in order to be competitive they must remain on the bleeding edge of the latest technologies and that in order to “preserve backward compatibility [this] often requires that you drag historical baggage along, and in interpreted languages like PHP, this comes at a significant performance cost… Each new major release of Drupal contains many, often radical improvements in functionality, scalability and usability. These advancements are made possible by not maintaining backwards compatibility with previously released code (Drupal)”.

Django on the other hand emphasizes that (as of the release of Django 1.0) it “comes with a promise of API stability and forwards-compatibility. In a nutshell, this means that code you develop against Django 1.0 will continue to work against 1.1 unchanged, and you should need to make only minor changes for any 1.X release (Django Project).”

Overall Drupal’s philosophy means that there will always be major changes to the API with each release. While there may be a definitive upgrade path, new modules and plug-ins which are complex additions to the system will likely require complete rewrites. Ultimately this will create new challenges and lead to time consuming upgrades (Drupal). At some point they may not even be viable – a major issue we encountered with our current proprietary platform.

Security

Both Drupal and Django are relatively secure. Since they are open source there is a large community that has access to the code and therefore vulnerabilities are quickly identified and corrected. Drupal has major exploits in the past that they’ve had to correct. As a result the core Drupal team has matured and has been forced to focus more on security.

In order to maintain effective security both platforms should be secured by encrypting the connection where applicable (shopping carts, backend administration panels, etc.) and the development team needs to be aware of the code they’re using. It’s simple to find a new plug-in (again, for both platforms) rather than developing a new one but if the developer does not take the time to review and understand the code behind the plug-in then they might inadvertently introduce new risks and vulnerabilities to the system.

Django does have slightly more “preventative tools” to assist in these matters. For example, Django automatically escapes special SQL parameters to prevent SQL injections, cross-site request forgery (CSRF), etc. Conversely, magic quotes can be enabled in PHP to prevent the same SQL injections. Overall, understanding the code being implemented, enabling basic security policies (base paths, secure cookies, etc.), and code reviews from peers (for an additional perspective and to prevent common mistakes even from experienced developers) are key to maintaining security (Holovaty and Kaplan-Moss, 2009).

Drupal and Django, from a platform perspective, are equal. Django has two slight advantages in that: (1) their customer base is smaller so it is less known and therefore a smaller target for potential hackers, and (2) due to the strict object-oriented framework I would speculate that the developers who work in Django tend to be better programmers in general; The fact that Django is so object-oriented inherently reduces the potential for vulnerabilities (even by mistake) that are often seen in other procedural code because data will typically travel through a single common layer (or class) whereby data can be cleaned, security checks conducted, etc.

Community Support

Drupal has a larger client base and, as a result, greater community support as well. Drupal hosts events, meet-ups, chat sessions, forums, etc. They offer free subscriptions to a number of mailing lists in order to keep their customer base informed about security announcements, upgrades, events, etc. and everything is very well documented.

Django does offer a fair level of community support but it is not as well known as Drupal and it has not been around as long. I suspect this will increase moving forward. They also consolidate and aggregate this information for quick access on their website at https://www.djangoproject.com/community/. You can subscribe to RSS feeds for: Community blog posts, Django Q&A, Django jobs, Django links, and New/updated Django packages. They also have an IRC channel, ticket system to report bugs and make feature requests, etc. and participate in Google’s “Summer of Code”.

There is a wealth of information (including full documentation for both platforms) online and for free. There are numerous books for all levels of programming, configuration, customization, and end-user “how-to’s” for both as well. For Drupal these can cover everything from common modules/plug-ins or even focus on nothing other than writing templates for Drupal. Django books focus on the framework, how to extend, building CMS’s with Django, etc. I found three books for Django at my local Barnes & Noble and would consider two of them very good (I bought “The Definitive Guide to Django: Web Development Done Right”).

Enterprise Support

As both of these platforms are open source and community supported there is no “official” support for either. They’re not adopted by a larger company (like RedHat for Linux). That said, there are many web development companies that specialize in and focus on Drupal and offer enterprise support services that would be equivalent and Drupal maintains a list of “trusted” partners and affiliates on their website at http://drupal.org/drupal-services – all of whom have contributed to the Drupal Project and can provide everything: installation, configuration, design, module development, customization, training, support and more.

Django again does not have the same resources as Drupal in regards to enterprise support but it does have them. MediaTemple (http://mediatemple.net/webhosting/gs/features/containers.php#django) offers the biggest and most supported Django hosting environment. [MediaTemple no longer supports “Django GridContainer but djangoproject.com maintains a list of Django-friendly web hosts at https://code.djangoproject.com/wiki/DjangoFriendlyWebHosts.] Companies like Revolution Systems (http://www.revsys.com/services/django/) offer monthly and yearly support plans and they advertise that their “Django support staff is led by Jacob Kaplan-Moss (http://www.revsys.com/about/bio/jkm.html), one of the lead developers of Django. A Django support contract from Revolution Systems is like having Django experts on staff, at a fraction of the cost.”

Learning Curve

In general we have a “technology-agnostic web development team (Cooper, 2011)” and advanced senior developers. The team has a proven history of being able to pick-up new systems and technologies quickly and learning a new platform that is this object oriented will primarily be a matter of syntax and perhaps some minor hurdles understanding how Python works with Apache. From an experienced user:

Drupal is sometimes criticized for having a steep learning curve. But as someone who went through the Django learning curve last year, I won’t claim that Django’s is much better. This really depends on the pre-existing skills of the developer. In my case, my move from PHP-based systems to Django was my first exposure to object-oriented programming, my first exposure to Python, and my first exposure to MVC/MTV thinking. Not to mention learning the framework itself. I think we hear this complaint more about Drupal than about Django because there are more newbie developers in the Drupal world, while Django for the most part attracts more experienced developers.

In fact, I’d go as far as to say that Drupal has an advantage here for basic sites, where little to no customization is required. However, as soon as customization is required, you’re going to need an experienced developer on hand with either system. For systems flexible enough to let you build whatever you can imagine, there are no free lunches (Hacker, 2009).

Rapid Development

Both platforms support rapid development and I believe this is something more applicable to the individual developers rather than the platform. Since Drupal does have those “framework-like tendencies” and Django is a framework they will both simplify a number of tedious yet mandatory and often repetitive development tasks.

Out of the box Drupal will provide more functionality in order to support the development of a full-blown CMS comparable to our existing proprietary platform in use today. But, Django will likely offer most of the front-end functionality and does automate the creation of a basic administrative interface that can be expanded upon or further customized later.

Ultimately both platforms have “steep learning curves” (Hacker, 2009) but given the level of experience within our team I think we’ll be very comfortable with developing in either platform in a matter of weeks.

Deployment

Deployment of Drupal is very straight-forward as it is on a standard LAMP stack which we are very familiar with. But, due to the lack of API stability (virtually non-existent) Drupal upgrades are going to be complex and non-native modules will likely be delayed in their support for newer versions (if they are supported at all since they will also require rewrites). While the initial deployment is extremely simple, upgrades are going to be troublesome and this is a significant factor when it comes to maintaining an application long-term.

Django has one additional hurdle in that it requires integration between Apache and Python. There are modules to support this that are fairly simple to install (particularly in Linux), namely mod_wsgi (most common) and mod_python. Nginx is the second most common web server for Django (next to Apache) but may offer some performance benefits. Django also recommends the use of PostgreSQL for the database system due to some additional features it supports but MySQL is still the most commonly used in the community (DjangoSites.org, 2011).

Unit Testing

Drupal proposed a “Unit test automation” project in 2006 (http://drupal.org/node/58881) but the principal tool used for Unit Testing in Drupal today is the SimpleTest module (http://drupal.org/project/simpletest). As it is a module it also requires upgrades with each new release of Drupal and therefore will not be highly beneficial to conduct unit tests on new releases of Drupal. Fortunately as of Drupal 7.x the SimpleText module has been moved to the core which should mitigate this issue long-term. The SimpleTest module was last modified on April 9, 2011 (Drupal, 2011).

Django supports both Unit tests (expressed methods on a Python class that subclasses unittest.TestCase or Django’s customized TestCase) as well as Doctests (tests that are embedded in your functions’ docstrings and are written in a way that emulates a session of the Python interactive interpreter). Both approaches are effective and which approach is utilized is a development decision based on team experience. Django’s support for unit testing is expansive and native to the application and Python itself (Django Project, 2011).

Architecture:

Django’s most attractive attribute is its heavily object-oriented, strict architecture – much of which is due to the fact that it is written in Python so much of this is inherited. This section really consists of excerpts solely from Scot Hacker’s article “Drupal or Django? A Guide for Decision Makers” (http://birdhouse.org/blog/2009/11/11/drupal-or-django/) but I concur with his analysis here 100% so there was no sense in trying to re-word it or redo the same research. As such I did not annotate each section but please note the information here is not my original work. Hacker did incorporate comments from the Django community in attempt to balance the pro-Django bias.

Object Orientation

Drupal is not generally object-oriented but Drupal 7’s field and entity APIs are a step towards an ORM. Some aspects, such as Drupal’s database layer and many other subsystems including contributed modules like Views are object-oriented to various extents. The ability to extended classes is not what you would expect from a truly object-oriented system. Drupal instead relies on hook-system for extensions (refer to “Extensibility” previously).

Python itself (upon which Django is built) is extremely object-oriented and correctly implements objects, querysets, variables, templates, etc. As such Django is equally object-oriented and thus a much better foundation for any long-term development.

Design Pattern

Django utilizes the MVC methodology (called MTV in the Django community) which is the most common “design pattern” (although it is not a true design pattern but more so a framework pattern). Zend PHP also implements the MVC methodology. “Django is MVC from the ground up”.

Since Drupal is not object-oriented the “Design Pattern” is not applicable.

Templating System

Drupal’s templates are very complex and difficult to work with and customize. Drupal does support a theme layer which even supports sub themes via inheritance since Drupal 5 but, like our current proprietary platform, Drupal allows for business logic in templates which is a major breakdown for a CMS in that it does not clearly separate data from business logic from presentation.

By contrast Django very strictly separates the presentation layer and allows no business logic in the templates. This is absolutely best practice and Django enforces it well.

Object Relational Mapping (ORM)

Django supports ORM whereby initial design consists of defining a data model for all objects. Django then utilizes this data model in order to automatically generate database schema and identify data relationships. This also enhances the security of the application as the developer can utilize querysets rather than direct SQL queries which help to avoid mistakes, improve optimization, and even prevent SQL injection attacks.

Drupal does not support ORM which some attribute to increasing its “flexibility” but this is often a disadvantage and convolutes the code base as well as the separation of data and business logic.

SEO Compliance

SEO compliance is tied much more to your specific content, template (HTML) design, META data, and URL structures more so than your CMS or development framework. A properly defined CMS will incorporate tools to facilitate your SEO research and perhaps W3C compatibility but when designing the templates or the CMS this is up to the developers to adhere to current standards and maintain them accordingly.

Drupal does offer modules that can analyze content and recommend to end users things such as adding titles to all link tags.

Both Drupal as well as Django have basic configurations for handling META data. And, Drupal can handle website taxonomy. There is a taxonomy application for Django as well (http://code.google.com/p/django-taxonomy/) but it would likely require some additional development in order to fully integrate with your web application. This is again due to the fact the Django is a framework and taxonomy is simply an element of a good CMS.

Summary

In order to best summarize the key factors reviewed in this comparative analysis I have scored each platform on a scale from one to five (with five being the best). Here is how each platform measures up:

CMS Technology Comparison

Conclusion

For comparison, please note that our current proprietary CMS platform was ranked lower solely due to the fact that it lacks community and enterprise support. In addition the above comparison does not take into account cost and since both Drupal and Django are open source and built on open source technology stacks they are attractive alternatives.

Drupal and Django are both unique in their own right but here I have defined the grounds for comparison and noted the differences in order to make that comparison as equal and fair as possible. Both platforms are portable, scalable, flexible, and supportive of rapid development after a minor learning curve.

Drupal’s most attractive attributes are its vast community and enterprise support options. However, it is severely lacking in architecture and stability. Drupal performance and extensibility (also impacting deployment) are sub-par and not what I would expect for a Production ready enterprise Content Management System. The benefit of using Drupal would be to minimize the up-front development necessary to get a new CMS up and running if immediate replacement was necessary but the long-term hassles associated with upgrades to the core and maintenance required to support plug-ins are too cumbersome for a long-term CMS. The complexities of customizing Drupal will make it difficult to maintain rapid development and I believe that in a couple of years we would again be looking to replace the entire system.

Django lacks slightly in community and enterprise support but there are indeed available options. And, given that our CMS today really does not have equivalent support I do not anticipate this would ever become a real issue. The greatest benefits that Django offers are what I would consider superior architecture and native Unit- (and Doc-) testing. Django is built on a more reliable core and enforces best practices, strict OO standards and, as a true framework, minimizes development. The disadvantage of Django is that it will indeed require development in order to completely replace our current proprietary platform but this is something that I believe we can certainly accomplish over the next one year. Django will ultimately prove the most stable, reliable, and long-lasting option to replace and consolidate our existing Content Management Systems.

Additional Resources

  1. Django Project: https://www.djangoproject.com/
    1. Deployment Statistics: http://www.djangosites.org/stats/
    2. Documentation: https://docs.djangoproject.com/en/1.3/
    3. Community: https://www.djangoproject.com/community/
    4. The Django Book: http://www.djangobook.com/
    5. Media Temple: http://mediatemple.net/webhosting/gs/features/containers.php#django
      1. DjangoFriendlyWebHosts – Django: https://code.djangoproject.com/wiki/DjangoFriendlyWebHosts
    6. Revolution Systems: http://www.revsys.com/services/django/
  2. Drupal: http://drupal.org/
    1. Documentation: http://drupal.org/documentation
    2. Community & Support: http://drupal.org/community
    3. Marketplace: http://drupal.org/drupal-services

References

  1. Hacker, Scot (2009, November 11) Drupal or Django? A Guide for Decision Makers Scot Hacker’s Foobar Blog. Retrieved May 25, 2011 from http://birdhouse.org/blog/2009/11/11/drupal-or-django/.
  1. Drupal. http://drupal.org/.
  1. Django Project. https://www.djangoproject.com/.
  1. Projeckt.net Admin (2010, November 21) Django vs. Drupal vs. WordPress. Projeckt.net. Retrieved May 25, 2011 from http://www.projeckt.net/2010/11/django-vs-drupal-vs-wordpress/.
  1. Lyons, Daniel (2005, October 24) MySQL Steps Up. Forbes.com. Retrieved May 25, 2011 from http://www.forbes.com/2005/10/24/mysql-oracle-ibm-cz_dl_1024mysql.html.
  1. VanDyk, John (2004, July 28) Drupal page rendering process, nags of a similar ilk. Retrieved June 2, 2011 from http://kitt.hodsden.org/drupal_rendering_process.
  1. Documentation: drupal_render, Drupal. Retrieved June 2, 2011 from http://api.drupal.org/api/drupal/includes–common.inc/function/drupal_render/7.
  1. Middleware, Django Project. Retrieved June 2, 2011 from https://docs.djangoproject.com/en/dev/topics/http/middleware/?from=olddocs.
  1. Multiple authors (2011, June 2) nginx, Wikipedia. Retrived June 2, 2011 from http://en.wikipedia.org/wiki/Nginx.
  1. On backward compatibility: the drop is always moving, Drupal. Retrieved on June 2, 2011 from http://drupal.org/node/65922.
  1. API stability, Django Project. Retrieved June 2, 2011 from https://docs.djangoproject.com/en/dev/misc/api-stability/?from=olddocs.
  1. Holovaty, Adrian, and Kaplan-Moss, Jacob (2007, November 20) Chapter 20: Security, The Django Book. Retrieved June 3, 2011 from http://www.djangobook.com/en/beta/chapter20/.
  1. Anonymous (2011, June 3) Deployment Statistics, DjangoSites.org. Retrieved June 3, 2011 from http://www.djangosites.org/stats/.
  1. Download & Extend: SimpleTest, Drupal. Retrieved on June 3, 2011 from http://drupal.org/project/simpletest.
  1. Testing Django applications, Django Project. Retrieved June 3, 2011 from https://docs.djangoproject.com/en/dev/topics/testing/.

About christopherjcoleman

Independent IT Consultant. Cloud Expert. United States Navy Veteran. Dedicated. Focused. Driven. I make companies better by developing applications to meet specific business needs on reliable, cost-efficient cloud infrastructure. If the right solution doesn't exist then create it. I have achieved my greatest accomplishments because someone else told me "it's not possible; there is no way to do it" - and now there is.

Discussion

7 thoughts on “Comparative Analysis: Drupal versus Django

  1. Excellent. This is a very fair comparison between these technologies and is very helpful. Thank you.

    Posted by Patrick | 2013-09-26, 14:30
  2. I saw Django 1.0 in your post but the latest stable in may 2013 was already Django 1.5..
    Also Django is more a Framework like Symfony is. They both come with an ORM, views, templating, etc.
    More appropriate would be to compare Mezzanine (http://mezzanine.jupo.org/) which is a Django based CMS.

    Posted by Patrice | 2013-10-18, 11:33
  3. “Django also recommends passing through static media files (CSS, images, Flash, etc.) to a light-weight web server such as NginX or lightHttpd that does not require the overhead or system resources as a full-fledged version of Apache if it is unnecessary.”

    They may recommend this somewhere, however, I think a much better solution with regards to handling resources like this – except in some unique installations of course – (and please someone correct me if I’m wrong) – is to have these resources handled by a CDN. (a) There’s no need (that I can think of, based on my admittedly not diverse project exposure) to have your servers getting pummeled for all of these resources when a CDN could handle it much better. And (b), unless you don’t have a very geographically spread out audience, you’re paining your users a lot of latency by not having your static resources spread out across international data centers.

    ps. In ALL due respect – and this is almost embarrassingly silly and probably a regionally acquired opinion – it is hard for me to take a technical blog post such as this seriously when written by a suit :-o Thank you, though, for the time and work put into the article!!

    Posted by John | 2015-03-24, 21:29
    • (I do see that your bio has a high focus on consulting and your service to the country, so it is quite understandable – just had to at least let you know and in all friendliness conveyable through a comment box) ;-)

      Posted by John | 2015-03-24, 21:33
    • John, thanks for your comments. I left out any information regarding web servers or architectural design as I assume any of these platforms could be implemented to leverage the same things. That said, I agree in general with distributing static media (JS, CSS, images) that way and generally architect my projects to serve them from a subdomain (e.g. static.mydomain.com) tied to an S3 bucket in Amazon if the platform can easily integrate that way (Django with S3boto does for example), or via Amazon CloudFront (still leveraging NginX as the webserver but using CloudFront for the CDN).

      P.S. I initially wrote this review as a Principal Architect and following my previous positions as Team Lead and Lead Programmer … that’s how I earned the “suit” ;)

      Posted by christopherjcoleman | 2015-03-25, 19:13
      • Heh heh well done! Yea, I’m a big fan of both of those combos. Such fun technologies available these days in such close reach

        Posted by John | 2015-03-28, 05:40

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: