views:

432

answers:

3

Hi all,

I have a class in which I want to override the get_or_create method. Basically if my class doesn't store the answer I want it do some process to get the answer and it's not provided. The method is really a get_or_retrieve method. So here's the class:

class P4User(models.Model):
  user      = models.CharField(max_length=100, primary_key=True)
  fullname  = models.CharField(max_length=256)
  email     = models.EmailField()
  access    = models.DateField(auto_now_add=True)
  update    = models.DateField(auto_now_add=True)

  @classmethod
  def get_or_retrieve(self, username, auto_now_add=False):
    try:
        return self.get(user=username), False
    except self.model.DoesNotExist:
        import P4
        import datetime
        from django.db import connection, transaction, IntegrityError
        p4 = P4.P4().connect()
        kwargs = p4.run(("user", "-o", username))[0]
        p4.disconnect()
        params = dict( [(k.lower(),v) for k, v in kwargs.items()])        
        obj = self.model(**params)
        sid = transaction.savepoint()
        obj.save(force_insert=True)
        transaction.savepoint_commit(sid)
        return obj, True
    except IntegrityError, e:
        transaction.savepoint_rollback(sid)
        try:
            return self.get(**kwargs), False
        except self.model.DoesNotExist:
            raise e

  def __unicode__(self):
    return str(self.user)

Now I completely admit that I have used the db/models/query.py as my starting point. My problem is this line.

obj = self.model(**params)

I am able to get the params but I haven't defined self.model. I don't understand what that needs to be and it's not intuitively obvious what value that should be. Even looking back at the query.py I can't figure this out. Can someone explain this to me? I would really like to understand it and fix my code.

Thanks

+2  A: 

get_or_create is a Manager method, that is you access it via model.objects - it is the manager class that has an attribute model. So maybe the easiest thing to do would be to create a custom Manager and put your method there.

However, fixing your code as it stands is easy. self.model is just the classname - that line is simply instantiating the class with the given parameters. So you could just do

obj = P4User(**params)

although this breaks if you subclass the model.

Daniel Roseman
You are right on the money. I ended up creating a manager. I'll share that below for anyone else..
rh0dium
A: 

Use self instead of self.model.

The code you are copying from, is method for class Queryset. There, self.model is the Model whose queryset is intended to be used. Your method is classmethod of a model itself.

simplyharsh
+1  A: 

Hi all,

Daniel was right in his suggestion to use a Manager class. Here is what I ended up with.

# Managers

class P4Manager(models.Manager):
  def p4_run_command(self, command):
    """Runs a basic perforce command and return the values"""
    p4 = P4.P4()
    p4.connect()
    values = p4.run(command)
    p4.disconnect()
    return self.__unify_key_values__(values)

  def __unify_key_values__(self, args):
    """Unified method to clean up the lack of standard returns from p4 api"""
    final = []
    for item in args:
      params = dict( [(k.lower(),v) for k, v in item.items()])
      results = {}
      for k, v in params.items():
        if k in ['password', ]: continue
        if k in ["access", "update"]:
          v = datetime.datetime.strptime(v, "%Y/%m/%d %H:%M:%S") 
        results[k]=v
      final.append(results)
    return final

  def __get_or_retrieve_singleton__(self, **kwargs):
    """This little sucker will retrieve a key if the server doesn't have it.
       In short this will go out to a perforce server and attempt to get a
       key if it doesn't exist.    
    """
    assert len(kwargs.keys())==2, \
            'get_or_retrieve() must be passed at one keyword argument'
    callback = kwargs.pop('callback', None)
    try:      
      return self.get(**kwargs), False
    except self.model.DoesNotExist:
      params = self.p4_run_command((kwargs.keys()[0], "-o", kwargs.values()))
      if callback:
        params = callback(*params)
      obj = self.model(**params)
      sid = transaction.savepoint()
      obj.save(force_insert=True)
      transaction.savepoint_commit(sid)
      return obj, True
    except IntegrityError, e:
      transaction.savepoint_rollback(sid)
      try:
        return self.get(**kwargs), False
      except self.model.DoesNotExist:
        raise e

class P4UserManager(P4Manager):
  """
  A Generic User Manager which adds a retrieve functionality
  """
  def get_or_retrieve(self, user):
    kwargs = { 'callback' : self.__userProcess__ ,
               'user': user }
    return self.__get_or_retrieve_singleton__(**kwargs)

  def __userProcess__(self, *args):
    args = args[0]
    if not args.has_key('access'):
      raise self.model.DoesNotExist()
    return args

# Models

class P4User(models.Model):
  """This simply expands out 'p4 users' """
  user      = models.CharField(max_length=100, primary_key=True)
  fullname  = models.CharField(max_length=256)
  email     = models.EmailField()
  access    = models.DateField(auto_now_add=True)
  update    = models.DateField(auto_now_add=True)
  objects   = P4UserManager()

  def __unicode__(self):
    return str(self.user)

I hope other find this usefull

rh0dium