views:

2479

answers:

6

Hello, I would like to ask what does it mean "AttributeError: 'unicode' object has no attribute 'has_key'" Here is the full stack trace:

Traceback (most recent call last):
  File     "D:\Projects\GoogleAppEngine\google_appengine\google\appengine\ext\webapp\__init__.py", line 509, in __call__
    handler.post(*groups)
  File "D:\Projects\workspace\foo\src\homepage.py", line 71, in post
    country=postedcountry
  File "D:\Projects\GoogleAppEngine\google_appengine\google\appengine\ext\db\__init__.py", line 656, in __init__
    prop.__set__(self, value)
  File "D:\Projects\GoogleAppEngine\google_appengine\google\appengine\ext\db\__init__.py", line 2712, in __set__
    value = self.validate(value)
  File "D:\Projects\GoogleAppEngine\google_appengine\google\appengine\ext\db\__init__.py", line 2742, in validate
    if value is not None and not value.has_key():
AttributeError: 'unicode' object has no attribute 'has_key'

Let me describe a little bit more about the situation:

  • First I created the models.py that has the db.Model for CMSRequest which has an attribute country that reference to the CMSCountry class

    class CMSRequest(db.Model):
      country = db.ReferenceProperty(CMSCountry, required=True)
    
    
    class CMSCountry(db.Model):
      name = db.StringProperty(required=True)
    
  • Then, I created a bulkloader class to import the data into the CMSCountry

  • In the form, user can select the country from the drop down list box, the results are posted back and save to the CMSRequest object

    def post(self):
      postedcountry = self.request.get('country')
      cmsRequest = models.CMSRequest(postedcountry)
    

Maybe I have found the solution to my question, it is because I have not converted the posted key of CMSCountry back to save to the CMSRequest.

Thank you everyone!

+4  A: 

In this line:

if value is not None and not value.has_key():

value is a unicode string. It looks like the code is expecting it to be a db.Model,

(From what I can see, has_key is a method of db.Model, as well as a method of Python dictionaries, but this must be the db.Model one because it's being called with no arguments.)

Are you passing a string to a GAE API that expects a db.Model?

RichieHindle
hi, I have edited the question with some more code
sfa
@Hoang: It looks like you need something along the lines of `cmsRequest = models.CMSRequest(country=models.CMSCountry(postedcountry))` (I don't know whether that's exactly the right way to get a `CMSCountry` from a string, but you see what I'm saying).
RichieHindle
Thank Richie, based on your suggestion, I have modified the code as following:def post(self): postedcountry = self.request.get('country') country = models.CMSCountry.get(postedcountry) cmsRequest = models.CMSRequest(country)The key of the country should be populated in the html for the options, and postedcountry will have the key of the CMSCountry.
sfa
+1  A: 

If you read the traceback, it'll tell you exactly what is going on:

    if value is not None and not value.has_key():
AttributeError: 'unicode' object has no attribute 'has_key'

What this says is the the value variable which you're using doesn't have the has_key attribute. And, what it's telling you is that your value variable isn't a dictionary, as it looks like you're expecting...instead, it's a unicode object, which is basically a string.

brianz
+2  A: 

Note: normally "mapping" types in Python (dictionaries and dictionary like classes ... such as various types of dbm (indexed file) and some DBMS/ORM interfaces ... will implement a has_key() method.

Somehow you have gotten a Unicode (string) object into this statement when you were expecting to have some sort of dictionary or other mapping object reference.

In general AttributeError means that you have tangled up your object bindings (variable assignments). You've given a name to some object other than the type that you intended. (Or sometimes it means you have a typo like ".haskey()" instead of has_key() ... etc).

BTW: the use of has_key() is somewhat dated. Normally it's better to test your containers with the Python in operator (which implicitly calls __contains__() --- and which works on lists, strings, and other sequences as well as mapping types).

Also value.has_key() would raise an error even if value were a dictionary since the .has_key() method requires an argument.

In your code I would either explicitly test for if postedcountry is not None: ... or I'd supply your .get() with an (optional) default for "postedcountry."

How do you want to handle the situation where request had no postedcountry? Do you want to assume it's being posted from some particular default? Force a redirection to some page that requires the user to supply a value for that form element? Alert the Spanish Inquisition?

Jim Dennis
+1  A: 

You're attempting to set a string to a ReferenceProperty. The 'country' field of CMSCountry is a db.ReferenceProperty, which takes a db.Key or a CMSCountry object, but you're attempting to set it to a string.

You should be doing something like this:

def post(self):
  postedcountry = self.request.get('country')
  country = models.CMSCountry.all().filter('name =', postedcountry)
  if not country:
    # Couldn't find the country
  else:
    cmsRequest = models.CMSRequest(country=country)

Please do file a bug about the rather unhelpful error message, though.

Nick Johnson
+1  A: 

Your problem is that postedcountry is a string and not a country object. Values retrieved from self.request.get are the string values of variables passed by the browser.

You need to look up a country object using some GQL. Exactly how you do that will depend on what exactly the country field of your HTML form is returning , Object Key?, country name?

def post(self):
  postedcountry = self.request.get('country')

  # <-------- Need to look up a country here !!!!!

  cmsRequest = models.CMSRequest(country=postedcountry)
Gareth Simpson
A: 

The dictionary object's has_key() method is deprecated in 3.0 - use the "in" expression instead. If you are using the old library in 3.x, you must make code changes to accommodate it.

stackunderflow