0

I have an app on appengine that stores some data fields entered by user. I want to prevent redundant entries, i.e. if all fields are same, data should not be entered in database.

(Optional) If identical data is entered, value of a corresponding column "count" should be incremented.

I tried using Django Meta option unique_together for this purpose but it doesn't seem to work. Identical data is still being stored in database. Please help. Here is my code:

class Log(db.Model):
    name = db.StringProperty()
    location = db.StringProperty()
    msg = db.StringProperty()
    class Meta:
        unique_together = (("name","location","msg"),)

2 Answers2

0

There are some misunderstanding.

Firstly, you are using datastore in your code, not django. There is no unique_together nor Meta options for datastore. Datastore is a nosql service on appengine.

If you want to make datastore entity to be unique. The easiest way is using key_name. key_name will be unique. The later entity will replace the old one while they have the same key_name.

For example:

# key_name has length limit (500), hash it can make sure it won't exceed the limit 
log = Log(
    key_name=str(hash((name,location,msg))), 
    name=name,
    location=location,
    msg=msg
)
log.put() 
# it will replace any exist log which has the same name, location, msg combination.

# and the item can be Retrieve via key_name directly.
log = Log.get(key_name)

EDIT2:

built-in hash may return different value in different machine. So it is better to use hashlib instead.

you can defined your key_name in many ways, just make sure it won't collision in accident. for example: md5: http://docs.python.org/2/library/md5.html or just append all field together. key_name=name + "|" + location + "|" + msg

for more information: https://developers.google.com/appengine/docs/python/datastore/entities#Retrieving_an_Entity

If you want to use django on app engine, the model should be defined as:

from django.db import models

class Log(models.Model):
    name = models.CharField(max_length=255)
    location = models.StringProperty(max_length=255)
    msg = models.StringProperty(max_length=255)
    class Meta:
        unique_together = (("name","location","msg"),)

EDIT3:

Here is a complete example, one for db and the other for ndb. For ndb, it is quite simple. For db, it is a little bit hard.

from google.appengine.ext import db
from google.appengine.ext import ndb
import webapp2

class Log(db.Model):
    name = db.StringProperty()
    location = db.StringProperty()
    msg = db.StringProperty()

    count = db.IntegerProperty()

    @classmethod
    def key_name(cls, name, location, msg):
        return  name+"|"+location+"|"+msg

    @classmethod
    def get(cls, name, location, msg):
        return db.get(db.Key.from_path(cls.__name__, cls.key_name(name, location, msg) ))

class nLog(ndb.Model):
    name = ndb.StringProperty()
    location = ndb.StringProperty()
    msg = ndb.StringProperty()

    count = ndb.IntegerProperty()


class Test1(webapp2.RequestHandler):
    def get(self):
        name='test_name'
        location = 'test_location'
        msg = 'test_msg'

        Qkey_name= Log.key_name(name, location, msg)

        log = Log(
            key_name=Qkey_name,
            name=name,
            location=location,
            msg=msg,
            count=0
        ).put()

        if Log.get(name, location, msg) is not None:
            Qcount = Log.get(name, location, msg).count
        else:
            Qcount = 1

class Test2(webapp2.RequestHandler):
    def get(self):
        name='test_name'
        location = 'test_location'
        msg = 'test_msg'

        Qkey_name = name + "|" + location + "|" + msg
        log = nLog(
            id=Qkey_name,
            name=name,
            location=location,
            msg=msg,
            count=0
        ).put()

        if nLog.get_by_id(Qkey_name) is not None:
            Qcount = nLog.get_by_id(Qkey_name).count
        else:
            Qcount = 1



app = webapp2.WSGIApplication([
    (r'/1', Test1),
    (r'/2', Test2)
    ], debug=True)
lucemia
  • 6,349
  • 5
  • 42
  • 75
  • Oh, thanks a lot for pointing that out :) Can you please point me in some direction about how to use key_name? – Amanpreet Singh May 12 '13 at 04:15
  • please see my edit. I have added some sample code and refs for datastore usage – lucemia May 12 '13 at 04:27
  • Thank you very much for such an enlightening answer! – Amanpreet Singh May 12 '13 at 04:37
  • It's probably not a good idea to use the built-in hash function for this. See: http://stackoverflow.com/questions/7646520/is-this-an-appropriate-use-of-pythons-built-in-hash-function – Julian Go May 12 '13 at 04:59
  • @JulianNamaro I guess you are right. Also, every time the dev server is restarted, hash values for same data are different. – Amanpreet Singh May 12 '13 at 06:10
  • @lucemia I get this error when I use your method. `raise datastore_errors.BadKeyError('Invalid string key %s.' % encoded) BadKeyError: Invalid string key` Any ideas? – Amanpreet Singh May 12 '13 at 06:13
  • @AmanpreetSingh the error said the key_name is empty string. There should be some other thing wrong. – lucemia May 12 '13 at 07:22
  • @lucemia this is about my implementation of count. I am checking if generated key already exists in datastore, so I'm comparing it. Here's my code http://tny.cz/df8c1c25 – Amanpreet Singh May 12 '13 at 17:02
  • @lucemia oops, I had used Log.get(Qkey_name) only in my code, posted wrong in paste link. What can be reason of error? – Amanpreet Singh May 13 '13 at 06:30
  • @AmanpreetSingh I have provided a complete example, hope it can help you~ – lucemia May 13 '13 at 07:25
  • 1
    @lucemia Thank you very much. Really. You answer perfectly. I wonder why official appengine docs don't have such good examples :) – Amanpreet Singh May 13 '13 at 08:02
0

I am not familiar with django but to solve your problem I would just use some sort of hash of the data and assign it as key_name of the entity. That way you are guaranteed it will be unique, and a counter should be trivial to implement using a put hook.

Julian Go
  • 4,442
  • 3
  • 23
  • 28