Creating the Database Class

In order to interact with the database, we can create a class that will model a Database. For the purposes of this class we will have to imagine that a database is a real-life object that we could interact with.

When thinking about creating a class we must think of two things:

  • Does any data need to live in each of the objects?
  • Do the objects need to be able to do things?

If there is data that needs to be in the objects, then the data names should be specified in the __init__ method.

If there are things the objects should be able to do, then these actions should be defined as methods inside the class, for each object to have access to.

Data in the Database class

When the mongod process starts in your computer, it opens up a web port for applications to connect to it. By default, the port number is 27017.

We cannot connect to a web port by itself, however. We also need a web address. Because mongod is running in your computer, the web address will be 127.0.0.1.

The complete location, called URI (Universal Resource Identifier) will be 127.0.0.1:27017.

We also need to specify the type of resource that we are connecting to when we access that location. We do this by appending the resource type to the URI: mongodb://127.0.0.1:27017.

Thus the Database class would need this URI to be defined in the __init__ method, like so:

class Database:
  def __init__(self):
    self.uri = "mongodb://127.0.0.1:27017"

Instance and Class Properties

If we were to create two objects of type Database, they both would have a property uri.

db_one = Database()
db_two = Database()

print(db_one.uri)  # "mongodb://127.0.0.1:27017"
print(db_two.uri)  # "mongodb://127.0.0.1:27017"

This is something we already know! We can also change one of the objects' properties without affecting the property in the other object:

db_one = Database()
db_two = Database()

db_two.uri = "hello"

print(db_one.uri)  # "mongodb://127.0.0.1:27017"
print(db_two.uri)  # "hello"

This is very useful, if we want the URI for different objects to be different in some cases. Here, we want all our Database objects to connect to the same database, so it does not make sense to allow for the URI to differ between objects.

We can then create a class property, which is a variable that is not owned by each object, but rather by the class as a whole. Any object, including those not of type Database, can then access the property (or indeed change it).

We would change the uri to a class property like so:

class Database:
  uri = "mongodb://127.0.0.1:27017"

  def __init__(self):
    pass  # This method now doesn't do anything. We could just delete it.

Constants

Python does not have a way to define constants. Other languages, like Java or C++, allow the programmer to define constant values that cannot change.

In Python, we can define normal variables only, but if we give them fully uppercase names, this tells other programmers that the name should not change.

Let's change the uri variable to one named like a constant:

class Database:
  URI = "mongodb://127.0.0.1:27017"

Connecting to the database

Now that we have the URI defined, we need to be able to initiate a connection to the mongod process using that URI. We can do this using a pre-existing library called pymongo. Let's install this library by placing it in the requirements.txt file:

pymongo==2.7.2

Then, we can go back to our Database class and create our first method in the class:

import pymongo

class Database:
  URI = "mongodb://127.0.0.1:27017"

  def connect(self):
    # This initialises the connection to the URI
    client = pymongo.MongoClient(Database.URI)

    # This creates a variable which is the 'fullstack' database in that connection
    database = client['fullstack']

The connect method is shown with comments explaining what each line does.

However, something to note in the method is that the parameter self is not used at all.

In this instance, it might be better to make the method a @staticmethod, which makes it a method that does not affect a specific object, but instead is a method that should be executed at the class level.

import pymongo

class Database:
  URI = "mongodb://127.0.0.1:27017"

  @staticmethod
  def connect(self):
    client = pymongo.MongoClient(Database.URI)
    database = client['fullstack']

However, the method still does nothing because it is not returning anything. Since it is not returning, the variable database it creates is lost at the end of the method.

Let's make the database variable into a class property.

import pymongo

class Database:
  URI = "mongodb://127.0.0.1:27017"
  database = None

  @staticmethod
  def connect(self):
    client = pymongo.MongoClient(Database.URI)
    database = client['fullstack']

Interacting with the database

Now that we have a class property which is the database that we are connected to, we can proceed and create the rest of the methods to interact with the database. For example, to insert or find data.

Finding data

The first method that we will implement will be used to find all elements matching certain criteria.

Remember the methods all go inside the Database class.

def find(collection, query):
  return Database.database[collection].find(query)

This method will be called from other parts of our app like so:

Database.find('users', {'username': 'jose'})

And that would find, in the collection 'users', all users that have the 'username' key with value 'jose'.

Often when we are looking for one specific user we want to get just one element returned from the database, as opposed to a list of users. For example, if we were trying to log a user in, we are not interested in all the users with that username, but rather just the only user with that username.

It is a separate issue if you have more than one user with the same username. You probably shouldn't!

There are other scenarios where returning only one element is useful, and MongoDB allows us to query the database and get only one element back. We could do this like so:

def find_one(collection, query):
  return Database.database[collection].find_one(query)

Inserting data

Inserting data is also very simple using MongoDB. All we have to do is specify the collection into which data is going, and which data we want to put in the collection.

def insert(collection, data):
  return Database.database[collection].insert(data)

This would be called like so:

Database.insert('users', {
  'username': 'jose',
  'password': '1234',
  'email': '[email protected]'
})

And it would insert the document into the database. For sake of completion, remember the document inserted would be as follows:

{
  '_id': ObjectID('...'),
  'username': 'jose',
  'password': '1234',
  'email': '[email protected]'
}

Because MongoDB puts an _id field in every document it inserts.

results matching ""

    No results matching ""