I have a ndb model and I use Python + google appengine. I want to add primary key to my first field "name". Forexample I added a name "Ada", after that if I want to add again "Ada", it mustnt add, it gives me an error. I can do it easily with django but I couldnt figure out with google appengine. Can you show me a solution way please. Here is my codes:
class User(ndb.Model):
username = ndb.StringProperty(indexed=False)
created_date = ndb.DateTimeProperty(auto_now_add=True)
updated_date = ndb.DateTimeProperty(auto_now_add=True)
You should use the get_or_insert() which will add a named key to your entity.
So if your username is name = 'Ada' you could do something like this:
user_db = User.get_or_insert(name.lower(), username=name)
The first parameter is the key_name and I used the lower() function just to make sure that they will be consistent and somebody with a name 'ADA' wouldn't be able to create a new entity, unless you want that.
Related
We are trying to work with legacy DB Tables that were generated outside of Django and are not structured in an ideal way. We also can not modify the existing tables.
The DB uses the same user ID (pk) across all the tables, wether or not there is a record for that user ID. It also uses that ID as a PK on the other tables, rather than rely on them to auto increment their own IDs.
So imagine something like this below:
class Items(models.Model):
user_id = models.ForeignKey('User', db_column='UserID')
class User(models.Model):
user_id = models.IntegerField(primary_key=True)
class UserTypeA(models.Model):
user_id = models.IntegerField(primary_key=True) # Same Value as User
class UserTypeB(models.Model):
user_id = models.IntegerField(primary_key=True) # Same Value as User
What we thought of creating a relationship between Items and UserTypeA (as well as UserTypeB) is to create another field entry that uses the same column as the user_id.
class Items(models.Model):
user_id = models.ForeignKey('User', db_column='UserID')
user_type_a = models.ForeignKey('UserTypeA', db_column='UserID')
user_type_b = models.ForeignKey('UserTypeB', db_column='UserID')
This unfortunately returns a "db_column is already used" type error.
Any thoughts on how to better approach the way what we're trying to do?
A detail to note is that we're only ever reading from this databases (no updates to), so a read-only solution is fine.
Thanks,
-RB
I've solved a similar problem with this (this code should be put before the definition of your Model):
from django.db.models.signals import class_prepared
def remove_field(sender, **kwargs):
if sender.__name__ == "MyModel":
sender._meta.local_fields.remove(sender.myFKField.field)
class_prepared.connect(remove_field)
(Tested in Django 1.5.11)
Django uses local_fields to make the CREATE TABLE query.
So, I've just attached the signal class_prepared and check if sender equals the class I was expecting. If so, I've removed the field from that list.
After doing that, the CREATE TABLE query didn't include the field with same db_column and the error did not ocurr.
However the Model still working properly (with manager methods properly populating the removed field from local_fields), I can't tell the real impact of that.
When I checked group_cover table which is created by Django, there were group_id_id field and group_cover field.
I'd like to change group_id_id to group_id.
models.py
class Group(models.Model):
group_id = models.AutoField(primary_key=True)
group_name = models.CharField(max_length=50, unique=False, blank=False)
class Group_Cover(models.Model):
group_id = models.OneToOneField(Group, primary_key=True) # this create group_id_id
group_cover = models.ImageField(upload_to="/image/group/")
class Group_Member(models.Model):
user_id = models.ForeignKey(User2) # this create user_id_id
group_id = models.ForeignKey(Group) # this create group_id_id
Yeah, if I write,
group = models.OneToOneField(Group, primary_key=True)
It might work, but I may not need "_id" suffix on some field.
I read this document, but owing to my poor English, I couldn't understand the way.
Would you please teach me how to change?
Django adds an _id postix to primary keys that are generated automatically. You generally don't need to worry about them unless using a legacy data base.
Solution 2 would be the one i would recommend for a new project. Solution 1 for legacy databases.
Solution 1
To modify your existing code, use the following db_column attribute as it allows you to name the field in the database.:
group = models.AutoField(primary_key=True, db_column='group_id')
Documentation
Solution 2
To get the same results in a more "Django" way let Django generate the Primary keys automatically then reference the model in the OneToOne and Foreign key fields as shown below.
class Group(models.Model):
group_name = models.CharField(max_length=50, unique=False, blank=False)
class Group_Cover(models.Model):
group = models.OneToOneField(Group)
group_cover = models.ImageField(upload_to="/image/group/")
class Group_Member(models.Model):
user = models.ForeignKey(User2)
group = models.ForeignKey(Group)
Your assumption is correct, you need to rename your fields to not include the _id (i.e group instead of group_id). This will fix your "issue" but more than anything it more accurately represents the relationship/field. You have relationships to a model, not a reference to the id.
_id is an automatic reference provided by django to make it easier to just retrieve the _id from a model.
From the documentation
Behind the scenes, Django appends "_id" to the field name to create its database column name. In the above example, the database table for the Car model will have a manufacturer_id column. (You can change this explicitly by specifying db_column) However, your code should never have to deal with the database column name, unless you write custom SQL. You’ll always deal with the field names of your model object.
You should not worry about _id that is being added in database table. You should not deal with database if you are using ORM in Django. Also, you do not need to specify id unless its special type - group of attributes.
I would do it like this (I believe you do not need that many classes):
class Group(models.Model):
name = models.CharField(max_length=50, unique=False, blank=False)
cover = models.ImageField(upload_to="/image/group/")
users = models.ManyToManyField(User2)
Then you should access attributes with object notation. If you want id, use group.id, if you want to filter object, use Group.objects.filter(id__gt=10) or Group.objects.get(id=1) etc. My model should be doing exactly what you want to achieve.
I'm trying to write appengine python code that uses the built-in authentication 'users' object and using userid as an ndb key
Here's my model:
class UserAccounts(ndb.Model):
UserID = ndb.KeyProperty(required=True)
In my handler:
I get the current user
user = users.get_current_user()
Instantiate an entry
account = Models.UserAccounts()
Set the userid to the ndb entry
account.UserID = userid
When I run it, I get this:
Expected Key, got '113804764227149124198'
Where am I going wrong? As much as possible, I'd like to use a KeyProperty instead of StringProperty for performance reasons.
by:
account.UserID = userid
I assume you meant:
account.UserID = user.user_id()
The user id is a string, not a key, so you can't use a KeyProperty here. In fact, AFAIK, User objects as returned from users.get_current_user() don't have a key (at least not one that is documented) since they aren't datastore entries. There is an ndb.UserProperty, but I don't think it's use is generally encouraged.
What performance reasons are you referring to?
I think what you want is something like this UserAccounts(id=user_id), this way the user_id is the key. With this approach you can remove the userid field from the model definition
I have saved the user_id() field and now I want to query the datastore for that user.
How would I go about accomplishing that?
#Something like
user = db.Query(MyUser).filter('user.user_id() = ', 1234).fetch(limit=1)
Thanks
You should create a separate string property to store the user ID, then query for that. The UserProperty class is full of traps and is best avoided.
if what you are searching for is the actual entity Id then you can query for it with the get_by_id method
user = MyUser.get_by_id(1234)
note that if you create it with a parent the you need to pass that to the get_by_id() function too.
user = MyUser.get_by_id(1234, parent=parent)
docs:
https://developers.google.com/appengine/docs/python/datastore/modelclass#Model_get_by_id
I defined a simple class in GAE for keeping user profiles data like this:
class User(db.Model):
email = db.EmailProperty()
role = db.StringProperty(default=roles.USER)
first_name = db.StringProperty()
last_name = db.StringProperty()
...
I use memcache to keep session information. memcache data looks like this { 'key': 'agpjYW5kaXJhdGVzcgoLEgRVc2VyGCMM'}. I get session_id value from the cookie. When I try to get user info linked to that cookie like this:
session_id = request['session_id']
data = memcache.get(session_id)
user = User.get(data['key'])
I get KindError exception:
KindError: Kind 'User' is not a subclass of kind 'User'
I know this user exists, memcache exists. User class is defined only once in my project. Why this error occurs and how can I make it work?
UPDATE: I tried to use db.get() instead of User.get() and it worked. So, what's the problem there can be?
Model.get() does check whether the supplied key is of the correct kind, as defined in the documentation. If not of the correct kind it will throw a KindError.
db.get() does not do any type checking and therefore will succeed with the supplied value if it exists in the data store, but will not necessarily return a User entity.
So you need to check whether the key in your memcache is actually of the User kind. Are you sure it's not overwritten with the key of a different model at some point?
The App Engine framework defines a class called 'User' as part of the Users API. In addition, you have your own class by the same name. When the exception occurs, you're trying to use one, but getting the other.
To avoid this, rename your model. You should also be careful how you import modules in Python. Instead of:
from google.appengine.api.users import User
or worse:
from google.appengine.api.users import *
you should use:
from google.appengine.api import users
And then refer to users.User, which is unambiguous.
The problem, it seems to me, is more subtle than that. I was getting the error with this call to Model.get() (I'm retrieving a top-level singleton object, always there):
datastore = GDSDatastore.get(gds.Key.from_path(*path))
so I investigated with this code:
datastore = gds.get(gds.Key.from_path(*path))
if not(datastore is None or isinstance(datastore, GDSDatastore)):
logger.error("KindError isinstance(GDSDatastore)=%s class=%s" % (isinstance(datastore, GDSDatastore), datastore.__class__.__name__))
raise gds.KindError('Kind %r is not a GDSDatastore instance' %
(datastore.kind()))
The vast majority of the time I get no error, but today I got this interesting log:
KindError isinstance(GDSDatastore)=False class=GDSDatastore
Now, that strikes me as rather peculiar.
(Note: GDSDatastore is defined locally: class GDSDatastore(gds.Model))