adewes / blitzdb

Blitz is a document-oriented database for Python that is backend-agnostic. It comes with a flat-file database for JSON documents and provides MongoDB-like querying capabilities.
http://blitzdb.readthedocs.org
MIT License
330 stars 37 forks source link

Python 3.4.1 and blitzdb issue #32

Closed juanchristian closed 9 years ago

juanchristian commented 9 years ago

Testing code:

CODE ---------------------

!/usr/bin/env

import requests from blitzdb import Document, FileBackend

API_URL = 'http://api.themoviedb.org/3' API_KEY = 'ddf3xxxxxxxx0289'

class Actor(Document): pass

def get_actor(_id): r = requests.get('{}/person/{}?api_key={}'.format(API_URL, str(_id), API_KEY)) return r.json()

actor_1 = Actor(get_actor(1)) actor_2 = Actor(get_actor(2))

backend = FileBackend("db.blitz") actor_1.save(backend) actor_2.save(backend)

print(backend.get(Actor,{'imdb_id' : 'nm0000184'})) print('\n') print(backend.get(Actor,{'imdb_id' : 'nm0000434'}))

OUTPUT ---------------------

Warning: cjson could not be imported, CJsonSerializer will not be available. Traceback (most recent call last): File ".\uff.py", line 27, in print(backend.get(Actor,{'imdb_id' : 'nm0000184'})) File "C:\Python34\lib\site-packages\blitzdb\backends\file\backend.py", line 456, in get raise cls.DoesNotExist blitzdb.document.DoesNotExist: DoesNotExist(Actor)

QUESTION ---------------------

Why the output says that Actor doesn't exists when I already added it here 'actor_1.save(backend)' and 'actor_2.save(backend)'

Oh yes, and here is what the call to the API returns:

{"adult":false,"also_known_as":["George Walton Lucas Jr. "],"biography":"Arguably the most important film innovator in the history of the medium, George Lucas continually \"pushed the envelope\" of filmmaking technology since his early days as a student at U.S.C. Considered a wunderkind by his contemporaries, he had a much harder time communicating his vision to studio executives, whose meddling managed to compromise each of his first three feature directing efforts in some way. The monumental success of \"Star Wars\" (1977) ushered in the era of the \"summer blockbuster,\" which, despite the later popularity of low budget independent films, was still the prevailing mentality powering the Hollywood engine.\n\nThough he set the tone and established the expectations which influenced studios to devote the bulk of their resources to films designed to blast off into hyperspace for spectacular profits, it was doubtful that a film as revolutionary as \"Star Wars\" was in its day could get made in the later blockbuster assembly line climate of the new millennium.","birthday":"1944-05-14","deathday":"","homepage":"","id":1,"imdb_id":"nm0000184","name":"George Lucas","place_of_birth":"Modesto - California - USA","popularity":2.185575,"profile_path":"/rJ1zvSeZfge0mHtLnzJn4Mkw18S.jpg"}

dmytrokyrychuk commented 9 years ago

You should call backend.commit() to save changes.

adewes commented 9 years ago

Yeah this might just be the problem! When using filter or get, BlitzDB will not show you any records that have been saved but not committed to the database.

If you don't want to call commit every time, you can create the backend with the autocommit = True option, which will automatically commit after every save or update call.

Please let us know if this fixed your problem!

juanchristian commented 9 years ago

Same issue:

!/usr/bin/env

import requests from blitzdb import Document, FileBackend

API_URL = 'http://api.themoviedb.org/3' API_KEY = 'ddf3xxxxxxxx0289'

class Actor(Document): pass

def get_actor(_id): r = requests.get('{}/person/{}?api_key={}'.format(API_URL, str(_id), API_KEY)) return r.json()

actor_1 = Actor(get_actor(1)) actor_2 = Actor(get_actor(2))

backend = FileBackend("db.blitz") actor_1.save(backend) actor_2.save(backend) backend.commit()

print(backend.get(Actor,{'imdb_id' : 'nm0000184'})) print('\n') print(backend.get(Actor,{'imdb_id' : 'nm0000434'}))

OUTPUT --------------

Warning: cjson could not be imported, CJsonSerializer will not be available. Traceback (most recent call last): File ".\test.py", line 24, in print(backend.get(Actor,{'imdb_id' : 'nm0000184'})) File "C:\Python34\lib\site-packages\blitzdb\backends\file\backend.py", line 456, in get raise cls.DoesNotExist blitzdb.document.DoesNotExist: DoesNotExist(Actor)

adewes commented 9 years ago

I ran your code with BlitzDB 0.2.6 and unfortunately I still can't reproduce the issue. My output looks like this:

Actor({'pk' : 'ae214f4e5abb11e480bd2477037d8f1c'},lazy = False)

Actor({'pk' : 'ae21c4c45abb11e480bd2477037d8f1c'},lazy = False)

The JSON output I receive from the API is the following:

{u'name': u'George Lucas', u'popularity': 3.2783625, u'imdb_id': u'nm0000184', u'birthday': u'1944-05-14', u'adult': False, u'place_of_birth': u'Modesto - California - USA', u'deathday': u'', u'profile_path': u'/rJ1zvSeZfge0mHtLnzJn4Mkw18S.jpg', u'homepage': u'', u'id': 1, u'biography': u'Arguably the most important film innovator in the history of the medium, George Lucas continually "pushed the envelope" of filmmaking technology since his early days as a student at U.S.C. Considered a wunderkind by his contemporaries, he had a much harder time communicating his vision to studio executives, whose meddling managed to compromise each of his first three feature directing efforts in some way. The monumental success of "Star Wars" (1977) ushered in the era of the "summer blockbuster," which, despite the later popularity of low budget independent films, was still the prevailing mentality powering the Hollywood engine.\n\nThough he set the tone and established the expectations which influenced studios to devote the bulk of their resources to films designed to blast off into hyperspace for spectacular profits, it was doubtful that a film as revolutionary as "Star Wars" was in its day could get made in the later blockbuster assembly line climate of the new millennium.', u'also_known_as': [u'George Walton Lucas Jr. ']} {u'name': u'Mark Hamill', u'popularity': 11.137056, u'imdb_id': u'nm0000434', u'birthday': u'1951-09-25', u'adult': False, u'place_of_birth': u'Concord, California, USA', u'deathday': u'', u'profile_path': u'/klQrOckrIuUluOEp7g0osOfTgWI.jpg', u'homepage': u'', u'id': 2, u'biography': u'From Wikipedia, the free encyclopedia.\n\nMark Richard Hamill (born September 25, 1951) is an American actor, voice artist, producer, director, and writer. Hamill is best known for his role as Luke Skywalker in the original Star Wars trilogy and also well known for voice-acting characters such as the Joker in various animated series, animated films and video games, beginning with Batman: The Animated Series, the Skeleton king in Super Robot Monkey Team Hyperforce Go!, Fire Lord Ozai in Avatar: The Last Airbender, Master Eraqus in Kingdom Hearts: Birth by Sleep, Skips in Regular Show, and Senator Stampington on Metalocalypse.\n\nDescription above from the Wikipedia article Mark Hamill, licensed under CC-BY-SA, full list of contributors on Wikipedia .', u'also_known_as': []}

The only thing I could advise you to do is to delete the database directory and try again. Also, please make sure that you've installed the latest version of BlitzDB. I'm sorry that I can't provide you better advice.

adewes commented 9 years ago

I also checked with Python 3 and discovered a unicode bug (which is unrelated to your problem though). Please check out BlitzDb v0.2.7.

juanchristian commented 9 years ago

Working now on v0.2.7 using the same code.

!/usr/bin/env

import requests from blitzdb import Document, FileBackend

API_URL = 'http://api.themoviedb.org/3' API_KEY = 'xxxxxxxxxx'

class Actor(Document): pass

def get_actor(_id): r = requests.get('{}/person/{}?api_key={}'.format(API_URL, str(_id), API_KEY)) return r.json()

actor_1 = Actor(get_actor(1)) actor_2 = Actor(get_actor(2))

backend = FileBackend("db.blitz") actor_1.save(backend) actor_2.save(backend) backend.commit()

print(backend.get(Actor, {'imdb_id': 'nm0000184'})) print(backend.get(Actor, {'imdb_id': 'nm0000434'}))

Actor({'pk' : 'd64d37645ae611e49c00a417310f9aa6'},lazy = False) Actor({'pk' : 'd64d37655ae611e4bd84a417310f9aa6'},lazy = False)

juanchristian commented 9 years ago

PS: Is there a way to implement something like:

var = Actor(ID_GOES_HERE) and have the class Actor calling the API using requests.get() instead of a function get_actor for that?

I tried:

def __init__(self, id):
    super(requests.get(
        '{}/person/{}?api_key={}'.format(API_URL, str(id), API_KEY)).json())`

But, didn't work.

adewes commented 9 years ago

Great, happy that it works now!

You could possibly accomplish this with the initialize function:

http://blitzdb.readthedocs.org/en/latest/api/document.html#blitzdb.document.Document.initialize

It's also possible to define a pre_save function in the class, which will get called before a document gets saved, and where you could e.g. call the API and update the movie information if it's not yet contained in the document. Let me know if that works for you!

juanchristian commented 9 years ago

I think I didn't get what you said, let's say I have this:

!/usr/bin/env

import requests from blitzdb import Document, FileBackend

class TMDB:

def __init__(self, key):
    self._API_KEY = key
    self._API_URL = 'http://api.themoviedb.org/3/'

def get_id(self, query_type, query):
    query = str(query).replace(' ', '%20')
    response = requests.get(self._API_URL
                            + 'search/' + query_type
                            + '?api_key=' + self._API_KEY
                            + '&query=' + query).json()

    if response['total_results'] == 0:
        raise

    return response['results'][0]['id']

def get_object(self, query_type, id):
    return requests.get(self._API_URL + query_type + '/' + id + '?api_key=' + self._API_KEY).json()

API = TMDB('xxxxxxxxxxxx') backend = FileBackend("db.blitz")

class Actor(Document):

def __init__(self, id):
    actor = API.get_object('person', 287)
    actor.save(backend)
    backend.commit()

I need to implement this thing on the init of Actor so I can call it like var = Actor(123) and inside the same func this would save and commit to the db. I know that this approach isn't quit good for this kind of work, but I just want to play with blitzdb in order to learn it better!

adewes commented 9 years ago

Hey @jcchristian sorry for the super-late answer! You could do it like this, but if you overwrite the init function make sure to support the right signature and call the original init of the superclass (e.g. via super(Actor,self).__init__(...).

As I said above, initialize will get called whenever the object receives it's values from the database, so you could just overwrite it to check if your data is there, and if not go and get it from the database. Like so:

class Actor(Document):

    def initialize(self):
        if not 'is_loaded' in self:
            #fetch your data from the DB here...
            self.is_loaded = True

Does that solve your problem?

juanchristian commented 9 years ago

Many thanks, I'l close here now. :+1: