Thursday, April 30, 2009

Producing Stable Javascript with jQuery

If one were to view the page source of ten random different web pages, a large percentage, if not all, of those pages would contain some form of javascript. Applications that use the web browser has the delivery medium are using javascript more and more. Of course, the developers creating these pages aren't using javascript just for the sake of it. Javascript is often introduced to a web page that is lacking in a rich, interactive, user experience. In order for any javascript code to effectively change the way the user experiences the web page, the DOM elements that make up the page need to be manipulated. This means that the javascript needs to be able to locate a specific DOM element or search for a set of DOM elements that meet some specified criteria. One we have obtained a DOM element, or a set of DOM elements in our code, we need to perform actions on these elements in order to manipulate them. In the case of a set of DOM elements, this set needs to be iterated while invoking behavior on each element. Using tradition javascript DOM manipulation can lead to nothing short of a nightmare. On top of the messy code, developers have the added burden of making it work on all browsers. Thankfully, the jQuery toolkit helps alleviate some of this madness by providing some consistency.

At the core of jQuery is the jQuery() query function. This function allows developers to execute DOM element quires at any level of complexity. The function can be used to fetch a single DOM element by id, or using other query constraints such as class or attribute to retrieve a set of DOM elements. The result set returned by the jQuery() function is actually another javascript object. Methods may be invoked on this object. The interesting aspect of this is that the invoked behavior is called for each element in the result set, not just the result set object. If the result set contains a single DOM element, the invoked behavior is called for that single element. If the result set contains several DOM elements, the invoked behavior is applied to every element. If the result set contains no elements, the behavior isn't invoked. This is extremely useful because behavior will never be invoked on a non-existent DOM element. No error handling or looping constructs need to be implemented by the developer. jQuery also projects a shorthand $() function for the main jQuery() function. This, however can be hard on the eyes after a while.

If your javascript also employs logic other than manipulating the DOM, jQuery is also powerful in this area. The jQuery() function also accepts javascript arrays. Developers can then invoke the each() function on the result set. This provides a consistent way to iterate through javascript arrays. This is mostly important because trying to get the same javascript array iteration code to work on multiple browsers isn't easy. Why write the cross-browser code when it is already done?

Tuesday, April 28, 2009

Exploring Application Menu Data Structures

All modern GUI applications have menus in one form or another. These menus offer the end user an easy way to navigate throughout the application while consuming minimal real estate. This is achieved because menus in applications are often hierarchical, which means that only the top level of the hierarchy needs to be displayed when the menu is not being navigated. Not only do menus help make efficient use of the application GUI space, but they also help with the logical organization of the actions the user may perform within the application. As the user navigates to lower levels in the hierarchy, the more specific the available actions become. Horizontal, desktop-like menus are becoming more frequent in web browser GUI applications. This further complicates the task of designing a portable menu data structure.

The hierarchical menu data structure is by far the most commonly encountered menu structure. The data structure for GUI menus changes drastically when the structure is built dynamically verses a static menu structure. For statically built menus, which are represented by HTML, there is no concern about changing menu items. Although this approach is a poor design, the developer simply edits the HTML menu structure to add in the new menu items as needed. With static menus such as these, the structure is entirely dictated by the surrounding page and CSS styles. Another reason for avoiding static menu data structures. Consider the requirement of dynamically building a menu data structure that is functional for both hierarchical and non-hierarchical menu styles. If the data structure used for this is dynamically constructed, it can be reused. If we want to use this dynamic menu-building code to construct a single-level GUI menu, we simply use the code to build hierarchy data structure with multiple roots an no children. However, this calls for flexible presentation code which transforms the data structure into the GUI. This is another challenging problem in its' own right.

Here is an example data structure that could potentially be used to build a GUI menu.

{\
"File":[{"Exit":{"action":exit()}}],\
"Edit":[{"Heading":[{"H1":{action:h1()}},\
{"H2":{"action":h2()}}]\
}]\
}
Here, we have two root elements in the hierarchy. Here is another example of representing the same menu data structure.
[
{"title":"File",\
"children":[{"title":"Exit",\
"action":exit()}]},\
{"title":"Edit",\
"children":[{"title":"Heading",\
"children":[{"title":"H1",\
"action":h1()},\
{"title":"H2",\
"action":h2()}]\
}]\
}\
]
Here, children are explicitly specified for each node in the hierarchy. Both approaches are valid ones, one preferred by one developer over the other. However, the boss of what this data structure should look like, ultimately, is the code that uses it to build the GUI menu.

The chief idea behind using data structures such as these, and building them dynamically, is to achieve better portability and reuse with GUI code. However, much GUI code, especially in web applications, change the menu data structure simply to change the style of the GUI. This is invalid since the menu items do not change. It would be much nicer, for developers hailing from all walks of life, if a consistent GUI menu data structure could be used and reused. This is especially important for cross-browser compatibility in web applications. The good news is that many modern javascript toolkits support the notion of widgets. The widgets within these widget sets comply with one another to offer a consistent style framework. This gives developers an opportunity to use a consistent menu data structure. Widgets aren't always needed for this purpose. If developers write their own code to build the intermediary HTML representation of the menu data structure, that works too. Just be consistent about it.

Monday, April 27, 2009

Python BaseHTTPRequestHandler Request Parsing

When a client sends an HTTP request to a server, that request is sent as a raw string of HTTP request data. Each component of the HTTP request, while in the transit state, is still considered a single monolithic component. This raw HTTP request must be parsed. It is the parsing action that transforms this raw HTTP request string into a coherent set of HTTP request components. Python provides a module in the standard distribution that provides basic HTTP request parsing capabilities. The class used for this is called BaseHTTPRequestHandler. As the name suggests, it is the most basic of all HTTP request handling capabilities provided by Python and must be sub-classed to carry-out any meaningful functionality. What we are interested in here isn't such much the extending of the BaseHTTPRequestHandler class as much as how the HTTP request parsing of the class is broken down. This fundamental action of web programming in Python is rarely seen because many web application frameworks do this for us. Even when using the fundamental BaseHTTPRequestHandler class, developers need not concern themselves with parsing individual HTTP requests. This is also taken care of by the class as a step in the request handling activity. It is useful, however, to see how the most fundamental HTTP request parsing functionality works and if there are any weaknesses with the approach.

The following is an illustration of how an HTTP request is parsed by the BaseHTTPRequestHandler class. Keep in mind, this is only a high-level view of what actually happens. There are many smaller steps kept out of the illustration for simplicity.



One question to ask here is if any of these actions can be parallelized. One candidate might be the initialization of the HTTP headers. The reason being, there is no prerequisite input to the header initialization that isn't available before the actions previous to the header initialization are executed. Suppose this action were carried out in a new thread, would there really be any significant performance gain. Unlikely. Is it an alternative design worth exploring? I would think so. See below for an illustration of the header initialization executing in parallel with the other HTTP parsing actions.

Friday, April 24, 2009

An idea for loading Pylons middleware

Any given web application framework, Python based or otherwise, must support the concept of middleware. Some frameworks have better support for middleware than others but they all support it, even if the support is rigid and implicit. The reason to associate the concept of middleware with web application frameworks is because the middleware is code that runs in between the request and the response. In most cases, this would be an HTTP request-response pair but the same concept applies to other protocols. In the Python world of web applications, there isn't much support for this idea. Most Python web application frameworks support the concept of extension or plugins. These components can achieve the same effect as middleware components but are more geared toward domain-specific problems. Middleware is supposed to be more solution generic in that it will affect the framework as a whole. The best support of this concept is in the Pylons Python web application framework.

The middleware concept plays a big role in Pylons. In fact, several of the core Pylons components act as middleware. For instance, the routing of an HTTP request, the HTTP session, Caching, are all middleware components in Pylons. These components, as well as other middleware components, may affect the state of the core Pylons WSGI application object. Some of these middleware components will surely affect the state of the HTTP response that is sent back to the client. The middleware architecture in web application frameworks is a wise design decision because it helps reduce the complexity of the code found in the core application object. This happens by providing a better distribution of responsibilities. If all this code were located in the core application object, it would be quite messy and hard to maintain. The middleware construct also offers developers more extensibility opportunities. The entire framework functionality may be altered on account of some middleware component. Below is a simple illustration of how the Pylons web application framework interacts with components.



When a new Pylons skeleton project is generated, a middleware Python configuration script is built and placed in the project directory. It is this generated Python script that is responsible for loading the middleware components to be used in the application. This is an important task t perform when the application is started because the core application object needs its' core middleware components. Without them, much crucial functionality will be missing. However, a nice experiment to try out with the Pylons framework would be to alter this generated middleware loading code. Rather than have static Python code which directly instantiates these components, they could be loaded by iterating through a set of entry points. This way, Pylons middleware components could be discovered and loaded as they are installed on the system. This, however, could be a more challenging feat in reality. The default Pylons middleware components would need to be entry points themselves. These components could always have entry points added to them. At the very least, it would be an idea worth exploring.

Thursday, April 23, 2009

TurboGears i18n catalog loading

In a perfect world, every single application would have full translations of every major spoken language. Applications having this ability is referred to as internationalization. Internationalization become increasingly more important when dealing with web applications. The whole idea behind web applications is portability amongst many disparate clients. Thus, it is safe to assume that not all of these clients are going to speak a universal language. Fortunately, there are many internationalization tools available at developers disposal. One of the more popular tools on Linux platforms is the GNU gettext utility. In fact, Python has a built-in module that is built on top of this technology. Taking this a step further, some Python developers have built abstractions on top of the gettext module, providing further internationalization capabilities for Python web applications. The gettext Python module uses message catalogs to store the translated messages used throughout any given application. These catalogs can then be compiled into a more efficient format for performance purposes. This method of using a single message catalog would suffice for monolithic applications that cannot be extended. However, all modern Python web application frameworks can be extended one way or another. These components that extend these frameworks are more than likely to need internationalization of messages. The problem of storing and using translated messages suddenly becomes much more difficult.

The TurboGears Python web application framework provides internationalization support. TurboGears provides a function that will translate the specified string input. Of course, the message must exist in a message catalog but that is outside the scope of this discussion. The tg_gettext() function is the default implementation provided by the framework. The reason it is the default implementation and not the only implementation is because a custom text translation function may be specified in the project configuration. Either way, custom or default, the text translation function becomes the global _() function that can then be used anywhere in the application. Below is illustration of how the tg_gettext() function works.



The get_catalog() function does as the name says and returns the message catalog of the specified locale. The function will search the locale directory specified in the i18n.locale_dir configuration value. Once a message catalog has been loaded by TurboGears, it is then stored in the _catalogs dictionary. The _catalogs dictionary acts as a cache for message translations. Illustrated below is the work-flow of this function.



As mentioned earlier, this method of using a single message catalog is all well and good until your application needs to be extended by third-party components. If these components have their own message catalogs, any text translation calls made to _() will use the base application catalog. The reason being, the first time a catalog is loaded, it stays loaded because of the _catalogs cache. This means that even if the extension to the web application framework were to update the i18n.locale_dir configuration value, it will make no difference if the specified locale has already been cached. Ideally, the get_catalogs() function could check the _catalogs cache based on the locale directory rather than the locale.

Tuesday, April 21, 2009

Software, Patents, and Innovation

An interesting entry over at the open source initiative asks if patents hinder or encourage innovation. This is an interesting question regardless of the field in question. The entry talks about the centuries-old case of the steam engine. Once a patent for an idea has been established, anyone wishing to employ this idea will be in debt to the patent holder. How likely is anyone wishing to use or further develop the idea to get involved? Also, how does the patent holder benefit from this situation? They don't. In fact, the encouragement often works in the opposite direction. In many cases, there is no alternative than to use an idea that has been patented. This restriction then grows to resentment toward the patent holder and that is not something any designer, especially in the software industry wants. The idea of openness and community collaboration not withstanding, software suffers the same patent-type problems.

Early on in the development of the steam engine, the idea was patented. This brought about an era of the steam engine where there was no spectacular innovation. Years later, the steam engine designers lifted the patent. Sure enough, this brought about an era of design innovation. Such much so that the innovation rate of the steam engine doubled over the previous patented era. Designers and engineers are not turned away by the thought of patent infringements.

It seems that patents in general do not benefit anyone. The main motivation patented ideas offer is to create the patent in the first place. This is an extremely flawed approach to building anything well. The concept of "do something you love" is tossed out the window. Who loves to patent things? Why not do something well and have the rest follow. In the end it all comes down to the designer's attitude. In the case of the steam engine, once the patent was lifted, attitudes changed. That, in turn, changed everything.

In software, patents are hard to define. There is simply no way around it. Especially trying to patent an algorithm of some sort. If I were to take an existing algorithm that has been patented, completely re-factor it, and end up with the same result, would that be considered patent infringement? If so, what is really being patented is a specific output paired with a specific input. That would be very unjust.

Friday, April 17, 2009

The Django paginator

Most, if not all, modern web applications need pagination in one form or another. Pagination is the act of transforming a large data set into pages of a more manageable size. Google gives us a perfect example case of pagination. Google constantly deals with enormous data sets. Users who perform searches using google would promptly switch to a different search engine if there were no pagination provided. On the other side of the coin, pagination also provides more manageable data sets for the server code to deal with. Instead of the client saying "give me this entire large data set" the client says "give me page one of this large data set, page size being ten". From the developer perspective, pagination isn't always the most enjoyable task. They are error prone if not implemented correctly and can pose challenges when different query constraints come into play. An additional challenge with pagination is the fact that different web application frameworks use slightly different approaches to their pagination implementation. Another approach to implementing pagination could be to implement the functionality directly into the ORM. This would obviously only work with database query results but this is probably the most common use for pagination. However, if an ORM like SQLAlchemy for instance, were to implement pagination functionality, it could be used by developers inside a web application framework while still being functional outside the framework. The Django Python web application framework offers a good pagination implementation. It is not restricted to database query results and is easy for developers to understand and use. There is also much room in the implementation for extended functionality if so desired. To two classes used to implement the pagination functionality in Django are Paginator and Page as illustrated below.





The main class used by the Django web application framework is the Paginator class. The Paginator class deals directly with the data set in question. The constructor accepts an object_list as a required parameter. The constructor also accepts a per_page parameter that specifies the page size. This parameter is also required. Once the Paginator class has been instantiated with a data set, it can be used to generate Page instances. This is done by invoking the Paginator.page() method, specifying the desired page number. The Paginator class also defines several managed properties that can be used to query the state of the Paginator instance. Managed properties in Python are simply methods that may be invoked as attributes. The count attribute will return the number of objects in the data set. The num_pages attribute will return the number of pages that the data set contains, based on the specified page size. Finally, the page_range attribute will return a list containing each page number.

There is room for extended functionality in the Paginator class. Mainly, there could be some operators overloaded to make Paginator instances behave more like new-style Python instances. The Paginator class could define a __len__() method. This way, developers could invoke the builtin len() function on Paginator instances to retrieve the number of pages. An __iter__() method could also be defined for the Paginator class. This would allow instances of this class to be used in iterations. Each iteration would yield a new Page instance. Finally, a __getitem__() method would allow Paginator instances to behave like Python lists. The desired page number could be specified as an index.

The Page class complements the Paginator class in that it represents a specific page within the data set managed by the Paginator instance. The has_next(), has_previous() and has_other_pages() methods of Page instances are useful in determining if the page has any neighbouring pages. The start_index() method will return the index in the original data set owned by the Paginator instance that created the Page instance. The end_index() will return the end index in the original data set.

Like the Paginator class, there is also room for extended functionality here to make Page instances behave more like new-style Python instances. The Page class could define a __len__() method that could return the page size. The Page class could also define an __iter__() method that could enable Page instances to be used in iterations. Finally, the __getitem__() method, if it were defined, could return the specified object from the original object list.

Wednesday, April 15, 2009

Trac component registration and management

Trac is a highly flexible project management system written in Python and based around a component architecture. In fact, a large portion of the base Trac system is indeed a set of components. Example components from this set would include the Trac ticketing system or the Trac wiki formatting engine. Using a component based architecture is a smart design decision in the majority software solutions for more reasons than one. Perhaps the most compelling reason to implement a component based architecture is the replaceability that components provide. Components both require and provide interfaces which means that these components can easily be swapped for a different component that provides the same interfaces as the original component. At the very core of Trac are a small set of classes that define how components in Trac work. Like any well designed software core, it is small and unlikely to change drastically in the future. The other benefit of the core being small is the fact that this core is depended upon by all Trac components in any given Trac installation. This core is not only required for interface purposes, but also for component registration and management. This way Trac always knows during its' lifetime what components are available to it. The core set of classes for dealing with components in Trac are ComponentMeta, ComponentManager, and Component.

The most important class here for Trac component developers is the Component class. This class is the external interface Trac provides to the outside world. The Component class is intended to be generalized or extended by each component within the Trac plugin. The ComponentMeta class is used to register defined components within a Trac environment by performing meta operations. That is, by transforming the original Component class as necessary. The ComponentManager class acts as a storage pool for all components in a Trac environment. Any time the Trac system needs access to any given component, it is retrieved through this class. This provides a centralized place for all components to live. Although the Component class is all the developers need concern themselves with, since the behavior of the other two classes is encapsulated, it is nonetheless useful to have a general idea of why they exist.

The Component class states that ComponentMeta class is its' meta class. Given this declaration in Python, when Component gets instantiated, the result returned from ComponentMeta.__new__() is what the instance will ultimately be. This is a useful feature of the language because it allows the behavior of the original class to be modified based on the context. The ComponentMeta.__new__() method has all the contextual data provided to it as parameters, including the original class, the name, base classes, and constructor parameters. The ComponentMeta class not only registers the various interfaces provided by the component in question, but will also redefine the Component constructor while still preserving the functionality of the original constructor. It does this by defining a nested maybe_init() function inside the ComponentMeta.__new__() method. The nested maybe_init() function will become the new Component constructor. The reason redefining the original constructor is so that a ComponentManager instance may now be passed to the Component constructor. This ComponentManager instance will then store the component. What really makes this useful is that if the original constructor existed within the Component in question, it is still invoked by the new maybe_init() constructor.

The ComponentManager is where Trac components are stored once loaded. As mentioned above, the ComponentMeta class dynamically injects functionality into the Component instantiation process that will store itself in a ComponentManager instance. Components stored in the ComponentManager instance be be retrieved by simple attribute access, using the component name as the attribute. This is implemented by the ComponentManager using the __get__() method. If a component is requested by this method that is not currently enabled, the ComponentManager will enable it before returning it. Otherwise it will simply return it. Developers also have an opportunity with Trac to subclass the ComponentManager and override the empty methods it invokes when enabling components. This could potentially be useful if enabling a component is a meaningful event.

Tuesday, April 7, 2009

Introducing AmiNation

AmiNation is a full-featured Python web application framework and is a product of orangoolabs.com. AmiNation is the driving force behind the Skeletonz content management system written in Python. Although AmiNation is not a formal Python package, it isn't at package at all let alone a formal one, it is a collection of smaller projects that form a coherent whole. These component projects that make the AmiNation web framework are actually independent of one another. So, in a way, it makes the coupling of projects that decide to employ AmiNation Much smaller. If, for instance, there was no longer a need for the AmiDb component, it could safely be removed without affecting the AmiFormat component. The AmiNation web framework provides a good solution for developers looking to implement a Python component-based web architecture. There are three components that make up the AmiNation framework. The AJS component is a lightweight javascript library. The AmiFormat component is an HTML formatter that provides a wiki-like syntax and produces HTML output for the web. Finally, AmiDb is a small database wrapper that sits on top of several other Python database libraries.

The AJS javascript library is very lightweight. Having lightweight javascript libraries is a very important requirement given the likelihood of the library being retrieved several times in the same day by the same user. The AJS library is lighter, that is, smaller in size, than most commonly used javascript libraries today such as jQuery. The AJS javascript library provides functionality for easily creating DOM elements on the fly. This dynamic DOM creation is an important feature of any modern day javascript library as is the requirement of making remote, ajax-style requests, which the AJS library also supports. The DOM creation functions provided by the library are easy to use because they are named after the element the developer wants to create. For instance, SPAN(). DOM elements often need to be created in response to some GUI event or in response to a remote request response. Often, DOM elements will need to be queried. For instance, a developer may implement a query that checks the which classes a certain set of DOM elements contain. Although this is supported by the AJS javascript library, the syntax can be awkward at times. Similar to the way jQuery syntax can grow to be over-complicated if not used with caution.

The AmiFormat component of the AmiNation web framework can be though of as a formatting engine. It provides a formatHTML() method that can be invoked on formatter instances. This method will take a string as a parameter and replace any custom syntax it finds in the string with HTML formatted for the web. It is trivial for developers to add new syntax to the formatter. It mostly involves creating a new class with some parameter validation and rendering functionality. This class is then registered with the formatter instance. The syntax used in the string passed to the formatter follows a square bracket element style. Inside these square brackets, any number of named parameters can be passed to the element. It is mostly a matter of the element supporting the parameter by checking for it and using it when rendering the template. The square bracket style of element syntax is great when using two brackets. However, this syntax can look rather clumsy when using four brackets while closing the second pair.

The AmiDb component is a simple database wrapper. It sits on top of SQLAlchemy and other Python database libraries. The main reason for the AmiDb component is to offer more flexibility than what traditional ORM technology offers when executing complex database queries. There are a limited number of functions in this component that allow developers less restrictive functionality. SQLAlchemy is actively maintained whereas AmiDb is not. In fact, the AmiNation project as a whole is not actively maintained. Hopefully, this trend is reversed because there is a lot of useful design here that could help simplify many Python web application projects that currently use much more than what is needed in terms of library functionality. If nothing else, the AmiNation will serve as an exemplary Python project with outstanding design elements hidden within.

Friday, April 3, 2009

Magic methods of the Django QuerySet

The QuerySet class defined in the Django Python web application framework is used to manage query results returned from the database. From the developer perspective, QuerySet instances provide a high-level interface for dealing with query results. Part of the reason for this is that instances of this class behave very similarly to other Python primitive types such as lists. This is accomplished by defining "magic" methods, or, operator overloading. If an operator for a specific instance is overloaded, it simply means that the default behavior for that operator has been replaced when the operand involves this instance. This means that we can do things such as use two QuerySet instances in an and expression that will invoke custom behavior rather than the default behavior. This useful because it is generally easier and more intuitive to use operators in certain contexts than invoking methods. In the case of using two instances in an and expression, it makes more sense to use the built-in and Python keyword than it does to invoke some and() method on the instance. The Django provides several "magic" methods that do just this and we will discuss some of them below.

Python instances that need to provide custom pickling behavior need to implement the __getstate__() method. The QuerySet class provides an implementation of this method. The Django QuerySet implementation removes any references to self by copying the __dict__ attribute. Here is an example of how this method might get invoked.
import pickle
pickle.dumps(query_set_obj)

The representation of Python instances is provided by the __repr__() method if it is defined. The Django QuerySet implementation will call the builtin list() function on itself. The size of the resulting list is based on the REPR_OUTPUT_SIZE variable which defaults to 20. The result of calling the builtin repr() function on this new list is then returned. Here is an example of how this method might get invoked.
print query_set_obj

The length of the QuerySet instance can be obtained by calling the builtin len() function while using the instance as the parameter. In order for this to work, a __len__() method must be defined by the instance. In the case of Django QuerySet instances, the first thing checked is the length of the result cache. The result cache is simply a Python list that exists in Python memory to try and save on database query costs. However, if the result cache is empty, it is filled by the QuerySet iterator. If the result cache is not empty, it is extended by the QuerySet iterator. This means that the result cache is updated with any results that should be in the result cache but are not at the time of the __len__() invocation. The builtin len() function is then called on the result cache and returned. Here is an example of how this method might get invoked.
print len(query_set_obj)

Python allows user-defined instance to participate in iterations. In order to do so, the class must define an __iter__() method. This method defines the behavior for how individual elements in a set are returned in an iteration. The QuerySet implementation of this method will first check if the result cache exists. If the QuerySet result cache is empty, the iterator defined for the QuerySet instance is returned. The iterator does the actual SQL querying and so in this scenario, the iteration looses out on the performance gained by having cached results. However, if a result cache does exist in the QuerySet instance, the builtin iter() function is called on the cache and this new iterator is returned. Here is an example of how the __iter__() method might be invoked.
for row in query_set_obj:
print row

Python instances can also take part in if statements and invoke custom behavior defined by the __nonzero__() method. If an instance defines this method, it will be invoked if the instance is an operand in a truth test. The Django QuerySet implementation of this method first checks for a result cache, as does most of the other "magic" methods. If the result cache does exist, it will return the result of calling the builtin bool() function on the result cache. If there is no result cache yet, the method will attempt to retrieve the first item by performing an iteration on the QuerySet instance. If the first item cannot be found, false is returned. Here is an example of how the __nonzero__() might be invoked.
if query_set_obj:
print "NOT EMPTY"

Finally, Python instance may be part or and an or Python expressions. The Django QuerySet instance defines both __and__() and __or__() methods. When these methods are invoked, they will change the underlying SQL query used by returning a new QuerySet instance. Here is an example of how both these methods may be used.
print query_set_obj1 and query_set_obj2
print query_set_obj1 or query_set_obj2