Search code examples
jsonsqliteponyorm

How should I seed this PonyORM - SQLite database using a .json file?


How exactly should I be loading data from a .json file to the database via setup.py?

I have been trying to solve this problem for >6hours now. I have looked at the PonyORM documentation and the JSON documentation. I was unable to find sufficient information to solve the issue.

The entities.py file should be correct, fyi I have attached the intended structure of the database as an image. The populate_database() information is added to the database after it is created. This works as it is expected to.

The config.py is certainly working as it should be.

First I run this code to create the database and tables:

    #entities.py
    from datetime import date
    from pony.orm import *

    # creating db
    db = Database()
    db.bind('sqlite', 'db/mydata.sqlite', create_db=True)

    # defining the brand class
    class Brand(db.Entity):
            name = PrimaryKey(str)                          #Primary Key - Brand, /only one/
            nationality = Required(str)                     #Nationality, /only one/
            cars = Set('Car')                               #Brand can have multiple cars, /one or more/

    # defining the car class
    class Car(db.Entity): 
            id = PrimaryKey(int, auto=True)                 #Primary Key - Car ID, /only one/
            model = Required(str)                           #Model, /only one/
            price = Required(float)                         #Price, /only one/
            availability = Required(int)                    #Availability, /only one/
            brand = Required(Brand)                         #A car must have only one brand, /only one/
            order = Optional('Order')                       #A car may or may not belong to an order, /one or none/

    # defining the order class
    class Order(db.Entity):
            id = PrimaryKey(int, auto=True)                 #Primary Key - Order ID, /only one/
            dop = Required(date)                            #Date of purchase, automatically set when record is added, /only one/
            cars = Set(Car)                                 #Order can have multiple cars, /one or more/
            customer = Required('Customer')                 #Customer to whom the order belongs, /only one/

    # defining the customer class
    class Customer(db.Entity):
            number = PrimaryKey(str, 11)                    #Primary Key - Phone Number (because it is a unique attribute), /only one/
            name = Required(str)                            #First name, /only one/
            surname = Required(str)                         #Surname, /only one/
            orders = Set(Order)                             #Order, One customer could have multiple orders, /one or more/

    db.generate_mapping(create_tables=True)

    @db_session
    def populate_database():

            Fiat = Brand(name='Fiat', nationality='Italian')
            car1 = Car(model='Punto', price='123.00', availability='5', brand=ferrari)
            jay= Customer(number='01211100001', name='Jay',surname='Son')
            order1 = Order(dop='01/01/2017', cars=car1, customer=jay)
            order1.cars.add(car1)

    populate_database() 

After I run this code to load the data from the json file to the database:

#setup.py
import json
import config
from entities import *
'''
    Setup module
    ===========

    The setup module should have all the methods that has to be executed before
    the application is started (i.e. seeding the database).
'''


'''
    Seed the database with information from the json dump file.
    NOTE:   this method is specific to each application, which means you have
            to write your import method for your application.
'''
@db_session
def seed_database(dump_filename):
    # reading the json file
    data = json.load(open(dump_filename, 'r'))

    # going through the list of customers
    for record in data['Customer']:
        customer = Customer(name = record['name'],
                          surname = record['surname'],
                            number = record['number'])

    # going through the list of brands
    for record in data['Brand']:
        brand = Brand(name = record['name'],
                      nationality = record['nationality'])


    # going through the list of cars
    for record in data['Car']:
        car = Car(model = record['model'],
                  price = record['price'],
                  availability = record['availability'],
                  brand = record['brand'])

    # going throught the list of customers
    for record in data['Order']:
        order = Order(dop = record['dop'],
                      cars = record['car'],
                      customer = record['customer'])

if __name__ == "__main__":
    seed_database(config.DB_DUMP_FILE_NAME)

This is the json file contents:

{
"Customer": [{
    "number": "01277000000",
    "name": "John",
    "surname": "Green"
}],
"Brand": [{
    "name": "Ferrari",
    "nationality": "Italian" 
}],
"Car": [{
    "model": "Enzo",
    "price": 123.00,
    "availability": 5,
    "brand": "Ferrari"
}, {
    "model": "458",
    "price": 432.10,
    "availability": 4,
    "brand": "Ferrari"
}],
"Order": [{
    "dop": "01/01/2017",
    "car": ,
    "customer": "01277000000"
}, {
    "dop": "01/02/2017",
    "car": ,
    "customer": "01277000000"
}]
}

The config.py simply contains:

# DB_FILE_NAME : path to the SQLite database file.
DB_FILE_NAME = 'db/mydata.sqlite'

# DB_DUMP_FILE_NAME : path to the json file that will be used to seed the database.
DB_DUMP_FILE_NAME = 'data/data.json'

Any advice will be greatly appreciated, thanks in advance.

Database Structure


Solution

  • Your loading script is almost correct except for a few details:

    1) You should not unconditionally call populate_database() in entities.py, because it will try to insert the same data on each import and throw an error because of a duplicate data. Wrap it with if __name__ == '__main__'.

    2) When you load an initial data from JSON file, you need to hardcode primary keys of loaded objects, and so you need to add id value to each Car description:

    "Car": [{
        "id": 1,
        ...
    }, {
        "id": 2,
        ...
    }],
    

    3) The JSON file is not a valid JSON, because you didin't specify a car attribute value for Order. Actually, it should be cars, not car, because in the Order entity definition cars attribute has the Set type. In JSON file you can specify a list of cars ids:

    "Order": [{
        "dop": "01/01/2017",
        "cars": [1],
        "customer": "01277000000"
    }, {
        "dop": "01/02/2017",
        "cars": [2],
        "customer": "01277000000"
    }]
    

    In the seed_database function you need to convert each id to corresponding car from a database:

    # going throught the list of customers
    for record in data['Order']:
        order = Order(dop = record['dop'],
                      cars = [Car[id] for id in record['cars']],  # <<< see here
                      customer = record['customer'])
    

    After that your loading script should work as expected